Compare commits

..
185 changed files with 2512 additions and 5856 deletions
+1 -1
View File
@@ -17,7 +17,7 @@ given the ability to merge pull requests.
## Code of Conduct
This project adheres to the Contributor Covenant [code of
conduct](https://github.com/spring-cloud/spring-cloud-build/blob/main/docs/modules/ROOT/partials/code-of-conduct.adoc). By participating, you are expected to uphold this code. Please report
conduct](https://github.com/spring-cloud/spring-cloud-build/blob/main/docs/src/main/asciidoc/code-of-conduct.adoc). By participating, you are expected to uphold this code. Please report
unacceptable behavior to spring-code-of-conduct@pivotal.io.
## Code Conventions and Housekeeping
@@ -1,13 +0,0 @@
name: Build Release
runs:
using: composite
steps:
- name: Build Release
shell: bash
# If we are on Java 8 we need to build the docs zip for jubilee projects so it is distributed as part of the release
run: |
if java -version 2>&1 | grep -q '1\.8\.'; then
./mvnw --batch-mode --settings release-train-settings.xml clean deploy --activate-profiles releaseTrain,docs -DaltDeploymentRepository="release-train::default::file://$(pwd)/deployment-repository" -Dmaven-deploy-plugin.deployZipUrl="file://$(pwd)/deployment-repository" -Dmaven-deploy-plugin-default.phase=deploy -Dupload-docs-zip.packaging=zip -DskipTests
else
./mvnw --batch-mode --settings release-train-settings.xml clean deploy --activate-profiles releaseTrain -DaltDeploymentRepository="release-train::default::file://$(pwd)/deployment-repository" -DskipTests
fi
@@ -1,10 +0,0 @@
artifactory:
artifacts:
- pattern: "/**/spring-cloud-*-docs-*.zip"
properties:
zip.deployed: "false"
zip.type: "docs"
- pattern: "/**/spring-cloud-docs-*.zip"
properties:
zip.deployed: "false"
zip.type: "docs"
@@ -1,7 +0,0 @@
name: Test Release
runs:
using: composite
steps:
- name: Test Release
shell: bash
run: ./mvnw --batch-mode --settings release-train-settings.xml clean verify --activate-profiles releaseTrain
-2
View File
@@ -1,2 +0,0 @@
require:
members: false
+34 -44
View File
@@ -1,59 +1,49 @@
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"
exclude-paths:
- ".github/workflows/release-train-test.yml"
- ".github/workflows/release-train-build.yml"
- 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:
# glob 7.2.3+ conflicts with jshint (minimatch) in wro4j-maven-plugin
- dependency-name: "org.webjars.npm:glob"
# only upgrade patch versions for maintenance branch
# only upgrade by minor or patch
- dependency-name: "*"
update-types:
- version-update:semver-major
- version-update:semver-minor
- package-ecosystem: npm
target-branch: docs-build
directory: /
schedule:
interval: weekly
- package-ecosystem: npm
target-branch: main
directory: /docs
schedule:
interval: weekly
- package-ecosystem: "github-actions"
directory: "/"
target-branch: "5.0.x"
schedule:
interval: "weekly"
exclude-paths:
- ".github/workflows/release-train-test.yml"
- ".github/workflows/release-train-build.yml"
- package-ecosystem: maven
directory: /
schedule:
interval: daily
target-branch: 5.0.x
ignore:
# glob 7.2.3+ conflicts with jshint (minimatch) in wro4j-maven-plugin
- dependency-name: "org.webjars.npm:glob"
# only upgrade patch versions for maintenance branch
- dependency-name: "*"
update-types:
- version-update:semver-major
- version-update:semver-minor
- package-ecosystem: npm
target-branch: 5.0.x
directory: /docs
schedule:
interval: weekly
- version-update:semver-major
-17
View File
@@ -1,17 +0,0 @@
workflow:
generator:
project:
java:
versions:
primary: 17
workflows:
release-train:
build:
env:
COMMERCIAL_REPO_USERNAME: secrets.COMMERCIAL_ARTIFACTORY_USERNAME
COMMERCIAL_REPO_PASSWORD: secrets.COMMERCIAL_ARTIFACTORY_PASSWORD
GH_ACTIONS_REPO_TOKEN: secrets.GH_ACTIONS_REPO_TOKEN
test:
env:
COMMERCIAL_REPO_USERNAME: secrets.COMMERCIAL_ARTIFACTORY_USERNAME
COMMERCIAL_REPO_PASSWORD: secrets.COMMERCIAL_ARTIFACTORY_PASSWORD
-33
View File
@@ -1,33 +0,0 @@
name: Spring Cloud Netflix CI Job
on:
push:
branches:
- main
- 4.3.x
# Scheduled builds run daily at midnight UTC
schedule:
- cron: '0 0 * * *'
# Manual trigger with optional branch override
workflow_dispatch:
inputs:
branches:
description: "Which branch should be built (can be a comma-separated list of branches)"
required: true
default: 'main'
type: string
jobs:
deploy:
uses: spring-cloud/spring-cloud-github-actions/.github/workflows/deploy.yml@b6a6b1963b8762420526591c3d363bd7d09e7d4b # v1.1.1
with:
branches: ${{ inputs.branches }}
secrets:
ARTIFACTORY_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
ARTIFACTORY_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
COMMERCIAL_ARTIFACTORY_USERNAME: ${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}
COMMERCIAL_ARTIFACTORY_PASSWORD: ${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
+6 -22
View File
@@ -1,46 +1,30 @@
# Generated by the rollout-deploy-docs-trigger workflow in
# spring-cloud/spring-cloud-github-actions.
#
# Do not edit this file directly - update examples/deploy-docs-trigger.yml
# there and re-run the rollout, otherwise your change will be overwritten.
name: Deploy Docs
on:
push:
# Allow-list of exactly this branch. A topic or Dependabot branch cut from
# it carries this file too, and without this filter every push to one of
# those would dispatch a full docs build for a ref that is not in the
# Antora playbook.
branches:
- main
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:
contents: read # required to check out private commercial repositories
actions: write # required to dispatch the docs build
actions: write
jobs:
build:
runs-on: ubuntu-latest
# if: github.repository_owner == 'spring-cloud'
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v4
with:
ref: docs-build
fetch-depth: 1
# A branch push rebuilds just that branch's docs; a tag push rebuilds
# everything so the new version appears in the version dropdown.
- 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:
+33
View File
@@ -0,0 +1,33 @@
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
name: Build
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
java: ["17"]
steps:
- uses: actions/checkout@v4
- name: Set up JDK ${{ matrix.java }}
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: ${{ matrix.java }}
cache: 'maven'
- name: Build with Maven
run: ./mvnw clean install -B -U -P sonar
- uses: codecov/codecov-action@v3
with:
fail_ci_if_error: false
-9
View File
@@ -1,9 +0,0 @@
name: PR
on:
pull_request:
branches: [ main, 4.3.x ]
jobs:
build:
uses: spring-cloud/spring-cloud-github-actions/.github/workflows/pr.yml@b6a6b1963b8762420526591c3d363bd7d09e7d4b # v1.1.1
-91
View File
@@ -1,91 +0,0 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Build"
run-name: "${{ inputs.callback-ref }} Build"
"on":
workflow_dispatch:
inputs:
callback:
description: "Repository to which a callback should be made upon completion"
required: true
type: "string"
callback-ref:
description: "Ref in the callback repository to which a callback should be made upon completion"
required: true
type: "string"
release-train-maven-repository-url:
description: "URL of a Maven repository to be used to resolve artifacts of projects earlier in the train"
required: true
type: "string"
permissions:
contents: "read"
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
jobs:
build-release:
name: "Build Release"
runs-on: "ubuntu22-2-8"
steps:
- name: "Prevent Re-runs"
id: "prevent-re-runs"
run: |-
if [ "$GITHUB_RUN_ATTEMPT" -gt 1 ]; then
echo "Re-runs are prohibited. Use the 'Release Train  Retry' workflow to retry build failures"
exit 1
fi
- name: "Set up Java"
id: "set-up-java"
uses: "actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95" # v5.6.0
with:
distribution: "liberica"
java-version: "17"
- name: "Check Out Code"
id: "check-out-code"
uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0
- name: "Build Release"
id: "build-release"
uses: "./.github/actions/release-train-build"
env:
COMMERCIAL_REPO_PASSWORD: "${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}"
COMMERCIAL_REPO_USERNAME: "${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}"
GH_ACTIONS_REPO_TOKEN: "${{ secrets.GH_ACTIONS_REPO_TOKEN }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_PASSWORD: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_PASSWORD }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_URL: "${{ inputs.release-train-maven-repository-url }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_USERNAME: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_USERNAME }}"
- name: "Upload Deployment Repository"
id: "upload-deployment-repository"
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
with:
name: "deployment-repository"
path: "deployment-repository/**"
- name: "Upload Deployment Spec"
id: "upload-deployment-spec"
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
with:
archive: "false"
if-no-files-found: "ignore"
name: "deployment-spec"
path: ".github/actions/release-train-build/deployment-spec.yml"
- name: "Save Build System Caches"
id: "save-build-system-caches"
uses: "actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9" # v6.1.0
with:
key: "release-train-${{ inputs.callback-ref }}-${{ github.ref_name }}"
path: "~/.m2/repository"
- name: "Send Callback"
id: "send-callback"
if: "${{ !cancelled() }}"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
gh workflow run callback \
--repo ${{ inputs.callback }} \
--ref ${{ inputs.callback-ref }} \
--field commit-hash=${{ steps.check-out-code.outputs.commit }} \
--field deployment-repository-artifact-identifier=${{ steps.upload-deployment-repository.outputs.artifact-id }} \
--field deployment-spec-artifact-identifier=${{ steps.upload-deployment-spec.outputs.artifact-id }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }} \
--field result=${{ job.status == 'success' && 'built' || 'build-failed' }} \
--field workflow-run-url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
-55
View File
@@ -1,55 +0,0 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Join"
run-name: "${{ inputs.release-train }} Join"
"on":
workflow_dispatch:
inputs:
deployment-destination:
description: "Destination to which the release should be deployed"
options:
- "Maven Central"
- "Spring Enterprise"
required: true
type: "choice"
release-train:
description: "Release train"
required: true
type: "string"
release-train-repository:
default: "spring-io/release-train"
description: "Release train repository"
required: true
type: "string"
permissions:
contents: "none"
jobs:
join-release-train:
name: "Join Release Train"
runs-on: "ubuntu-latest"
steps:
- name: "Join Release Train"
id: "join-release-train"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
run_url=$(
gh workflow run join \
--repo ${{ inputs.release-train-repository }} \
--ref ${{ inputs.release-train }} \
--field commit-hash=${{ github.sha }} \
--field deployment-destination=${{ inputs.deployment-destination == 'Maven Central' && 'maven-central' || 'spring-enterprise' }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }}
)
echo "Dispatched workflow run. Waiting for $run_url to complete."
run_id=${run_url##*/}
watch_exit_code=0
gh run watch $run_id --repo ${{ inputs.release-train-repository }} --exit-status --interval=3 > /dev/null 2>&1 || watch_exit_code=$?
if [[ $watch_exit_code -eq 0 ]]; then
echo "Workflow run succeeded."
else
echo "Workflow run failed."
fi
exit $watch_exit_code
-46
View File
@@ -1,46 +0,0 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Leave"
run-name: "${{ inputs.release-train }} Leave"
"on":
workflow_dispatch:
inputs:
release-train:
description: "Release train"
required: true
type: "string"
release-train-repository:
default: "spring-io/release-train"
description: "Release train repository"
required: true
type: "string"
permissions:
contents: "none"
jobs:
leave:
name: "Leave"
runs-on: "ubuntu-latest"
steps:
- name: "Leave"
id: "leave"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
run_url=$(
gh workflow run leave \
--repo ${{ inputs.release-train-repository }} \
--ref ${{ inputs.release-train }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }}
)
echo "Dispatched workflow run. Waiting for $run_url to complete."
run_id=${run_url##*/}
watch_exit_code=0
gh run watch $run_id --repo ${{ inputs.release-train-repository }} --exit-status --interval=3 > /dev/null 2>&1 || watch_exit_code=$?
if [[ $watch_exit_code -eq 0 ]]; then
echo "Workflow run succeeded."
else
echo "Workflow run failed."
fi
exit $watch_exit_code
-47
View File
@@ -1,47 +0,0 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Ready"
run-name: "${{ inputs.release-train }} Ready"
"on":
workflow_dispatch:
inputs:
release-train:
description: "Release train"
required: true
type: "string"
release-train-repository:
default: "spring-io/release-train"
description: "Release train repository"
required: true
type: "string"
permissions:
contents: "none"
jobs:
ready:
name: "Ready"
runs-on: "ubuntu-latest"
steps:
- name: "Ready"
id: "ready"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
run_url=$(
gh workflow run ready \
--repo ${{ inputs.release-train-repository }} \
--ref ${{ inputs.release-train }} \
--field commit-hash=${{ github.sha }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }}
)
echo "Dispatched workflow run. Waiting for $run_url to complete."
run_id=${run_url##*/}
watch_exit_code=0
gh run watch $run_id --repo ${{ inputs.release-train-repository }} --exit-status --interval=3 > /dev/null 2>&1 || watch_exit_code=$?
if [[ $watch_exit_code -eq 0 ]]; then
echo "Workflow run succeeded."
else
echo "Workflow run failed."
fi
exit $watch_exit_code
-34
View File
@@ -1,34 +0,0 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Retry"
run-name: "${{ inputs.release-train }} Retry"
"on":
workflow_dispatch:
inputs:
release-train:
description: "Release train"
required: true
type: "string"
release-train-repository:
default: "spring-io/release-train"
description: "Release train repository"
required: true
type: "string"
permissions:
contents: "none"
jobs:
trigger-retry:
name: "Trigger Retry"
runs-on: "ubuntu-latest"
steps:
- name: "Trigger Retry"
id: "trigger-retry"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
gh workflow run retry \
--repo ${{ inputs.release-train-repository }} \
--ref ${{ inputs.release-train }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }}
-83
View File
@@ -1,83 +0,0 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Test"
run-name: "${{ inputs.callback-ref }} Test"
"on":
workflow_dispatch:
inputs:
callback:
description: "Repository to which a callback should be made upon completion"
required: true
type: "string"
callback-ref:
description: "Ref in the callback repository to which a callback should be made upon completion"
required: true
type: "string"
release-train-maven-repository-url:
description: "URL of a Maven repository to be used to resolve artifacts of projects earlier in the train"
required: true
type: "string"
permissions:
contents: "read"
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
jobs:
test-release:
name: "Test Release"
runs-on: "ubuntu22-2-8"
steps:
- name: "Prevent Re-runs"
id: "prevent-re-runs"
run: |-
if [ "$GITHUB_RUN_ATTEMPT" -gt 1 ]; then
echo "Re-runs are prohibited. Use the 'Release Train  Retry' workflow to retry test failures"
exit 1
fi
- name: "Set up Java"
id: "set-up-java"
uses: "actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95" # v5.6.0
with:
distribution: "liberica"
java-version: "17"
- name: "Check Out Code"
id: "check-out-code"
uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0
- name: "Restore Build System Caches"
id: "restore-build-system-caches"
uses: "actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9" # v6.1.0
with:
key: "release-train-${{ inputs.callback-ref }}-${{ github.ref_name }}"
path: "~/.m2/repository"
- name: "Test Release"
id: "test-release"
uses: "./.github/actions/release-train-test"
env:
COMMERCIAL_REPO_PASSWORD: "${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}"
COMMERCIAL_REPO_USERNAME: "${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_PASSWORD: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_PASSWORD }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_URL: "${{ inputs.release-train-maven-repository-url }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_USERNAME: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_USERNAME }}"
- name: "Send Callback"
id: "send-callback"
if: "${{ !cancelled() }}"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
gh workflow run callback \
--repo ${{ inputs.callback }} \
--ref ${{ inputs.callback-ref }} \
--field commit-hash=${{ steps.check-out-code.outputs.commit }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }} \
--field result=${{ job.status == 'success' && 'tested' || 'test-failed' }} \
--field workflow-run-url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- name: "Upload Build System Reports"
id: "upload-build-system-reports"
if: "${{ failure() }}"
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
with:
name: "build-system-reports"
path: |-
**/target/surefire-reports
**/target/failsafe-reports
+1 -1
View File
@@ -33,5 +33,5 @@ _configprops.adoc
_spans.adoc
_metrics.adoc
_conventions.adoc
/package.json
package.json
package-lock.json
BIN
View File
Binary file not shown.
+18 -4
View File
@@ -1,4 +1,18 @@
wrapperVersion=3.3.4
distributionType=bin
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.16/apache-maven-3.9.16-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar
# 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
+2 -9
View File
@@ -49,7 +49,7 @@ build succeed, please raise a ticket to get the settings added to
source control.
The projects that require middleware (i.e. Redis) for testing generally
require that a local instance of https://www.docker.com/get-started[Docker] is installed and running.
require that a local instance of [Docker](https://www.docker.com/get-started) is installed and running.
[[documentation]]
== Documentation
@@ -65,17 +65,10 @@ parsing or rendering it, just copying it to `${main.basedir}`
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.
You can generate the docs site using the following command:
[indent=0]
----
./mvnw -pl docs -P docs antora:antora
----
[[working-with-the-code]]
== Working with the code
If you don't have an IDE preference we would recommend that you use
https://spring.io/tools[Spring Tools Suite] or
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.
+8 -2
View File
@@ -1,7 +1,13 @@
antora:
extensions:
- require: '@springio/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-netflix'
- '@springio/antora-extensions/static-page-extension'
site:
title: Spring Cloud Netflix
url: https://docs.spring.io/spring-cloud-netflix/reference/
@@ -30,4 +36,4 @@ runtime:
format: pretty
ui:
bundle:
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.15/ui-bundle.zip
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.2/ui-bundle.zip
+1 -1
View File
@@ -6,7 +6,7 @@ nav:
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 -DdisableConfigurationProperties
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/
+235 -291
View File
@@ -1,257 +1,6 @@
[[features]]
= Spring Cloud Netflix Features
[[spring-cloud-eureka-server]]
== Service Discovery: Eureka Server
Eureka Server acts as a service registry that Eureka clients use to discover other services.
You can run Eureka as a standalone server or configure multiple peer-aware servers for higher availability.
The following sections show how to get started with Eureka Server and then describe standalone and peer-aware deployments.
[[netflix-eureka-server-starter]]
=== How to Include Eureka Server
To include Eureka Server in your project, use the starter with a group ID of `org.springframework.cloud` and an artifact ID of `spring-cloud-starter-netflix-eureka-server`.
See the https://projects.spring.io/spring-cloud/[Spring Cloud Project page] for details on setting up your build system with the current Spring Cloud Release Train.
NOTE: If your project already uses Thymeleaf as its template engine, the Freemarker templates of the Eureka server may not be loaded correctly. In this case it is necessary to configure the template loader manually:
.application.yml
----
spring:
freemarker:
template-loader-path: classpath:/templates/
prefer-file-system-access: false
----
[[spring-cloud-running-eureka-server]]
=== Quick Start
The following example shows a minimal Eureka server:
[source,java,indent=0]
----
@SpringBootApplication
@EnableEurekaServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
----
For a single standalone Eureka server, disable the client-side behavior so that the server does not attempt to register with or fetch the registry from a peer:
.application.yml
----
eureka:
client:
registerWithEureka: false
fetchRegistry: false
----
The server has a home page with a UI and HTTP API endpoints for the normal Eureka functionality under `/eureka/*`.
[TIP]
====
Due to Gradle's dependency resolution rules and the lack of a parent bom feature, depending on `spring-cloud-starter-netflix-eureka-server` can cause failures on application startup.
To remedy this issue, add the Spring Boot Gradle plugin and import the Spring cloud starter parent bom as follows:
.build.gradle
[source,java,indent=0]
----
buildscript {
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:{spring-boot-docs-version}")
}
}
apply plugin: "spring-boot"
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:{spring-cloud-version}"
}
}
----
====
=== `defaultOpenForTrafficCount` and its effect on EurekaServer warmup time
Netflix Eureka's `waitTimeInMsWhenSyncEmpty` setting is not taken into account in Spring Cloud Eureka server at the beginning. In order to enable the warmup time, set `eureka.server.defaultOpenForTrafficCount=0`.
[[spring-cloud-eureka-server-zones-and-regions]]
=== High Availability, Zones and Regions
The Eureka server does not have a back end store, but the service instances in the registry all have to send heartbeats to keep their registrations up to date (so this can be done in memory).
Clients also have an in-memory cache of Eureka registrations (so they do not have to go to the registry for every request to a service).
By default, every Eureka server is also a Eureka client and requires (at least one) service URL to locate a peer.
If you do not provide it, the service runs and works, but it fills your logs with a lot of noise about not being able to register with the peer.
[[spring-cloud-eureka-server-standalone-mode]]
=== Standalone Mode
The combination of the two caches (client and server) and the heartbeats make a standalone Eureka server fairly resilient to failure, as long as there is some sort of monitor or elastic runtime (such as Cloud Foundry) keeping it alive.
In standalone mode, you might prefer to switch off the client side behavior so that it does not keep trying and failing to reach its peers.
The following example shows how to switch off the client-side behavior:
.application.yml (Standalone Eureka Server)
----
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
----
Notice that the `serviceUrl` is pointing to the same host as the local instance.
[[spring-cloud-eureka-server-peer-awareness]]
=== Peer Awareness
Eureka can be made even more resilient and available by running multiple instances and asking them to register with each other.
In fact, this is the default behavior, so all you need to do to make it work is add a valid `serviceUrl` to a peer, as shown in the following example:
.application.yml (Two Peer Aware Eureka Servers)
----
---
spring:
profiles: peer1
eureka:
instance:
hostname: peer1
client:
serviceUrl:
defaultZone: https://peer2/eureka/
---
spring:
profiles: peer2
eureka:
instance:
hostname: peer2
client:
serviceUrl:
defaultZone: https://peer1/eureka/
----
In the preceding example, we have a YAML file that can be used to run the same server on two hosts (`peer1` and `peer2`) by running it in different Spring profiles.
You could use this configuration to test the peer awareness on a single host (there is not much value in doing that in production) by manipulating `/etc/hosts` to resolve the host names.
In fact, the `eureka.instance.hostname` is not needed if you are running on a machine that knows its own hostname (by default, it is looked up by using `java.net.InetAddress`).
You can add multiple peers to a system, and, as long as they are all connected to each other by at least one edge, they synchronize
the registrations amongst themselves.
If the peers are physically separated (inside a data center or between multiple data centers), then the system can, in principle, survive "`split-brain`" type failures.
You can add multiple peers to a system, and as long as they are all
directly connected to each other, they will synchronize
the registrations amongst themselves.
.application.yml (Three Peer Aware Eureka Servers)
----
eureka:
client:
serviceUrl:
defaultZone: https://peer1/eureka/,http://peer2/eureka/,http://peer3/eureka/
---
spring:
profiles: peer1
eureka:
instance:
hostname: peer1
---
spring:
profiles: peer2
eureka:
instance:
hostname: peer2
---
spring:
profiles: peer3
eureka:
instance:
hostname: peer3
----
[[spring-cloud-eureka-server-prefer-ip-address]]
=== When to Prefer IP Address
In some cases, it is preferable for Eureka to advertise the IP addresses of services rather than the hostname.
Set `eureka.instance.preferIpAddress` to `true` and, when the application registers with eureka, it uses its IP address rather than its hostname.
[TIP]
====
If the hostname cannot be determined by Java, then the IP address is sent to Eureka.
Only explicit way of setting the hostname is by setting `eureka.instance.hostname` property.
You can set your hostname at the run-time by using an environment variable -- for example, `eureka.instance.hostname=$\{HOST_NAME}`.
====
=== Securing The Eureka Server
You can secure your Eureka server simply by adding Spring Security to your
server's classpath via `spring-boot-starter-security`. By default, when Spring Security is on the classpath it will require that
a valid CSRF token be sent with every request to the app. Eureka clients will not generally possess a valid
cross site request forgery (CSRF) token you will need to disable this requirement for the `/eureka/**` endpoints.
For example:
[source,java,indent=0]
----
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated())
.httpBasic(withDefaults());
http.csrf().ignoringRequestMatchers("/eureka/**");
return http.build();
}
----
For more information on CSRF see the https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#csrf[Spring Security documentation].
A demo Eureka Server can be found in the Spring Cloud Samples https://github.com/spring-cloud-samples/eureka/tree/Eureka-With-Security-4.x[repo].
=== JDK 11 Support
The JAXB modules which the Eureka server depends upon were removed in JDK 11. If you intend to use JDK 11
when running a Eureka server you must include these dependencies in your POM or Gradle file.
[source,xml,indent=0]
----
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
----
=== AOT and Native Image Support
Spring Cloud Netflix Eureka Server does not support Spring AOT transformations or native images.
=== Metrics
`EurekaInstanceMonitor` listens to events related to Eureka instance registration and creates/updates `Gauge`s for Eureka instance information in Micrometer's `MeterRegistry`. By default, this behavior is disabled. If you want to enable it, you need to set `eureka.server.metrics.enabled` to `true`.
By default, the `Gauge`s are named `eureka.server.instances` and have the following tags:
- `application`: application name
- `status`: instance status (`UP`, `DOWN`, `STARTING`, `OUT_OF_SERVICE`, `UNKNOWN`, see: `com.netflix.appinfo.InstanceInfo.InstanceStatus`)
You can add additional tags by injecting your own implementation of `EurekaInstanceTagsProvider`.
== Service Discovery: Eureka Clients
Service Discovery is one of the key tenets of a microservice-based architecture.
@@ -286,9 +35,8 @@ public class Application {
}
public static void main(String[] args) {
SpringApplication.run(CustomerServiceTestApplication.class, args);
}
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
----
@@ -310,8 +58,6 @@ WARNING: The `defaultZone` property is case sensitive and requires camel case be
The default application name (that is, the service ID), virtual host, and non-secure port (taken from the `Environment`) are `${spring.application.name}`, `${spring.application.name}` and `${server.port}`, respectively.
NOTE: By default, the Eureka application name and virtual host are both derived from `spring.application.name`. If you configure `eureka.instance.virtualHostName` differently, use the virtual host name when looking up instances through Spring Cloud's `DiscoveryClient`.
Having `spring-cloud-starter-netflix-eureka-client` on the classpath makes the app into both a Eureka "`instance`" (that is, it registers itself) and a "`client`" (it can query the registry to locate other services).
The instance behaviour is driven by `eureka.instance.*` configuration keys, but the defaults are fine if you ensure that your application has a value for `spring.application.name` (this is the default for the Eureka service ID or VIP).
@@ -349,41 +95,21 @@ The `eureka.client.tls.enabled` needs to be true to enable Eureka client side TL
NOTE: Because of a limitation in Eureka, it is not possible to support per-server basic auth credentials, so only the first set that are found is used.
If you want to customize the `RestClient` used by the Eureka HTTP Client you may want to create a bean of `EurekaClientHttpRequestFactorySupplier` and provide your own logic for generating a `ClientHttpRequestFactory` instance.
If you want to customize the RestTemplate used by the Eureka HTTP Client you may want to create a bean of `EurekaClientHttpRequestFactorySupplier` and provide your own logic for generating a `ClientHttpRequestFactory` instance.
All default timeout-related properties for `RestClient` used by the Eureka HTTP Client are set to 3 minutes (in keeping with Apache HC5 default `RequestConfig` and `SocketConfig`). Therefore, to specify the timeout values, you must specify the value directly with the properties in `eureka.client.timeout`. All timeout properties are in milliseconds.
All default timeout-related properties for RestTemplate used by the Eureka HTTP Client are set to 3 minutes (in keeping with Apache HC5 default `RequestConfig` and `SocketConfig`). Therefore, to specify the timeout values, you must specify the value directly with the properties in `eureka.client.rest-template-timeout`. (All timeout properties are in milliseconds.)
.application.yml
[source,yaml]
----
eureka:
client:
restclient:
timeout:
connect-timeout: 5000
connect-request-timeout: 8000
socket-timeout: 10000
rest-template-timeout:
connect-timeout: 5000
connect-request-timeout: 8000
socket-timeout: 10000
----
You can also customise the `RequestConfig` for the underlying Apache HttpClient 5 by creating a bean of type `EurekaClientHttpRequestFactorySupplier.RequestConfigCustomizer`:
[source,java,indent=0]
----
@Configuration
public class RestClientConfiguration {
@Bean
EurekaClientHttpRequestFactorySupplier.RequestConfigCustomizer requestConfigCustomizer() {
return builder -> builder.setProtocolUpgradeEnabled(false);
}
}
----
=== Status Page and Health Indicator
The status page and health indicators for a Eureka instance default to `/info` and `/health` respectively, which are the default locations of useful endpoints in a Spring Boot Actuator application.
@@ -543,15 +269,9 @@ It is initialized in a `SmartLifecycle` (with `phase=0`), so the earliest you ca
==== Underlying HTTP clients
`EurekaClient` uses either `RestClient`, `WebClient` or `JerseyClient` under the hood. In order to use the `EurekaClient`, you need to have one of the supported HTTP clients on your classpath.
`EurekaClient` uses either `RestTemplate`, `WebClient` or `JerseyClient` under the hood. In order to use the `EurekaClient`, you need to have one of the supported HTTP clients on your classpath.
To use `RestClient`, add `spring-boot-restclient` to your dependencies. To use `WebClient`, add `spring-boot-webclient` to your dependencies. If both `spring-boot-restclient`
and `spring-boot-webclient` are included in the dependencies and the `eureka.client.webclient.enabled` flag is set to `true`,
then `WebClient` will be used. If that's not the case, `RestClient` will be used.
NOTE: For any of those client implementations, if there's a builder bean available, it will be used to create the underlying client.
NOTE: We're planning on changing the default client to `RestClient` with the next major release.
To use `RestTemplate`, add `spring-boot-starter-web` to your dependencies. To use `WebClient`, add `spring-boot-starter-webflux` to your dependencies. If both `RestTemplate` and `WebClient` are on the classpath when `eureka.client.webclient.enabled` is set to `true`, `WebClient` is used. Otherwise, `RestTemplate` is used.
If you wish to use Jersey instead, you need to add the Jersey dependencies to your classpath.
The following example shows the dependencies you need to add:
@@ -574,8 +294,6 @@ The following example shows the dependencies you need to add:
</dependencies>
----
If you have `JerseyClient` on the classpath but do not wish to use it in your `EuerekaClient`, make sure to set `eureka.client.jersey.enabled` to `false`.
=== Alternatives to the Native Netflix EurekaClient
You need not use the raw Netflix `EurekaClient`.
@@ -656,13 +374,239 @@ We take `eureka.client.availabilityZones`, which is a map from region name to a
Spring Cloud Netflix Eureka Client integration supports Spring AOT transformations and native images, however, only with refresh mode disabled.
WARNING: If you want to run Eureka Client in AOT or native image modes, make sure to set `spring.cloud.refresh.enabled` to `false`.
WARNING: If you want to run Eureka Client in AOT or native image modes, make sure to set `spring.cloud.refresh.enabled` to `false`
NOTE: Given the AOT and native image closed-world assumption, using random port with Eureka clients is not supported for ahead of time compilation or native images.
[[spring-cloud-eureka-server]]
== Service Discovery: Eureka Server
To see the list of all Spring Cloud Netflix related configuration properties please check link:appendix.html[the Appendix page].
This section describes how to set up a Eureka server.
[[netflix-eureka-server-starter]]
=== How to Include Eureka Server
To include Eureka Server in your project, use the starter with a group ID of `org.springframework.cloud` and an artifact ID of `spring-cloud-starter-netflix-eureka-server`.
See the https://projects.spring.io/spring-cloud/[Spring Cloud Project page] for details on setting up your build system with the current Spring Cloud Release Train.
NOTE: If your project already uses Thymeleaf as its template engine, the Freemarker templates of the Eureka server may not be loaded correctly. In this case it is necessary to configure the template loader manually:
.application.yml
----
spring:
freemarker:
template-loader-path: classpath:/templates/
prefer-file-system-access: false
----
[[spring-cloud-running-eureka-server]]
=== How to Run a Eureka Server
The following example shows a minimal Eureka server:
[source,java,indent=0]
----
@SpringBootApplication
@EnableEurekaServer
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
----
The server has a home page with a UI and HTTP API endpoints for the normal Eureka functionality under `/eureka/*`.
The following links have some Eureka background reading: https://github.com/cfregly/fluxcapacitor/wiki/NetflixOSS-FAQ#eureka-service-discovery-load-balancer[flux capacitor] and https://groups.google.com/forum/?fromgroups#!topic/eureka_netflix/g3p2r7gHnN0[google group discussion].
[TIP]
====
Due to Gradle's dependency resolution rules and the lack of a parent bom feature, depending on `spring-cloud-starter-netflix-eureka-server` can cause failures on application startup.
To remedy this issue, add the Spring Boot Gradle plugin and import the Spring cloud starter parent bom as follows:
.build.gradle
[source,java,indent=0]
----
buildscript {
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:{spring-boot-docs-version}")
}
}
apply plugin: "spring-boot"
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:{spring-cloud-version}"
}
}
----
====
=== `defaultOpenForTrafficCount` and its effect on EurekaServer warmup time
Netflix Eureka's `waitTimeInMsWhenSyncEmpty` setting is not taken into account in Spring Cloud Eureka server at the beginning. In order to enable the warmup time, set `eureka.server.defaultOpenForTrafficCount=0`.
[[spring-cloud-eureka-server-zones-and-regions]]
=== High Availability, Zones and Regions
The Eureka server does not have a back end store, but the service instances in the registry all have to send heartbeats to keep their registrations up to date (so this can be done in memory).
Clients also have an in-memory cache of Eureka registrations (so they do not have to go to the registry for every request to a service).
By default, every Eureka server is also a Eureka client and requires (at least one) service URL to locate a peer.
If you do not provide it, the service runs and works, but it fills your logs with a lot of noise about not being able to register with the peer.
[[spring-cloud-eureka-server-standalone-mode]]
=== Standalone Mode
The combination of the two caches (client and server) and the heartbeats make a standalone Eureka server fairly resilient to failure, as long as there is some sort of monitor or elastic runtime (such as Cloud Foundry) keeping it alive.
In standalone mode, you might prefer to switch off the client side behavior so that it does not keep trying and failing to reach its peers.
The following example shows how to switch off the client-side behavior:
.application.yml (Standalone Eureka Server)
----
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
----
Notice that the `serviceUrl` is pointing to the same host as the local instance.
[[spring-cloud-eureka-server-peer-awareness]]
=== Peer Awareness
Eureka can be made even more resilient and available by running multiple instances and asking them to register with each other.
In fact, this is the default behavior, so all you need to do to make it work is add a valid `serviceUrl` to a peer, as shown in the following example:
.application.yml (Two Peer Aware Eureka Servers)
----
---
spring:
profiles: peer1
eureka:
instance:
hostname: peer1
client:
serviceUrl:
defaultZone: https://peer2/eureka/
---
spring:
profiles: peer2
eureka:
instance:
hostname: peer2
client:
serviceUrl:
defaultZone: https://peer1/eureka/
----
In the preceding example, we have a YAML file that can be used to run the same server on two hosts (`peer1` and `peer2`) by running it in different Spring profiles.
You could use this configuration to test the peer awareness on a single host (there is not much value in doing that in production) by manipulating `/etc/hosts` to resolve the host names.
In fact, the `eureka.instance.hostname` is not needed if you are running on a machine that knows its own hostname (by default, it is looked up by using `java.net.InetAddress`).
You can add multiple peers to a system, and, as long as they are all connected to each other by at least one edge, they synchronize
the registrations amongst themselves.
If the peers are physically separated (inside a data center or between multiple data centers), then the system can, in principle, survive "`split-brain`" type failures.
You can add multiple peers to a system, and as long as they are all
directly connected to each other, they will synchronize
the registrations amongst themselves.
.application.yml (Three Peer Aware Eureka Servers)
----
eureka:
client:
serviceUrl:
defaultZone: https://peer1/eureka/,http://peer2/eureka/,http://peer3/eureka/
---
spring:
profiles: peer1
eureka:
instance:
hostname: peer1
---
spring:
profiles: peer2
eureka:
instance:
hostname: peer2
---
spring:
profiles: peer3
eureka:
instance:
hostname: peer3
----
[[spring-cloud-eureka-server-prefer-ip-address]]
=== When to Prefer IP Address
In some cases, it is preferable for Eureka to advertise the IP addresses of services rather than the hostname.
Set `eureka.instance.preferIpAddress` to `true` and, when the application registers with eureka, it uses its IP address rather than its hostname.
[TIP]
====
If the hostname cannot be determined by Java, then the IP address is sent to Eureka.
Only explict way of setting the hostname is by setting `eureka.instance.hostname` property.
You can set your hostname at the run-time by using an environment variable -- for example, `eureka.instance.hostname=$\{HOST_NAME}`.
====
=== Securing The Eureka Server
You can secure your Eureka server simply by adding Spring Security to your
server's classpath via `spring-boot-starter-security`. By default, when Spring Security is on the classpath it will require that
a valid CSRF token be sent with every request to the app. Eureka clients will not generally possess a valid
cross site request forgery (CSRF) token you will need to disable this requirement for the `/eureka/**` endpoints.
For example:
[source,java,indent=0]
----
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated())
.httpBasic(withDefaults());
http.csrf().ignoringRequestMatchers("/eureka/**");
return http.build();
}
----
For more information on CSRF see the https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#csrf[Spring Security documentation].
A demo Eureka Server can be found in the Spring Cloud Samples https://github.com/spring-cloud-samples/eureka/tree/Eureka-With-Security-4.x[repo].
=== JDK 11 Support
The JAXB modules which the Eureka server depends upon were removed in JDK 11. If you intend to use JDK 11
when running a Eureka server you must include these dependencies in your POM or Gradle file.
[source,xml,indent=0]
----
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
----
=== AOT and Native Image Support
Spring Cloud Netflix Eureka Server does not support Spring AOT transformations or native images.
== Configuration properties
To see the list of all Spring Cloud Netflix related configuration properties please check link:appendix.html[the Appendix page].
+28 -39
View File
@@ -21,7 +21,7 @@
|eureka.client.eureka-server-total-connections | `+++200+++` | Gets the total number of connections that is allowed from eureka client to all eureka servers.
|eureka.client.eureka-server-total-connections-per-host | `+++50+++` | Gets the total number of connections that is allowed from eureka client to a eureka server host.
|eureka.client.eureka-server-u-r-l-context | | Gets the URL context to be used to construct the service url to contact eureka server when the list of eureka servers come from the DNS. This information is not required if the contract returns the service urls from eurekaServerServiceUrls. The DNS mechanism is used when useDnsForFetchingServiceUrls is set to true and the eureka client expects the DNS to configured a certain way so that it can fetch changing eureka servers dynamically. The changes are effective at runtime.
|eureka.client.eureka-service-url-poll-interval-seconds | `+++300+++` | Indicates how often(in seconds) to poll for changes to eureka server information. Eureka servers could be added or removed and this setting controls how soon the eureka clients should know about it.
|eureka.client.eureka-service-url-poll-interval-seconds | `+++0+++` | Indicates how often(in seconds) to poll for changes to eureka server information. Eureka servers could be added or removed and this setting controls how soon the eureka clients should know about it.
|eureka.client.fetch-registry | `+++true+++` | Indicates whether this client should fetch eureka registry information from eureka server.
|eureka.client.fetch-remote-regions-registry | | Comma separated list of regions for which the eureka registry information will be fetched. It is mandatory to define the availability zones for each of these regions as returned by availabilityZones. Failing to do so, will result in failure of discovery client startup.
|eureka.client.filter-only-up-instances | `+++true+++` | Indicates whether to get the applications after filtering the applications for instances with only InstanceStatus UP states.
@@ -31,7 +31,6 @@
|eureka.client.heartbeat-executor-thread-pool-size | `+++2+++` | The thread pool size for the heartbeatExecutor to initialise with.
|eureka.client.initial-instance-info-replication-interval-seconds | `+++40+++` | Indicates how long initially (in seconds) to replicate instance info to the eureka server.
|eureka.client.instance-info-replication-interval-seconds | `+++30+++` | Indicates how often(in seconds) to replicate instance changes to be replicated to the eureka server.
|eureka.client.jersey.enabled | `+++true+++` | Enables the use of JerseyClient for Eureka HTTP Client.
|eureka.client.log-delta-diff | `+++false+++` | Indicates whether to log differences between the eureka server and the eureka client in terms of registry information. Eureka client tries to retrieve only delta changes from eureka server to minimize network traffic. After receiving the deltas, eureka client reconciles the information from the server to verify it has not missed out some information. Reconciliation failures could happen when the client has had network issues communicating to server.If the reconciliation fails, eureka client gets the full registry information. While getting the full registry information, the eureka client can log the differences between the client and the server and this setting controls that. The changes are effective at runtime at the next registry fetch cycle as specified by registryFetchIntervalSecondsr
|eureka.client.on-demand-update-status-change | `+++true+++` | If set to true, local status updates via ApplicationInfoManager will trigger on-demand (but rate limited) register/updates to remote eureka servers.
|eureka.client.order | `+++0+++` | Order of the discovery client used by `CompositeDiscoveryClient` for sorting available clients.
@@ -46,12 +45,12 @@
|eureka.client.register-with-eureka | `+++true+++` | Indicates whether or not this instance should register its information with eureka server for discovery by others. In some cases, you do not want your instances to be discovered whereas you just want do discover other instances.
|eureka.client.registry-fetch-interval-seconds | `+++30+++` | Indicates how often(in seconds) to fetch the registry information from the eureka server.
|eureka.client.registry-refresh-single-vip-address | | Indicates whether the client is only interested in the registry information for a single VIP.
|eureka.client.rest-template-timeout.connect-request-timeout | `+++0+++` |
|eureka.client.rest-template-timeout.connect-timeout | `+++0+++` | Default values are set to 180000, in keeping with {@link RequestConfig} and {@link SocketConfig} defaults.
|eureka.client.rest-template-timeout.socket-timeout | `+++0+++` |
|eureka.client.service-url | | Map of availability zone to list of fully qualified URLs to communicate with eureka server. Each value can be a single URL or a comma separated list of alternative locations. Typically the eureka server URLs carry protocol,host,port,context and version information if any. Example: https://ec2-256-156-243-129.compute-1.amazonaws.com:7001/eureka/ The changes are effective at runtime at the next service url refresh cycle as specified by eurekaServiceUrlPollIntervalSeconds.
|eureka.client.should-enforce-registration-at-init | `+++false+++` | Indicates whether the client should enforce registration during initialization. Defaults to false.
|eureka.client.should-unregister-on-shutdown | `+++true+++` | Indicates whether the client should explicitly unregister itself from the remote server on client shutdown.
|eureka.client.timeout.connect-request-timeout | `+++180000+++` |
|eureka.client.timeout.connect-timeout | `+++180000+++` | Default values are set to 180000, in keeping with {@link RequestConfig} and {@link SocketConfig} defaults.
|eureka.client.timeout.socket-timeout | `+++180000+++` |
|eureka.client.tls.enabled | |
|eureka.client.tls.key-password | |
|eureka.client.tls.key-store | |
@@ -69,20 +68,19 @@
|eureka.instance.a-s-g-name | | Gets the AWS autoscaling group name associated with this instance. This information is specifically used in an AWS environment to automatically put an instance out of service after the instance is launched and it has been disabled for traffic..
|eureka.instance.app-group-name | | Get the name of the application group to be registered with eureka.
|eureka.instance.appname | `+++unknown+++` | Get the name of the application to be registered with eureka.
|eureka.instance.async-client-initialization | `+++false+++` | If true the EurekaClient will be initialized asynchronously when the InstanceRegistry bean is created.
|eureka.instance.data-center-info | | Returns the data center this instance is deployed. This information is used to get some AWS specific instance information if the instance is deployed in AWS.
|eureka.instance.default-address-resolution-order | `+++[]+++` |
|eureka.instance.environment | |
|eureka.instance.health-check-url | | Gets the absolute health check page URL for this instance. The users can provide the healthCheckUrlPath if the health check page resides in the same instance talking to eureka, else in the cases where the instance is a proxy for some other server, users can provide the full URL. If the full URL is provided it takes precedence. <p> It is normally used for making educated decisions based on the health of the instance - for example, it can be used to determine whether to proceed deployments to an entire farm or stop the deployments without causing further damage. The full URL should follow the format http://${eureka.hostname}:7001/ where the value ${eureka.hostname} is replaced at runtime.
|eureka.instance.health-check-url-path | | Gets the relative health check URL path for this instance. The health check page URL is then constructed out of the hostname and the type of communication - secure or insecure as specified in securePort and nonSecurePort. It is normally used for making educated decisions based on the health of the instance - for example, it can be used to determine whether to proceed deployments to an entire farm or stop the deployments without causing further damage.
|eureka.instance.health-check-url-path | | Gets the relative health check URL path for this instance. The health check page URL is then constructed out of the hostname and the type of communication - secure or unsecure as specified in securePort and nonSecurePort. It is normally used for making educated decisions based on the health of the instance - for example, it can be used to determine whether to proceed deployments to an entire farm or stop the deployments without causing further damage.
|eureka.instance.home-page-url | | Gets the absolute home page URL for this instance. The users can provide the homePageUrlPath if the home page resides in the same instance talking to eureka, else in the cases where the instance is a proxy for some other server, users can provide the full URL. If the full URL is provided it takes precedence. It is normally used for informational purposes for other services to use it as a landing page. The full URL should follow the format http://${eureka.hostname}:7001/ where the value ${eureka.hostname} is replaced at runtime.
|eureka.instance.home-page-url-path | `+++/+++` | Gets the relative home page URL Path for this instance. The home page URL is then constructed out of the hostName and the type of communication - secure or insecure. It is normally used for informational purposes for other services to use it as a landing page.
|eureka.instance.home-page-url-path | `+++/+++` | Gets the relative home page URL Path for this instance. The home page URL is then constructed out of the hostName and the type of communication - secure or unsecure. It is normally used for informational purposes for other services to use it as a landing page.
|eureka.instance.hostname | | The hostname if it can be determined at configuration time (otherwise it will be guessed from OS primitives).
|eureka.instance.initial-status | `+++up+++` | Initial status to register with remote Eureka server.
|eureka.instance.initial-status | | Initial status to register with remote Eureka server.
|eureka.instance.instance-enabled-onit | `+++false+++` | Indicates whether the instance should be enabled for taking traffic as soon as it is registered with eureka. Sometimes the application might need to do some pre-processing before it is ready to take traffic.
|eureka.instance.instance-id | | Get the unique Id (within the scope of the appName) of this instance to be registered with eureka.
|eureka.instance.ip-address | | Get the IPAdress of the instance. This information is for academic purposes only as the communication from other instances primarily happen using the information supplied in {@link #getHostName(boolean)}.
|eureka.instance.lease-expiration-duration-in-seconds | `+++90+++` | Indicates the time in seconds that the eureka server waits since it received the last heartbeat before it can remove this instance from its view and there by disallowing traffic to this instance. Setting this value too long could mean that the traffic could be routed to the instance even though the instance is not alive. Setting this value too small could mean, the instance may be taken out of traffic because of temporary network glitches.This value to be set to at least higher than the value specified in leaseRenewalIntervalInSeconds.
|eureka.instance.lease-expiration-duration-in-seconds | `+++90+++` | Indicates the time in seconds that the eureka server waits since it received the last heartbeat before it can remove this instance from its view and there by disallowing traffic to this instance. Setting this value too long could mean that the traffic could be routed to the instance even though the instance is not alive. Setting this value too small could mean, the instance may be taken out of traffic because of temporary network glitches.This value to be set to atleast higher than the value specified in leaseRenewalIntervalInSeconds.
|eureka.instance.lease-renewal-interval-in-seconds | `+++30+++` | Indicates how often (in seconds) the eureka client needs to send heartbeats to eureka server to indicate that it is still alive. If the heartbeats are not received for the period specified in leaseExpirationDurationInSeconds, eureka server will remove the instance from its view, there by disallowing traffic to this instance. Note that the instance could still not take traffic if it implements HealthCheckCallback and then decides to make itself unavailable.
|eureka.instance.metadata-map | | Gets the metadata name/value pairs associated with this instance. This information is sent to eureka server and can be used by other instances.
|eureka.instance.metadata-map.weight | `+++1+++` | The weight of service instance for weighted load balancing.
@@ -97,25 +95,25 @@
|eureka.instance.secure-port-enabled | `+++false+++` | Indicates whether the secure port should be enabled for traffic or not.
|eureka.instance.secure-virtual-host-name | `+++unknown+++` | Gets the secure virtual host name defined for this instance. This is typically the way other instance would find this instance by using the secure virtual host name.Think of this as similar to the fully qualified domain name, that the users of your services will need to find this instance.
|eureka.instance.status-page-url | | Gets the absolute status page URL path for this instance. The users can provide the statusPageUrlPath if the status page resides in the same instance talking to eureka, else in the cases where the instance is a proxy for some other server, users can provide the full URL. If the full URL is provided it takes precedence. It is normally used for informational purposes for other services to find about the status of this instance. Users can provide a simple HTML indicating what is the current status of the instance.
|eureka.instance.status-page-url-path | | Gets the relative status page URL path for this instance. The status page URL is then constructed out of the hostName and the type of communication - secure or insecure as specified in securePort and nonSecurePort. It is normally used for informational purposes for other services to find about the status of this instance. Users can provide a simple HTML indicating what is the current status of the instance.
|eureka.instance.status-page-url-path | | Gets the relative status page URL path for this instance. The status page URL is then constructed out of the hostName and the type of communication - secure or unsecure as specified in securePort and nonSecurePort. It is normally used for informational purposes for other services to find about the status of this instance. Users can provide a simple HTML indicating what is the current status of the instance.
|eureka.instance.virtual-host-name | `+++unknown+++` | Gets the virtual host name defined for this instance. This is typically the way other instance would find this instance by using the virtual host name.Think of this as similar to the fully qualified domain name, that the users of your services will need to find this instance.
|eureka.server.a-s-g-cache-expiry-timeout-ms | `+++600000+++` |
|eureka.server.a-s-g-cache-expiry-timeout-ms | `+++0+++` |
|eureka.server.a-s-g-query-timeout-ms | `+++300+++` |
|eureka.server.a-s-g-update-interval-ms | `+++300000+++` |
|eureka.server.a-s-g-update-interval-ms | `+++0+++` |
|eureka.server.a-w-s-access-id | |
|eureka.server.a-w-s-secret-key | |
|eureka.server.batch-replication | `+++false+++` |
|eureka.server.binding-strategy | `+++eip+++` |
|eureka.server.delta-retention-timer-interval-in-ms | `+++30000+++` |
|eureka.server.binding-strategy | |
|eureka.server.delta-retention-timer-interval-in-ms | `+++0+++` |
|eureka.server.disable-delta | `+++false+++` |
|eureka.server.disable-delta-for-remote-regions | `+++false+++` |
|eureka.server.disable-transparent-fallback-to-other-region | `+++false+++` |
|eureka.server.e-i-p-bind-rebind-retries | `+++3+++` |
|eureka.server.e-i-p-binding-retry-interval-ms | `+++300000+++` |
|eureka.server.e-i-p-binding-retry-interval-ms-when-unbound | `+++60000+++` |
|eureka.server.e-i-p-binding-retry-interval-ms | `+++0+++` |
|eureka.server.e-i-p-binding-retry-interval-ms-when-unbound | `+++0+++` |
|eureka.server.enable-replicated-request-compression | `+++false+++` |
|eureka.server.enable-self-preservation | `+++true+++` |
|eureka.server.eviction-interval-timer-in-ms | |
|eureka.server.eviction-interval-timer-in-ms | `+++0+++` |
|eureka.server.expected-client-renewal-interval-seconds | `+++30+++` |
|eureka.server.g-zip-content-from-remote-region | `+++true+++` |
|eureka.server.initial-capacity-of-response-cache | `+++1000+++` |
@@ -129,14 +127,13 @@
|eureka.server.max-threads-for-peer-replication | `+++20+++` |
|eureka.server.max-threads-for-status-replication | `+++1+++` |
|eureka.server.max-time-for-replication | `+++30000+++` |
|eureka.server.metrics.enabled | `+++false+++` | Indicates whether the metrics should be enabled for eureka instances.
|eureka.server.min-available-instances-for-peer-replication | `+++-1+++` |
|eureka.server.min-threads-for-peer-replication | `+++5+++` |
|eureka.server.min-threads-for-status-replication | `+++1+++` |
|eureka.server.my-url | |
|eureka.server.number-of-replication-retries | `+++5+++` |
|eureka.server.peer-eureka-nodes-update-interval-ms | `+++600000+++` |
|eureka.server.peer-eureka-status-refresh-time-interval-ms | |
|eureka.server.peer-eureka-nodes-update-interval-ms | `+++0+++` |
|eureka.server.peer-eureka-status-refresh-time-interval-ms | `+++0+++` |
|eureka.server.peer-node-connect-timeout-ms | `+++200+++` |
|eureka.server.peer-node-connection-idle-timeout-seconds | `+++30+++` |
|eureka.server.peer-node-read-timeout-ms | `+++200+++` |
@@ -151,7 +148,7 @@
|eureka.server.rate-limiter-registry-fetch-average-rate | `+++500+++` |
|eureka.server.rate-limiter-throttle-standard-clients | `+++false+++` |
|eureka.server.registry-sync-retries | `+++0+++` |
|eureka.server.registry-sync-retry-wait-ms | |
|eureka.server.registry-sync-retry-wait-ms | `+++0+++` |
|eureka.server.remote-region-app-whitelist | |
|eureka.server.remote-region-connect-timeout-ms | `+++1000+++` |
|eureka.server.remote-region-connection-idle-timeout-seconds | `+++30+++` |
@@ -165,18 +162,18 @@
|eureka.server.remote-region-urls | |
|eureka.server.remote-region-urls-with-name | |
|eureka.server.renewal-percent-threshold | `+++0.85+++` |
|eureka.server.renewal-threshold-update-interval-ms | `+++900000+++` |
|eureka.server.renewal-threshold-update-interval-ms | `+++0+++` |
|eureka.server.response-cache-auto-expiration-in-seconds | `+++180+++` |
|eureka.server.response-cache-update-interval-ms | `+++30000+++` |
|eureka.server.retention-time-in-m-s-in-delta-queue | `+++180000+++` |
|eureka.server.response-cache-update-interval-ms | `+++0+++` |
|eureka.server.retention-time-in-m-s-in-delta-queue | `+++0+++` |
|eureka.server.route53-bind-rebind-retries | `+++3+++` |
|eureka.server.route53-binding-retry-interval-ms | `+++300000+++` |
|eureka.server.route53-binding-retry-interval-ms | `+++0+++` |
|eureka.server.route53-domain-t-t-l | `+++30+++` |
|eureka.server.sync-when-timestamp-differs | `+++true+++` |
|eureka.server.use-read-only-response-cache | `+++true+++` |
|eureka.server.wait-time-in-ms-when-sync-empty | `+++300000+++` |
|eureka.server.wait-time-in-ms-when-sync-empty | `+++0+++` |
|eureka.server.xml-codec-name | |
|spring.cloud.compatibility-verifier.compatible-boot-versions | | 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.5.x}
|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.
@@ -208,13 +205,6 @@
|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.api-version.default | | Sets default version that should be used for each request.
|spring.cloud.loadbalancer.api-version.fallback-to-available-instances | `+++false+++` | Indicates whether all the available instances should be returned if no instances for the specified version are available.
|spring.cloud.loadbalancer.api-version.header | | Uses the HTTP header with the given name to obtain the version.
|spring.cloud.loadbalancer.api-version.media-type-parameters | | Uses the media type parameter with the given name to obtain the version.
|spring.cloud.loadbalancer.api-version.path-segment | | Uses the path segment at the given index to obtain the version.
|spring.cloud.loadbalancer.api-version.query-parameter | | Uses the query parameter with the given name to obtain the version.
|spring.cloud.loadbalancer.api-version.required | `+++false+++` | Indicates whether the API version is required with each request.
|spring.cloud.loadbalancer.cache.caffeine.spec | | The spec to use to create caches. See CaffeineSpec for more details on the spec format.
|spring.cloud.loadbalancer.cache.capacity | `+++256+++` | Initial cache capacity expressed as int.
|spring.cloud.loadbalancer.cache.enabled | `+++true+++` | Enables Spring Cloud LoadBalancer caching mechanism.
@@ -227,6 +217,7 @@
|spring.cloud.loadbalancer.eureka.approximate-zone-from-hostname | `+++false+++` | Used to determine whether we should try to get the `zone` value from host name.
|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.
@@ -248,7 +239,6 @@
|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.service-discovery.timeout | | String representation of Duration of the timeout for calls to service discovery.
|spring.cloud.loadbalancer.stats.include-path | `+++true+++` | Indicates whether the {@code path} should be added to {@code uri} tag in metrics. When {@link RestTemplate} is used to execute load-balanced requests with high cardinality paths, setting it to {@code false} is recommended.
|spring.cloud.loadbalancer.stats.micrometer.enabled | `+++false+++` | Enables Spring Cloud LoadBalancer Micrometer stats.
|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.
@@ -258,9 +248,8 @@
|spring.cloud.loadbalancer.zone | | Spring Cloud LoadBalancer zone.
|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 bean names or class names for beans to post process into refresh scope.
|spring.cloud.refresh.never-refreshable | `+++true+++` | Comma separated list of bean names or class names for beans to never be refreshed or rebound.
|spring.cloud.refresh.never-reset-nested-types | | Fully qualified class name prefixes of nested configuration properties values that should never be recursively reset when a bean is rebound. JDK and standard API types are always skipped; use this to additionally exclude library types whose object graphs are cyclic or hold internal state that must not be mutated. Each entry is matched against the fully qualified class name using a prefix comparison, so either a package or an individual class name can be supplied.
|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.
-10
View File
@@ -1,10 +0,0 @@
{
"dependencies": {
"antora": "3.2.0",
"@antora/atlas-extension": "1.0.0-alpha.5",
"@antora/collector-extension": "1.0.3",
"@asciidoctor/tabs": "1.0.0-beta.6",
"@springio/antora-extensions": "1.14.13",
"@springio/asciidoctor-extensions": "1.0.0-alpha.18"
}
}
+12 -21
View File
@@ -8,7 +8,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>5.1.0-SNAPSHOT</version>
<version>4.1.1-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<packaging>jar</packaging>
@@ -21,28 +21,20 @@
<!-- Don't upload docs jar to central / repo.spring.io -->
<maven-deploy-plugin-default.phase>none</maven-deploy-plugin-default.phase>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/asciidoc</sourceDirectory>
</build>
<profiles>
<profile>
<id>enable-configuration-properties</id>
<activation>
<property>
<name>!disableConfigurationProperties</name>
</property>
</activation>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
</profile>
<profile>
<id>docs</id>
<build>
@@ -70,7 +62,7 @@
<artifactId>antora-component-version-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.antora</groupId>
<groupId>io.spring.maven.antora</groupId>
<artifactId>antora-maven-plugin</artifactId>
</plugin>
<plugin>
@@ -78,7 +70,6 @@
<artifactId>maven-antrun-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
</plugin>
</plugins>
Vendored
+116 -146
View File
@@ -19,7 +19,7 @@
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Apache Maven Wrapper startup batch script, version 3.3.4
# Apache Maven Wrapper startup batch script, version 3.2.0
#
# Required ENV vars:
# ------------------
@@ -33,84 +33,75 @@
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ]; then
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /usr/local/etc/mavenrc ]; then
if [ -f /usr/local/etc/mavenrc ] ; then
. /usr/local/etc/mavenrc
fi
if [ -f /etc/mavenrc ]; then
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ]; then
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false
darwin=false
cygwin=false;
darwin=false;
mingw=false
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
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
fi
;;
;;
esac
if [ -z "$JAVA_HOME" ]; then
if [ -r /etc/gentoo-release ]; then
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=$(java-config --jre-home)
fi
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin; then
[ -n "$JAVA_HOME" ] \
&& JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
[ -n "$CLASSPATH" ] \
&& CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
if $cygwin ; then
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
[ -n "$CLASSPATH" ] &&
CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
fi
# For Mingw, 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." >&2
exit 1
)
pwd
)"
if $mingw ; then
[ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="$(which javac)"
if [ -n "$javaExecutable" ] && ! [ "$(expr "$javaExecutable" : '\([^ ]*\)')" = "no" ]; then
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
if $darwin; then
javaHome="$(dirname "$javaExecutable")"
javaExecutable="$(cd "$javaHome" && pwd -P)/javac"
if $darwin ; then
javaHome="$(dirname "\"$javaExecutable\"")"
javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
else
javaExecutable="$(readlink -f "$javaExecutable")"
javaExecutable="$(readlink -f "\"$javaExecutable\"")"
fi
javaHome="$(dirname "$javaExecutable")"
javaHome="$(dirname "\"$javaExecutable\"")"
javaHome=$(expr "$javaHome" : '\(.*\)/bin')
JAVA_HOME="$javaHome"
export JAVA_HOME
@@ -118,60 +109,52 @@ if [ -z "$JAVA_HOME" ]; then
fi
fi
if [ -z "$JAVACMD" ]; then
if [ -n "$JAVA_HOME" ]; then
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="$(
\unset -f command 2>/dev/null
\command -v java
)"
JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
fi
fi
if [ ! -x "$JAVACMD" ]; then
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ]; then
echo "Warning: JAVA_HOME environment variable is not set." >&2
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
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" >&2
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ]; do
if [ -d "$wdir"/.mvn ]; then
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
)
wdir=$(cd "$wdir/.." || exit 1; pwd)
fi
# end of workaround
done
printf '%s' "$(
cd "$basedir" || exit 1
pwd
)"
printf '%s' "$(cd "$basedir" || exit 1; pwd)"
}
# concatenates all lines of a file
@@ -182,7 +165,7 @@ concat_lines() {
# 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"
tr -s '\r\n' ' ' < "$1"
fi
}
@@ -194,85 +177,75 @@ log() {
BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
if [ -z "$BASE_DIR" ]; then
exit 1
exit 1;
fi
MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
export MAVEN_PROJECTBASEDIR
MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
log "$MAVEN_PROJECTBASEDIR"
trim() {
# MWRAPPER-139:
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
# Needed for removing poorly interpreted newline sequences when running in more
# exotic environments such as mingw bash on Windows.
printf "%s" "${1}" | tr -d '[:space:]'
}
##########################################################################################
# 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"
log "Found $wrapperJarPath"
else
log "Couldn't find $wrapperJarPath, downloading it ..."
log "Couldn't find $wrapperJarPath, downloading it ..."
if [ -n "$MVNW_REPOURL" ]; then
wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar"
else
wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar"
fi
while IFS="=" read -r key value; do
case "$key" in wrapperUrl)
wrapperUrl=$(trim "${value-}")
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:+"$QUIET"} "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
if [ -n "$MVNW_REPOURL" ]; then
wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
else
wget ${QUIET:+"$QUIET"} --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
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:+"$QUIET"} -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
else
curl ${QUIET:+"$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
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
javaSource=$(cygpath --path --windows "$javaSource")
javaClass=$(cygpath --path --windows "$javaClass")
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
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
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
fi
##########################################################################################
# End of extension
@@ -281,25 +254,22 @@ fi
# 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=$(trim "${value-}")
break
;;
case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
esac
done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
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
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
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." >&2
echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." >&2
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
@@ -314,12 +284,12 @@ 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")
[ -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
Vendored
+10 -11
View File
@@ -18,7 +18,7 @@
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Apache Maven Wrapper startup batch script, version 3.3.4
@REM Apache Maven Wrapper startup batch script, version 3.2.0
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@@ -59,22 +59,22 @@ set ERROR_CODE=0
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo. >&2
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo. >&2
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo. >&2
echo.
goto error
@REM ==== END VALIDATION ====
@@ -119,7 +119,7 @@ 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.3.4/maven-wrapper-3.3.4.jar"
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
@@ -133,7 +133,7 @@ if exist %WRAPPER_JAR% (
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar"
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 ...
@@ -160,12 +160,11 @@ FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapp
)
IF NOT %WRAPPER_SHA_256_SUM%=="" (
powershell -Command "&{"^
"Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash;"^
"$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
"If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
" Write-Error 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
" Write-Error 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
" Write-Error 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
" 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;"^
"}"^
"}"
+12 -91
View File
@@ -3,15 +3,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-netflix</artifactId>
<version>5.1.0-SNAPSHOT</version>
<version>4.1.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Spring Cloud Netflix</name>
<description>Spring Cloud Netflix</description>
<url>https://github.com/spring-cloud/spring-cloud-netflix</url>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>5.1.0-SNAPSHOT</version>
<version>4.1.1-SNAPSHOT</version>
<relativePath />
</parent>
<scm>
@@ -20,69 +19,11 @@
<developerConnection>scm:git:ssh://git@github.com/spring-cloud/spring-cloud-netflix.git</developerConnection>
<tag>HEAD</tag>
</scm>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
</license>
</licenses>
<developers>
<developer>
<id>dsyer</id>
<name>Dave Syer</name>
<email>david.syer at broadcom.com</email>
<organization>Broadcom, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>lead</role>
</roles>
</developer>
<developer>
<id>sgibb</id>
<name>Spencer Gibb</name>
<email>spencer.gibb at broadcom.com</email>
<organization>Broadcom, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>lead</role>
</roles>
</developer>
<developer>
<id>mgrzejszczak</id>
<name>Marcin Grzejszczak</name>
<email>>marcin.grzejszczak at broadcom.com</email>
<organization>Broadcom, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>developer</role>
</roles>
</developer>
<developer>
<id>rbaxter</id>
<name>Ryan Baxter</name>
<email>ryan.baxter at broadcom.com</email>
<organization>Broadcom, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>developer</role>
</roles>
</developer>
<developer>
<id>omaciaszeksharma</id>
<name>Olga Maciaszek-Sharma</name>
<email>olga.maciaszek-sharma at broadcom.com</email>
<organization>Broadcom, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>developer</role>
</roles>
</developer>
</developers>
<properties>
<bintray.package>netflix</bintray.package>
<spring-cloud-commons.version>5.1.0-SNAPSHOT</spring-cloud-commons.version>
<spring-cloud-config.version>5.1.0-SNAPSHOT</spring-cloud-config.version>
<testcontainers.version>1.21.4</testcontainers.version>
<spring-cloud-commons.version>4.1.1-SNAPSHOT</spring-cloud-commons.version>
<spring-cloud-config.version>4.1.1-SNAPSHOT</spring-cloud-config.version>
<testcontainers.version>1.19.3</testcontainers.version>
<mockserverclient.version>5.15.0</mockserverclient.version>
<!-- Sonar -->
@@ -91,6 +32,8 @@
<sonar.jacoco.reportPath>${project.basedir}/../target/jacoco.exec</sonar.jacoco.reportPath>
<sonar.language>java</sonar.language>
<maven-resources-plugin.version>3.1.0</maven-resources-plugin.version>
<!-- FIXME: 4.0.0 -->
<duplicate-finder-maven-plugin.skip>true</duplicate-finder-maven-plugin.skip>
</properties>
<build>
<plugins>
@@ -189,12 +132,6 @@
<groupId>org.mock-server</groupId>
<artifactId>mockserver-client-java</artifactId>
<version>${mockserverclient.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
@@ -216,29 +153,13 @@
<module>docs</module>
</modules>
<profiles>
<profile>
<id>central</id>
<build>
<plugins>
<plugin>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<configuration>
<excludeArtifacts>
<artifact>spring-cloud-netflix-eureka-client-tls-tests</artifact>
</excludeArtifacts>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>spring</id>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<url>https://repo.spring.io/libs-snapshot-local</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
@@ -249,7 +170,7 @@
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<url>https://repo.spring.io/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
@@ -267,7 +188,7 @@
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<url>https://repo.spring.io/libs-snapshot-local</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
@@ -278,7 +199,7 @@
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<url>https://repo.spring.io/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
@@ -286,7 +207,7 @@
<pluginRepository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/release</url>
<url>https://repo.spring.io/libs-release-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
-73
View File
@@ -1,73 +0,0 @@
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<servers>
<server>
<id>release-train</id>
<username>${env.RELEASE_TRAIN_MAVEN_REPOSITORY_USERNAME}</username>
<password>${env.RELEASE_TRAIN_MAVEN_REPOSITORY_PASSWORD}</password>
</server>
<server>
<id>spring-commercial-snapshot</id>
<username>${env.COMMERCIAL_REPO_USERNAME}</username>
<password>${env.COMMERCIAL_REPO_PASSWORD}</password>
</server>
<server>
<id>spring-commercial-release</id>
<username>${env.COMMERCIAL_REPO_USERNAME}</username>
<password>${env.COMMERCIAL_REPO_PASSWORD}</password>
</server>
<server>
<id>repo-spring-io-spring-commercial-snapshot</id>
<username>${env.COMMERCIAL_REPO_USERNAME}</username>
<password>${env.COMMERCIAL_REPO_PASSWORD}</password>
</server>
<server>
<id>repo-spring-io-spring-commercial-release</id>
<username>${env.COMMERCIAL_REPO_USERNAME}</username>
<password>${env.COMMERCIAL_REPO_PASSWORD}</password>
</server>
</servers>
<mirrors>
<mirror>
<!-- This redirects the spring-snapshots repo to the release-train repo -->
<id>release-train</id>
<mirrorOf>spring-snapshots</mirrorOf>
<url>${env.RELEASE_TRAIN_MAVEN_REPOSITORY_URL}</url>
</mirror>
</mirrors>
<profiles>
<profile>
<id>releaseTrain</id>
<repositories>
<repository>
<id>release-train</id>
<url>${env.RELEASE_TRAIN_MAVEN_REPOSITORY_URL}</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>release-train</id>
<url>${env.RELEASE_TRAIN_MAVEN_REPOSITORY_URL}</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
</settings>
+8 -19
View File
@@ -5,16 +5,16 @@
<parent>
<artifactId>spring-cloud-dependencies-parent</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>5.1.0-SNAPSHOT</version>
<version>4.1.1-SNAPSHOT</version>
<relativePath/>
</parent>
<artifactId>spring-cloud-netflix-dependencies</artifactId>
<version>5.1.0-SNAPSHOT</version>
<version>4.1.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>spring-cloud-netflix-dependencies</name>
<description>Spring Cloud Netflix Dependencies</description>
<properties>
<eureka.version>2.0.6</eureka.version>
<eureka.version>2.0.1</eureka.version>
</properties>
<dependencyManagement>
<dependencies>
@@ -43,10 +43,6 @@
<artifactId>eureka-client</artifactId>
<version>${eureka.version}</version>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
<exclusion>
<groupId>com.netflix.archaius</groupId>
<artifactId>archaius-core</artifactId>
@@ -63,10 +59,6 @@
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
<exclusion>
<artifactId>commons-configuration</artifactId>
<groupId>commons-configuration</groupId>
</exclusion>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
@@ -146,12 +138,9 @@
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
</exclusion>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- TODO: Move back to Eureka dependency when 1.10.17 released -->
</dependencies>
</dependencyManagement>
<profiles>
@@ -161,7 +150,7 @@
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<url>https://repo.spring.io/libs-snapshot-local</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
@@ -172,7 +161,7 @@
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<url>https://repo.spring.io/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
@@ -190,7 +179,7 @@
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<url>https://repo.spring.io/libs-snapshot-local</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
@@ -201,7 +190,7 @@
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<url>https://repo.spring.io/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>5.1.0-SNAPSHOT</version>
<version>4.1.1-SNAPSHOT</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-netflix-eureka-client-tls-tests</artifactId>
@@ -22,6 +22,7 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
@@ -47,10 +48,6 @@
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
@@ -73,7 +70,7 @@
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aspectj</artifactId>
<artifactId>spring-boot-starter-aop</artifactId>
<optional>true</optional>
</dependency>
<dependency>
@@ -95,24 +92,16 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite-engine</artifactId>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-restclient</artifactId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.70</version>
<scope>test</scope>
</dependency>
</dependencies>
@@ -1,5 +1,5 @@
/*
* Copyright 2018-present the original author or authors.
* Copyright 2018-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.
@@ -48,14 +48,14 @@ public class AppRunner implements AutoCloseable {
SpringApplicationBuilder builder = new SpringApplicationBuilder(appClass);
builder.properties("spring.application.name=" + appClass.getName());
builder.properties("spring.jmx.enabled=false");
builder.properties(String.format("server.port=%d", availableTcpPort()));
builder.properties(String.format("server.port=%d", availabeTcpPort()));
builder.properties(props());
app = builder.build().run();
}
}
private int availableTcpPort() {
private int availabeTcpPort() {
return TestSocketUtils.findAvailableTcpPort();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2018-present the original author or authors.
* Copyright 2018-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.
@@ -25,17 +25,17 @@ import java.util.function.Supplier;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanCreationException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
abstract class BaseCertTests {
abstract class BaseCertTest {
private static final Log log = LogFactory.getLog(BaseCertTests.class);
private static final Log log = LogFactory.getLog(BaseCertTest.class);
protected static final String KEY_STORE_PASSWORD = "test-key-store-password";
@@ -53,7 +53,7 @@ abstract class BaseCertTests {
protected static File wrongClientCert;
protected BaseCertTests() {
protected BaseCertTest() {
}
@SuppressWarnings("rawtypes")
@@ -155,7 +155,15 @@ abstract class BaseCertTests {
EurekaClientRunner client = createEurekaClient();
enableTlsClient(client);
client.setKeyStore(clientCert, WRONG_PASSWORD, WRONG_PASSWORD);
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(client::start);
Assertions.assertThrows(BeanCreationException.class, client::start);
}
@Test
void nonExistKeyStoreCauseFailure() {
EurekaClientRunner client = createEurekaClient();
enableTlsClient(client);
client.setKeyStore(new File("nonExistFile"));
Assertions.assertThrows(BeanCreationException.class, client::start);
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2018-present the original author or authors.
* Copyright 2018-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.
@@ -1,5 +1,5 @@
/*
* Copyright 2018-present the original author or authors.
* Copyright 2018-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,8 @@
package org.springframework.cloud.netflix.eureka;
import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.Suite;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
/**
* We need to use a suite cause we need to first run all the Eureka Servers, then close
@@ -26,8 +26,8 @@ import org.junit.platform.suite.api.Suite;
* That means that when the new context starts we will fail cause the executor service is
* already shutdown.
*/
@SelectClasses({ EurekaClientTests.class, RestClientEurekaClientTests.class, WebClientEurekaClientTests.class })
@Suite
@RunWith(Suite.class)
@Suite.SuiteClasses({ EurekaClientTest.class, RestTemplateEurekaClientTest.class })
public class EurekaClientSuite {
}
@@ -1,5 +1,5 @@
/*
* Copyright 2018-present the original author or authors.
* Copyright 2018-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,26 +16,20 @@
package org.springframework.cloud.netflix.eureka;
import java.util.Collections;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.BeforeAll;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.netflix.eureka.http.DefaultEurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.RestClientDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.http.RestClientTransportClientFactories;
import org.springframework.cloud.netflix.eureka.http.RestTemplateDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestClient;
import static org.assertj.core.api.Assertions.assertThat;
public class EurekaClientTests extends BaseCertTests {
public class EurekaClientTest extends BaseCertTest {
private static final Log LOG = LogFactory.getLog(EurekaClientTests.class);
private static final Log log = LogFactory.getLog(EurekaClientTest.class);
static EurekaServerRunner server;
@@ -43,11 +37,11 @@ public class EurekaClientTests extends BaseCertTests {
@BeforeAll
public static void setupAll() {
server = startEurekaServer(EurekaClientTests.TestEurekaServer.class);
service = startService(server, EurekaClientTests.TestApp.class);
assertThat(service.discoveryClientOptionalArgs()).isInstanceOf(RestClientDiscoveryClientOptionalArgs.class);
LOG.info("Successfully asserted that Jersey will be used");
waitForRegistration(() -> new EurekaClientTests().createEurekaClient());
server = startEurekaServer(EurekaClientTest.TestEurekaServer.class);
service = startService(server, EurekaClientTest.TestApp.class);
assertThat(service.discoveryClientOptionalArgs()).isInstanceOf(RestTemplateDiscoveryClientOptionalArgs.class);
log.info("Successfully asserted that Jersey will be used");
waitForRegistration(() -> new EurekaClientTest().createEurekaClient());
}
@Override
@@ -59,19 +53,6 @@ public class EurekaClientTests extends BaseCertTests {
@EnableAutoConfiguration
public static class TestApp {
@Bean
public RestClientTransportClientFactories forceRestClientTransportClientFactories(
RestClientDiscoveryClientOptionalArgs discoveryClientOptionalArgs) {
return new RestClientTransportClientFactories(discoveryClientOptionalArgs);
}
@Bean
public RestClientDiscoveryClientOptionalArgs discoveryClientOptionalArgs() {
return new RestClientDiscoveryClientOptionalArgs(
new DefaultEurekaClientHttpRequestFactorySupplier(new TimeoutProperties(), Collections.emptySet()),
RestClient::builder);
}
}
@SpringBootConfiguration
@@ -1,5 +1,5 @@
/*
* Copyright 2018-present the original author or authors.
* Copyright 2018-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.
@@ -1,5 +1,5 @@
/*
* Copyright 2018-present the original author or authors.
* Copyright 2018-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.
@@ -1,5 +1,5 @@
/*
* Copyright 2018-present the original author or authors.
* Copyright 2018-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.
@@ -1,114 +0,0 @@
/*
* Copyright 2018-present 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.netflix.eureka;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Collections;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.configuration.TlsProperties;
import org.springframework.cloud.netflix.eureka.http.DefaultEurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.EurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.RestClientDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.http.RestClientTransportClientFactories;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestClient;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration.setupTLS;
/**
* Tests for verifying TLS setup with {@link RestClientTransportClientFactories}.
*
* @author Olga Maciaszek-Sharma
*/
public class RestClientEurekaClientTests extends BaseCertTests {
private static final Log LOG = LogFactory.getLog(RestClientEurekaClientTests.class);
private static EurekaServerRunner server;
private static EurekaClientRunner service;
@BeforeAll
public static void setupAll() {
server = startEurekaServer(TestEurekaServer.class);
service = startService(server, TestApp.class);
// Will use RestClient
assertThat(service.discoveryClientOptionalArgs()).isInstanceOf(RestClientDiscoveryClientOptionalArgs.class);
LOG.info("Successfully asserted that RestClient will be used");
waitForRegistration(() -> new RestClientEurekaClientTests().createEurekaClient());
}
@AfterAll
public static void tearDownAll() {
stopService(service);
stopEurekaServer(server);
}
@Override
EurekaClientRunner createEurekaClient() {
return new EurekaClientRunner(TestApp.class, server);
}
@SpringBootConfiguration
@EnableAutoConfiguration
public static class TestApp {
// Want to force reusing exactly the same bean as on production without excluding
// jersey from the classpath
@Bean
public RestClientDiscoveryClientOptionalArgs forceRestClientDiscoveryClientOptionalArgs(
TlsProperties tlsProperties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier)
throws GeneralSecurityException, IOException {
RestClientDiscoveryClientOptionalArgs result = new RestClientDiscoveryClientOptionalArgs(
eurekaClientHttpRequestFactorySupplier, RestClient::builder);
setupTLS(result, tlsProperties);
return result;
}
@Bean
public RestClientTransportClientFactories forceRestClientTransportClientFactories(
RestClientDiscoveryClientOptionalArgs discoveryClientOptionalArgs) {
return new RestClientTransportClientFactories(discoveryClientOptionalArgs);
}
@Bean
EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier(
TimeoutProperties timeoutProperties) {
return new DefaultEurekaClientHttpRequestFactorySupplier(timeoutProperties, Collections.emptySet());
}
}
@SpringBootConfiguration
@EnableAutoConfiguration
@EnableEurekaServer
public static class TestEurekaServer {
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2018-present the original author or authors.
* Copyright 2018-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.
@@ -27,23 +27,17 @@ import org.junit.jupiter.api.BeforeAll;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.configuration.TlsProperties;
import org.springframework.cloud.netflix.eureka.http.WebClientDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.http.WebClientTransportClientFactories;
import org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration;
import org.springframework.cloud.netflix.eureka.http.EurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.RestTemplateDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import org.springframework.context.annotation.Bean;
import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration.setupTLS;
/**
* Tests for verifying TLS setup with {@link WebClientTransportClientFactories}.
*
* @author Yash Chauhan
*/
public class WebClientEurekaClientTests extends BaseCertTests {
public class RestTemplateEurekaClientTest extends BaseCertTest {
private static final Log LOG = LogFactory.getLog(WebClientEurekaClientTests.class);
private static final Log log = LogFactory.getLog(RestTemplateEurekaClientTest.class);
private static EurekaServerRunner server;
@@ -51,11 +45,12 @@ public class WebClientEurekaClientTests extends BaseCertTests {
@BeforeAll
public static void setupAll() {
server = startEurekaServer(TestEurekaServer.class);
service = startService(server, TestApp.class);
assertThat(service.discoveryClientOptionalArgs()).isInstanceOf(WebClientDiscoveryClientOptionalArgs.class);
LOG.info("Successfully asserted that WebClient will be used");
waitForRegistration(() -> new WebClientEurekaClientTests().createEurekaClient());
server = startEurekaServer(RestTemplateEurekaClientTest.RestTemplateTestEurekaServer.class);
service = startService(server, RestTemplateEurekaClientTest.RestTemplateTestApp.class);
// Will use RestTemplate
assertThat(service.discoveryClientOptionalArgs()).isInstanceOf(RestTemplateDiscoveryClientOptionalArgs.class);
log.info("Successfully asserted that RestTemplate will be used");
waitForRegistration(() -> new RestTemplateEurekaClientTest().createEurekaClient());
}
@AfterAll
@@ -66,25 +61,22 @@ public class WebClientEurekaClientTests extends BaseCertTests {
@Override
EurekaClientRunner createEurekaClient() {
return new EurekaClientRunner(TestApp.class, server);
return new EurekaClientRunner(RestTemplateTestApp.class, server);
}
@SpringBootConfiguration
@EnableAutoConfiguration
public static class TestApp {
public static class RestTemplateTestApp {
// Want to force reusing exactly the same bean as on production without excluding
// jersey from the classpath
@Bean
public WebClientDiscoveryClientOptionalArgs forceWebClientDiscoveryClientOptionalArgs(
TlsProperties tlsProperties) throws GeneralSecurityException, IOException {
WebClientDiscoveryClientOptionalArgs result = new WebClientDiscoveryClientOptionalArgs(WebClient::builder);
result.setHostnameVerifier((hostname, session) -> true);
setupTLS(result, tlsProperties);
return result;
}
@Bean
public WebClientTransportClientFactories forceWebClientTransportClientFactories() {
return new WebClientTransportClientFactories(WebClient::builder);
public RestTemplateDiscoveryClientOptionalArgs forceRestTemplateDiscoveryClientOptionalArgs(
TlsProperties tlsProperties, DiscoveryClientOptionalArgsConfiguration configuration,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier)
throws GeneralSecurityException, IOException {
return configuration.restTemplateDiscoveryClientOptionalArgs(tlsProperties,
eurekaClientHttpRequestFactorySupplier);
}
}
@@ -92,7 +84,7 @@ public class WebClientEurekaClientTests extends BaseCertTests {
@SpringBootConfiguration
@EnableAutoConfiguration
@EnableEurekaServer
public static class TestEurekaServer {
public static class RestTemplateTestEurekaServer {
}
+6 -20
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>5.1.0-SNAPSHOT</version>
<version>4.1.1-SNAPSHOT</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
@@ -18,30 +18,11 @@
<maven.javadoc.failOnError>false</maven.javadoc.failOnError>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-jackson2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-http-codec</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-restclient</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-webclient</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
@@ -101,6 +82,11 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-test-support</artifactId>
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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.
@@ -193,43 +193,20 @@ public class CloudEurekaTransportConfig implements EurekaTransportConfig {
@Override
public String toString() {
return new StringBuilder("CloudEurekaTransportConfig{").append("sessionedClientReconnectIntervalSeconds=")
.append(sessionedClientReconnectIntervalSeconds)
.append(", ")
.append("retryableClientQuarantineRefreshPercentage=")
.append(retryableClientQuarantineRefreshPercentage)
.append(", ")
.append("bootstrapResolverRefreshIntervalSeconds=")
.append(bootstrapResolverRefreshIntervalSeconds)
.append(", ")
.append("applicationsResolverDataStalenessThresholdSeconds=")
.append(applicationsResolverDataStalenessThresholdSeconds)
.append(", ")
.append("asyncResolverRefreshIntervalMs=")
.append(asyncResolverRefreshIntervalMs)
.append(", ")
.append("asyncResolverWarmUpTimeoutMs=")
.append(asyncResolverWarmUpTimeoutMs)
.append(", ")
.append("asyncExecutorThreadPoolSize=")
.append(asyncExecutorThreadPoolSize)
.append(", ")
.append("readClusterVip='")
.append(readClusterVip)
.append("', ")
.append("writeClusterVip='")
.append(writeClusterVip)
.append("', ")
.append("bootstrapResolverForQuery=")
.append(bootstrapResolverForQuery)
.append(", ")
.append("bootstrapResolverStrategy='")
.append(bootstrapResolverStrategy)
.append("', ")
.append("applicationsResolverUseIp=")
.append(applicationsResolverUseIp)
.append(", ")
.append("}")
.toString();
.append(sessionedClientReconnectIntervalSeconds).append(", ")
.append("retryableClientQuarantineRefreshPercentage=")
.append(retryableClientQuarantineRefreshPercentage).append(", ")
.append("bootstrapResolverRefreshIntervalSeconds=").append(bootstrapResolverRefreshIntervalSeconds)
.append(", ").append("applicationsResolverDataStalenessThresholdSeconds=")
.append(applicationsResolverDataStalenessThresholdSeconds).append(", ")
.append("asyncResolverRefreshIntervalMs=").append(asyncResolverRefreshIntervalMs).append(", ")
.append("asyncResolverWarmUpTimeoutMs=").append(asyncResolverWarmUpTimeoutMs).append(", ")
.append("asyncExecutorThreadPoolSize=").append(asyncExecutorThreadPoolSize).append(", ")
.append("readClusterVip='").append(readClusterVip).append("', ").append("writeClusterVip='")
.append(writeClusterVip).append("', ").append("bootstrapResolverForQuery=")
.append(bootstrapResolverForQuery).append(", ").append("bootstrapResolverStrategy='")
.append(bootstrapResolverStrategy).append("', ").append("applicationsResolverUseIp=")
.append(applicationsResolverUseIp).append(", ").append("}").toString();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present the original author or authors.
* 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.
@@ -56,6 +56,8 @@ import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
@@ -66,8 +68,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClas
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.health.autoconfigure.contributor.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.health.contributor.Health;
import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration;
import org.springframework.cloud.client.CommonsClientAutoConfiguration;
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
@@ -103,7 +103,6 @@ import static org.springframework.cloud.commons.util.IdUtils.getDefaultInstanceI
* @author Daniel Lavoie
* @author Olga Maciaszek-Sharma
* @author Tim Ysewyn
* @author Robert Bleyl
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@@ -225,8 +224,8 @@ public class EurekaClientAutoConfiguration {
}
@Bean
public EurekaServiceRegistry eurekaServiceRegistry(EurekaInstanceConfigBean eurekaInstanceConfigBean) {
return new EurekaServiceRegistry(eurekaInstanceConfigBean);
public EurekaServiceRegistry eurekaServiceRegistry() {
return new EurekaServiceRegistry();
}
// @Bean
@@ -281,11 +280,8 @@ public class EurekaClientAutoConfiguration {
public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient,
CloudEurekaInstanceConfig instanceConfig, ApplicationInfoManager applicationInfoManager,
@Autowired(required = false) ObjectProvider<HealthCheckHandler> healthCheckHandler) {
return EurekaRegistration.builder(instanceConfig)
.with(applicationInfoManager)
.with(eurekaClient)
.with(healthCheckHandler)
.build();
return EurekaRegistration.builder(instanceConfig).with(applicationInfoManager).with(eurekaClient)
.with(healthCheckHandler).build();
}
}
@@ -343,11 +339,8 @@ public class EurekaClientAutoConfiguration {
public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient,
CloudEurekaInstanceConfig instanceConfig, ApplicationInfoManager applicationInfoManager,
@Autowired(required = false) ObjectProvider<HealthCheckHandler> healthCheckHandler) {
return EurekaRegistration.builder(instanceConfig)
.with(applicationInfoManager)
.with(eurekaClient)
.with(healthCheckHandler)
.build();
return EurekaRegistration.builder(instanceConfig).with(applicationInfoManager).with(eurekaClient)
.with(healthCheckHandler).build();
}
}
@@ -399,7 +392,6 @@ public class EurekaClientAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(ConditionalOnEnabledHealthIndicator.class)
@ConditionalOnEnabledHealthIndicator("eureka")
public EurekaHealthIndicator eurekaHealthIndicator(EurekaClient eurekaClient,
EurekaInstanceConfig instanceConfig, EurekaClientConfig clientConfig) {
@@ -418,30 +410,75 @@ class EurekaClientHints implements RuntimeHintsRegistrar {
if (!ClassUtils.isPresent("com.netflix.discovery.DiscoveryClient", classLoader)) {
return;
}
hints.reflection()
.registerTypes(TypeReference.listOf(ApplicationInfoManager.class),
hint -> hint.withMembers(MemberCategory.INTROSPECT_DECLARED_METHODS))
.registerTypes(
TypeReference.listOf(DiscoveryClient.class, EurekaHttpClientDecorator.class,
InstanceInfo.ActionType.class, InstanceInfoJsonMixIn.class, SessionedEurekaHttpClient.class,
RetryableEurekaHttpClient.class, AsyncResolver.class, Applications.class,
TimedSupervisorTask.class),
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS,
MemberCategory.INTROSPECT_DECLARED_METHODS))
.registerTypes(
TypeReference.listOf(EurekaEndpoint.class, EurekaHttpClientDecorator.RequestExecutor.class,
EurekaClient.class, DiscoveryClient.class),
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS))
.registerTypes(TypeReference.listOf(DataCenterTypeInfoResolver.class),
hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS))
.registerTypes(TypeReference.listOf(Application.class),
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INTROSPECT_DECLARED_METHODS,
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS))
.registerTypes(TypeReference.listOf(DefaultEndpoint.class, EurekaHttpResponse.class, InstanceInfo.class,
InstanceInfo.PortWrapper.class, LeaseInfo.class, MyDataCenterInfo.class, DataCenterInfo.class,
DataCenterInfo.Name.class, ApplicationsJacksonBuilder.class, EurekaServiceInstance.class),
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
hints.reflection().registerType(TypeReference.of(DiscoveryClient.class),
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INTROSPECT_DECLARED_METHODS))
.registerType(TypeReference.of(EurekaEndpoint.class),
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS))
.registerType(TypeReference.of(DefaultEndpoint.class),
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
.registerType(TypeReference.of(EurekaHttpClientDecorator.class),
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS,
MemberCategory.INTROSPECT_DECLARED_METHODS))
.registerType(TypeReference.of(EurekaHttpResponse.class),
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
.registerType(TypeReference.of(EurekaHttpClientDecorator.RequestExecutor.class),
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS))
.registerType(TypeReference.of(ApplicationInfoManager.class),
hint -> hint.withMembers(MemberCategory.INTROSPECT_DECLARED_METHODS))
.registerType(TypeReference.of(InstanceInfo.class),
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
.registerType(TypeReference.of(InstanceInfo.ActionType.class),
hint -> hint.withMembers(MemberCategory.INTROSPECT_DECLARED_METHODS,
MemberCategory.DECLARED_FIELDS))
.registerType(TypeReference.of(InstanceInfo.PortWrapper.class),
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS))
.registerType(TypeReference.of(LeaseInfo.class),
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS))
.registerType(TypeReference.of(MyDataCenterInfo.class),
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS))
.registerType(TypeReference.of(DataCenterInfo.class),
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS))
.registerType(TypeReference.of(DataCenterInfo.Name.class),
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS))
.registerType(TypeReference.of(EurekaClient.class),
hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS))
.registerType(TypeReference.of(TimedSupervisorTask.class),
hint -> hint.withMembers(MemberCategory.INTROSPECT_DECLARED_METHODS,
MemberCategory.DECLARED_FIELDS))
.registerType(TypeReference.of(DataCenterTypeInfoResolver.class),
hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS))
.registerType(TypeReference.of(ApplicationsJacksonBuilder.class),
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS))
.registerType(TypeReference.of(InstanceInfoJsonMixIn.class),
hint -> hint.withMembers(MemberCategory.INTROSPECT_DECLARED_METHODS,
MemberCategory.DECLARED_FIELDS))
.registerType(TypeReference.of(Application.class),
hint -> hint.withMembers(MemberCategory.INTROSPECT_DECLARED_METHODS,
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS))
.registerType(TypeReference.of(Applications.class),
hint -> hint.withMembers(MemberCategory.INTROSPECT_DECLARED_METHODS,
MemberCategory.DECLARED_FIELDS))
.registerType(TypeReference.of(AsyncResolver.class),
hint -> hint.withMembers(MemberCategory.INTROSPECT_DECLARED_METHODS,
MemberCategory.DECLARED_FIELDS))
.registerType(TypeReference.of(RetryableEurekaHttpClient.class),
hint -> hint.withMembers(MemberCategory.INTROSPECT_DECLARED_METHODS,
MemberCategory.DECLARED_FIELDS))
.registerType(TypeReference.of(SessionedEurekaHttpClient.class),
hint -> hint.withMembers(MemberCategory.INTROSPECT_DECLARED_METHODS,
MemberCategory.DECLARED_FIELDS))
.registerType(TypeReference.of(EurekaServiceInstance.class),
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS));
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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.
@@ -41,7 +41,6 @@ import static org.springframework.cloud.netflix.eureka.EurekaConstants.DEFAULT_P
*
* @author Dave Syer
* @author Gregor Zurowski
* @author Mooyong Lee
*/
@ConfigurationProperties(EurekaClientConfigBean.PREFIX)
public class EurekaClientConfigBean implements EurekaClientConfig, Ordered {
@@ -61,6 +60,8 @@ public class EurekaClientConfigBean implements EurekaClientConfig, Ordered {
*/
public static final String DEFAULT_ZONE = "defaultZone";
private static final int MINUTES = 60;
@Autowired(required = false)
PropertyResolver propertyResolver;
@@ -95,7 +96,7 @@ public class EurekaClientConfigBean implements EurekaClientConfig, Ordered {
* Eureka servers could be added or removed and this setting controls how soon the
* eureka clients should know about it.
*/
private int eurekaServiceUrlPollIntervalSeconds = 300; // 5 * MINUTES
private int eurekaServiceUrlPollIntervalSeconds = 5 * MINUTES;
/**
* Gets the proxy port to eureka server if any.
@@ -997,147 +998,47 @@ public class EurekaClientConfigBean implements EurekaClientConfig, Ordered {
@Override
public String toString() {
return new StringBuilder("EurekaClientConfigBean{").append("propertyResolver=")
.append(propertyResolver)
.append(", ")
.append("enabled=")
.append(enabled)
.append(", ")
.append("transport=")
.append(transport)
.append(", ")
.append("registryFetchIntervalSeconds=")
.append(registryFetchIntervalSeconds)
.append(", ")
.append("instanceInfoReplicationIntervalSeconds=")
.append(instanceInfoReplicationIntervalSeconds)
.append(", ")
.append("initialInstanceInfoReplicationIntervalSeconds=")
.append(initialInstanceInfoReplicationIntervalSeconds)
.append(", ")
.append("eurekaServiceUrlPollIntervalSeconds=")
.append(eurekaServiceUrlPollIntervalSeconds)
.append(", ")
.append("proxyPort='")
.append(proxyPort)
.append("', ")
.append("proxyHost='")
.append(proxyHost)
.append("', ")
.append("proxyUserName='")
.append(proxyUserName)
.append("', ")
.append("proxyPassword='")
.append(proxyPassword)
.append("', ")
.append("eurekaServerReadTimeoutSeconds=")
.append(eurekaServerReadTimeoutSeconds)
.append(", ")
.append("eurekaServerConnectTimeoutSeconds=")
.append(eurekaServerConnectTimeoutSeconds)
.append(", ")
.append("backupRegistryImpl='")
.append(backupRegistryImpl)
.append("', ")
.append("eurekaServerTotalConnections=")
.append(eurekaServerTotalConnections)
.append(", ")
.append("eurekaServerTotalConnectionsPerHost=")
.append(eurekaServerTotalConnectionsPerHost)
.append(", ")
.append("eurekaServerURLContext='")
.append(eurekaServerURLContext)
.append("', ")
.append("eurekaServerPort='")
.append(eurekaServerPort)
.append("', ")
.append("eurekaServerDNSName='")
.append(eurekaServerDNSName)
.append("', ")
.append("region='")
.append(region)
.append("', ")
.append("eurekaConnectionIdleTimeoutSeconds=")
.append(eurekaConnectionIdleTimeoutSeconds)
.append(", ")
.append("registryRefreshSingleVipAddress='")
.append(registryRefreshSingleVipAddress)
.append("', ")
.append("heartbeatExecutorThreadPoolSize=")
.append(heartbeatExecutorThreadPoolSize)
.append(", ")
.append("heartbeatExecutorExponentialBackOffBound=")
.append(heartbeatExecutorExponentialBackOffBound)
.append(", ")
.append("cacheRefreshExecutorThreadPoolSize=")
.append(cacheRefreshExecutorThreadPoolSize)
.append(", ")
.append("cacheRefreshExecutorExponentialBackOffBound=")
.append(cacheRefreshExecutorExponentialBackOffBound)
.append(", ")
.append("serviceUrl=")
.append(serviceUrl)
.append(", ")
.append("gZipContent=")
.append(gZipContent)
.append(", ")
.append("useDnsForFetchingServiceUrls=")
.append(useDnsForFetchingServiceUrls)
.append(", ")
.append("registerWithEureka=")
.append(registerWithEureka)
.append(", ")
.append("preferSameZoneEureka=")
.append(preferSameZoneEureka)
.append(", ")
.append("logDeltaDiff=")
.append(logDeltaDiff)
.append(", ")
.append("disableDelta=")
.append(disableDelta)
.append(", ")
.append("fetchRemoteRegionsRegistry='")
.append(fetchRemoteRegionsRegistry)
.append("', ")
.append("availabilityZones=")
.append(availabilityZones)
.append(", ")
.append("filterOnlyUpInstances=")
.append(filterOnlyUpInstances)
.append(", ")
.append("fetchRegistry=")
.append(fetchRegistry)
.append(", ")
.append("dollarReplacement='")
.append(dollarReplacement)
.append("', ")
.append("escapeCharReplacement='")
.append(escapeCharReplacement)
.append("', ")
.append("allowRedirects=")
.append(allowRedirects)
.append(", ")
.append("onDemandUpdateStatusChange=")
.append(onDemandUpdateStatusChange)
.append(", ")
.append("encoderName='")
.append(encoderName)
.append("', ")
.append("decoderName='")
.append(decoderName)
.append("', ")
.append("clientDataAccept='")
.append(clientDataAccept)
.append("', ")
.append("shouldUnregisterOnShutdown='")
.append(shouldUnregisterOnShutdown)
.append("shouldEnforceRegistrationAtInit='")
.append(shouldEnforceRegistrationAtInit)
.append("', ")
.append("order='")
.append(order)
.append("'}")
.toString();
return new StringBuilder("EurekaClientConfigBean{").append("propertyResolver=").append(propertyResolver)
.append(", ").append("enabled=").append(enabled).append(", ").append("transport=").append(transport)
.append(", ").append("registryFetchIntervalSeconds=").append(registryFetchIntervalSeconds).append(", ")
.append("instanceInfoReplicationIntervalSeconds=").append(instanceInfoReplicationIntervalSeconds)
.append(", ").append("initialInstanceInfoReplicationIntervalSeconds=")
.append(initialInstanceInfoReplicationIntervalSeconds).append(", ")
.append("eurekaServiceUrlPollIntervalSeconds=").append(eurekaServiceUrlPollIntervalSeconds).append(", ")
.append("proxyPort='").append(proxyPort).append("', ").append("proxyHost='").append(proxyHost)
.append("', ").append("proxyUserName='").append(proxyUserName).append("', ").append("proxyPassword='")
.append(proxyPassword).append("', ").append("eurekaServerReadTimeoutSeconds=")
.append(eurekaServerReadTimeoutSeconds).append(", ").append("eurekaServerConnectTimeoutSeconds=")
.append(eurekaServerConnectTimeoutSeconds).append(", ").append("backupRegistryImpl='")
.append(backupRegistryImpl).append("', ").append("eurekaServerTotalConnections=")
.append(eurekaServerTotalConnections).append(", ").append("eurekaServerTotalConnectionsPerHost=")
.append(eurekaServerTotalConnectionsPerHost).append(", ").append("eurekaServerURLContext='")
.append(eurekaServerURLContext).append("', ").append("eurekaServerPort='").append(eurekaServerPort)
.append("', ").append("eurekaServerDNSName='").append(eurekaServerDNSName).append("', ")
.append("region='").append(region).append("', ").append("eurekaConnectionIdleTimeoutSeconds=")
.append(eurekaConnectionIdleTimeoutSeconds).append(", ").append("registryRefreshSingleVipAddress='")
.append(registryRefreshSingleVipAddress).append("', ").append("heartbeatExecutorThreadPoolSize=")
.append(heartbeatExecutorThreadPoolSize).append(", ")
.append("heartbeatExecutorExponentialBackOffBound=").append(heartbeatExecutorExponentialBackOffBound)
.append(", ").append("cacheRefreshExecutorThreadPoolSize=").append(cacheRefreshExecutorThreadPoolSize)
.append(", ").append("cacheRefreshExecutorExponentialBackOffBound=")
.append(cacheRefreshExecutorExponentialBackOffBound).append(", ").append("serviceUrl=")
.append(serviceUrl).append(", ").append("gZipContent=").append(gZipContent).append(", ")
.append("useDnsForFetchingServiceUrls=").append(useDnsForFetchingServiceUrls).append(", ")
.append("registerWithEureka=").append(registerWithEureka).append(", ").append("preferSameZoneEureka=")
.append(preferSameZoneEureka).append(", ").append("logDeltaDiff=").append(logDeltaDiff).append(", ")
.append("disableDelta=").append(disableDelta).append(", ").append("fetchRemoteRegionsRegistry='")
.append(fetchRemoteRegionsRegistry).append("', ").append("availabilityZones=").append(availabilityZones)
.append(", ").append("filterOnlyUpInstances=").append(filterOnlyUpInstances).append(", ")
.append("fetchRegistry=").append(fetchRegistry).append(", ").append("dollarReplacement='")
.append(dollarReplacement).append("', ").append("escapeCharReplacement='").append(escapeCharReplacement)
.append("', ").append("allowRedirects=").append(allowRedirects).append(", ")
.append("onDemandUpdateStatusChange=").append(onDemandUpdateStatusChange).append(", ")
.append("encoderName='").append(encoderName).append("', ").append("decoderName='").append(decoderName)
.append("', ").append("clientDataAccept='").append(clientDataAccept).append("', ")
.append("shouldUnregisterOnShutdown='").append(shouldUnregisterOnShutdown)
.append("shouldEnforceRegistrationAtInit='").append(shouldEnforceRegistrationAtInit).append("', ")
.append("order='").append(order).append("'}").toString();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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.
@@ -19,7 +19,6 @@ package org.springframework.cloud.netflix.eureka;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.EurekaClient;
@@ -36,7 +35,6 @@ import org.springframework.core.Ordered;
*
* @author Spencer Gibb
* @author Tim Ysewyn
* @author Olga Maciaszek-Sharma
*/
public class EurekaDiscoveryClient implements DiscoveryClient {
@@ -81,7 +79,7 @@ public class EurekaDiscoveryClient implements DiscoveryClient {
if (app.getInstances().isEmpty()) {
continue;
}
names.add(app.getName().toLowerCase(Locale.ROOT));
names.add(app.getName().toLowerCase());
}
return names;
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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.
@@ -21,12 +21,12 @@ import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.EurekaClientConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.SimpleStatusAggregator;
import org.springframework.boot.actuate.health.StatusAggregator;
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.boot.health.actuate.endpoint.SimpleStatusAggregator;
import org.springframework.boot.health.actuate.endpoint.StatusAggregator;
import org.springframework.cloud.client.ConditionalOnBlockingDiscoveryEnabled;
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent;
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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.
@@ -24,22 +24,19 @@ import java.util.Set;
import com.netflix.appinfo.HealthCheckHandler;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.health.actuate.endpoint.StatusAggregator;
import org.springframework.boot.health.contributor.CompositeHealthContributor;
import org.springframework.boot.health.contributor.CompositeReactiveHealthContributor;
import org.springframework.boot.health.contributor.Health;
import org.springframework.boot.health.contributor.HealthContributor;
import org.springframework.boot.health.contributor.HealthContributors;
import org.springframework.boot.health.contributor.HealthIndicator;
import org.springframework.boot.health.contributor.ReactiveHealthContributor;
import org.springframework.boot.health.contributor.ReactiveHealthContributors;
import org.springframework.boot.health.contributor.ReactiveHealthIndicator;
import org.springframework.boot.health.contributor.Status;
import org.springframework.boot.actuate.health.CompositeHealthContributor;
import org.springframework.boot.actuate.health.CompositeReactiveHealthContributor;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthContributor;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.NamedContributor;
import org.springframework.boot.actuate.health.ReactiveHealthContributor;
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.boot.actuate.health.StatusAggregator;
import org.springframework.cloud.client.discovery.health.DiscoveryCompositeHealthContributor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@@ -70,8 +67,6 @@ import org.springframework.util.Assert;
public class EurekaHealthCheckHandler
implements HealthCheckHandler, ApplicationContextAware, InitializingBean, Ordered, Lifecycle {
private static final Log log = LogFactory.getLog(EurekaHealthCheckHandler.class);
private static final Map<Status, InstanceInfo.InstanceStatus> STATUS_MAPPING = new HashMap<>() {
{
put(Status.UNKNOWN, InstanceStatus.UNKNOWN);
@@ -131,11 +126,7 @@ public class EurekaHealthCheckHandler
@Override
public InstanceStatus getStatus(InstanceStatus instanceStatus) {
if (running) {
InstanceStatus status = getHealthStatus();
if (status != instanceStatus) {
log.info("Eureka health status changed to " + status);
}
return status;
return getHealthStatus();
}
else {
// Return nothing if the context is not running, so the status held by the
@@ -151,41 +142,37 @@ public class EurekaHealthCheckHandler
}
protected Status getStatus(StatusAggregator statusAggregator) {
Set<Status> statuses = new HashSet<>();
for (Map.Entry<String, HealthContributor> entry : healthContributors.entrySet()) {
processContributor(statuses, entry.getKey(), entry.getValue());
Set<Status> statusSet = new HashSet<>();
for (HealthContributor contributor : healthContributors.values()) {
processContributor(statusSet, contributor);
}
for (Map.Entry<String, ReactiveHealthContributor> entry : reactiveHealthContributors.entrySet()) {
processContributor(statuses, entry.getKey(), entry.getValue());
for (ReactiveHealthContributor contributor : reactiveHealthContributors.values()) {
processContributor(statusSet, contributor);
}
return statusAggregator.getAggregateStatus(statuses);
return statusAggregator.getAggregateStatus(statusSet);
}
private void processContributor(Set<Status> statuses, String name, HealthContributor contributor) {
private void processContributor(Set<Status> statusSet, HealthContributor contributor) {
if (contributor instanceof CompositeHealthContributor) {
for (HealthContributors.Entry contrib : (CompositeHealthContributor) contributor) {
processContributor(statuses, contrib.name(), contrib.contributor());
for (NamedContributor<HealthContributor> contrib : (CompositeHealthContributor) contributor) {
processContributor(statusSet, contrib.getContributor());
}
}
else if (contributor instanceof HealthIndicator) {
Status status = ((HealthIndicator) contributor).health().getStatus();
log.debug("Health contributor " + name + " has status " + status);
statuses.add(status);
statusSet.add(((HealthIndicator) contributor).health().getStatus());
}
}
private void processContributor(Set<Status> statuses, String name, ReactiveHealthContributor contributor) {
private void processContributor(Set<Status> statusSet, ReactiveHealthContributor contributor) {
if (contributor instanceof CompositeReactiveHealthContributor) {
for (ReactiveHealthContributors.Entry contrib : (CompositeReactiveHealthContributor) contributor) {
processContributor(statuses, contrib.name(), contrib.contributor());
for (NamedContributor<ReactiveHealthContributor> contrib : (CompositeReactiveHealthContributor) contributor) {
processContributor(statusSet, contrib.getContributor());
}
}
else if (contributor instanceof ReactiveHealthIndicator) {
Health health = ((ReactiveHealthIndicator) contributor).health().block();
if (health != null) {
Status status = health.getStatus();
log.debug("Health contributor " + name + " has status " + status);
statuses.add(status);
statusSet.add(health.getStatus());
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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.
@@ -28,9 +28,9 @@ import com.netflix.discovery.shared.Application;
import com.netflix.discovery.shared.Applications;
import org.springframework.aop.support.AopUtils;
import org.springframework.boot.health.contributor.Health;
import org.springframework.boot.health.contributor.Health.Builder;
import org.springframework.boot.health.contributor.Status;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Health.Builder;
import org.springframework.boot.actuate.health.Status;
import org.springframework.cloud.client.discovery.health.DiscoveryHealthIndicator;
import org.springframework.cloud.util.ProxyUtils;
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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,7 +38,6 @@ import org.springframework.util.StringUtils;
* @author Spencer Gibb
* @author Ryan Baxter
* @author Gregor Zurowski
* @author Robert Bleyl
*/
@ConfigurationProperties("eureka.instance")
public class EurekaInstanceConfigBean implements CloudEurekaInstanceConfig, EnvironmentAware {
@@ -111,7 +110,7 @@ public class EurekaInstanceConfigBean implements CloudEurekaInstanceConfig, Envi
* Setting this value too long could mean that the traffic could be routed to the
* instance even though the instance is not alive. Setting this value too small could
* mean, the instance may be taken out of traffic because of temporary network
* glitches.This value to be set to at least higher than the value specified in
* glitches.This value to be set to atleast higher than the value specified in
* leaseRenewalIntervalInSeconds.
*/
private int leaseExpirationDurationInSeconds = 90;
@@ -169,7 +168,7 @@ public class EurekaInstanceConfigBean implements CloudEurekaInstanceConfig, Envi
/**
* Gets the relative status page URL path for this instance. The status page URL is
* then constructed out of the hostName and the type of communication - secure or
* insecure as specified in securePort and nonSecurePort.
* unsecure as specified in securePort and nonSecurePort.
*
* It is normally used for informational purposes for other services to find about the
* status of this instance. Users can provide a simple HTML indicating what is the
@@ -191,7 +190,7 @@ public class EurekaInstanceConfigBean implements CloudEurekaInstanceConfig, Envi
/**
* Gets the relative home page URL Path for this instance. The home page URL is then
* constructed out of the hostName and the type of communication - secure or insecure.
* constructed out of the hostName and the type of communication - secure or unsecure.
*
* It is normally used for informational purposes for other services to use it as a
* landing page.
@@ -213,7 +212,7 @@ public class EurekaInstanceConfigBean implements CloudEurekaInstanceConfig, Envi
/**
* Gets the relative health check URL path for this instance. The health check page
* URL is then constructed out of the hostname and the type of communication - secure
* or insecure as specified in securePort and nonSecurePort.
* or unsecure as specified in securePort and nonSecurePort.
*
* It is normally used for making educated decisions based on the health of the
* instance - for example, it can be used to determine whether to proceed deployments
@@ -270,12 +269,6 @@ public class EurekaInstanceConfigBean implements CloudEurekaInstanceConfig, Envi
*/
private boolean preferIpAddress = false;
/**
* If true the EurekaClient will be initialized asynchronously when the
* InstanceRegistry bean is created.
*/
private boolean asyncClientInitialization;
/**
* Initial status to register with remote Eureka server.
*/
@@ -554,14 +547,6 @@ public class EurekaInstanceConfigBean implements CloudEurekaInstanceConfig, Envi
this.preferIpAddress = preferIpAddress;
}
public boolean isAsyncClientInitialization() {
return asyncClientInitialization;
}
public void setAsyncClientInitialization(boolean asyncClientInitialization) {
this.asyncClientInitialization = asyncClientInitialization;
}
public InstanceStatus getInitialStatus() {
return initialStatus;
}
@@ -612,7 +597,6 @@ public class EurekaInstanceConfigBean implements CloudEurekaInstanceConfig, Envi
&& Objects.equals(namespace, that.namespace) && Objects.equals(hostname, that.hostname)
&& preferIpAddress == that.preferIpAddress && Objects.equals(initialStatus, that.initialStatus)
&& Arrays.equals(defaultAddressResolutionOrder, that.defaultAddressResolutionOrder)
&& asyncClientInitialization == that.asyncClientInitialization
&& Objects.equals(environment, that.environment);
}
@@ -623,109 +607,34 @@ public class EurekaInstanceConfigBean implements CloudEurekaInstanceConfig, Envi
leaseExpirationDurationInSeconds, virtualHostName, instanceId, secureVirtualHostName, aSGName,
metadataMap, dataCenterInfo, ipAddress, statusPageUrlPath, statusPageUrl, homePageUrlPath, homePageUrl,
healthCheckUrlPath, healthCheckUrl, secureHealthCheckUrl, namespace, hostname, preferIpAddress,
asyncClientInitialization, initialStatus, Arrays.hashCode(defaultAddressResolutionOrder), environment);
initialStatus, Arrays.hashCode(defaultAddressResolutionOrder), environment);
}
@Override
public String toString() {
return new StringBuilder("EurekaInstanceConfigBean{").append("hostInfo=")
.append(hostInfo)
.append(", ")
.append("inetUtils=")
.append(inetUtils)
.append(", ")
.append("appname='")
.append(appname)
.append("', ")
.append("appGroupName='")
.append(appGroupName)
.append("', ")
.append("instanceEnabledOnit=")
.append(instanceEnabledOnit)
.append(", ")
.append("nonSecurePort=")
.append(nonSecurePort)
.append(", ")
.append("securePort=")
.append(securePort)
.append(", ")
.append("nonSecurePortEnabled=")
.append(nonSecurePortEnabled)
.append(", ")
.append("securePortEnabled=")
.append(securePortEnabled)
.append(", ")
.append("leaseRenewalIntervalInSeconds=")
.append(leaseRenewalIntervalInSeconds)
.append(", ")
.append("leaseExpirationDurationInSeconds=")
.append(leaseExpirationDurationInSeconds)
.append(", ")
.append("virtualHostName='")
.append(virtualHostName)
.append("', ")
.append("instanceId='")
.append(instanceId)
.append("', ")
.append("secureVirtualHostName='")
.append(secureVirtualHostName)
.append("', ")
.append("aSGName='")
.append(aSGName)
.append("', ")
.append("metadataMap=")
.append(metadataMap)
.append(", ")
.append("dataCenterInfo=")
.append(dataCenterInfo)
.append(", ")
.append("ipAddress='")
.append(ipAddress)
.append("', ")
.append("statusPageUrlPath='")
.append(statusPageUrlPath)
.append("', ")
.append("statusPageUrl='")
.append(statusPageUrl)
.append("', ")
.append("homePageUrlPath='")
.append(homePageUrlPath)
.append("', ")
.append("homePageUrl='")
.append(homePageUrl)
.append("', ")
.append("healthCheckUrlPath='")
.append(healthCheckUrlPath)
.append("', ")
.append("healthCheckUrl='")
.append(healthCheckUrl)
.append("', ")
.append("secureHealthCheckUrl='")
.append(secureHealthCheckUrl)
.append("', ")
.append("namespace='")
.append(namespace)
.append("', ")
.append("hostname='")
.append(hostname)
.append("', ")
.append("asyncClientInitialization=")
.append(asyncClientInitialization)
.append(", ")
.append("preferIpAddress=")
.append(preferIpAddress)
.append(", ")
.append("initialStatus=")
.append(initialStatus)
.append(", ")
.append("defaultAddressResolutionOrder=")
.append(Arrays.toString(defaultAddressResolutionOrder))
.append(", ")
.append("environment=")
.append(environment)
.append(", ")
.append("}")
.toString();
return new StringBuilder("EurekaInstanceConfigBean{").append("hostInfo=").append(hostInfo).append(", ")
.append("inetUtils=").append(inetUtils).append(", ").append("appname='").append(appname).append("', ")
.append("appGroupName='").append(appGroupName).append("', ").append("instanceEnabledOnit=")
.append(instanceEnabledOnit).append(", ").append("nonSecurePort=").append(nonSecurePort).append(", ")
.append("securePort=").append(securePort).append(", ").append("nonSecurePortEnabled=")
.append(nonSecurePortEnabled).append(", ").append("securePortEnabled=").append(securePortEnabled)
.append(", ").append("leaseRenewalIntervalInSeconds=").append(leaseRenewalIntervalInSeconds)
.append(", ").append("leaseExpirationDurationInSeconds=").append(leaseExpirationDurationInSeconds)
.append(", ").append("virtualHostName='").append(virtualHostName).append("', ").append("instanceId='")
.append(instanceId).append("', ").append("secureVirtualHostName='").append(secureVirtualHostName)
.append("', ").append("aSGName='").append(aSGName).append("', ").append("metadataMap=")
.append(metadataMap).append(", ").append("dataCenterInfo=").append(dataCenterInfo).append(", ")
.append("ipAddress='").append(ipAddress).append("', ").append("statusPageUrlPath='")
.append(statusPageUrlPath).append("', ").append("statusPageUrl='").append(statusPageUrl).append("', ")
.append("homePageUrlPath='").append(homePageUrlPath).append("', ").append("homePageUrl='")
.append(homePageUrl).append("', ").append("healthCheckUrlPath='").append(healthCheckUrlPath)
.append("', ").append("healthCheckUrl='").append(healthCheckUrl).append("', ")
.append("secureHealthCheckUrl='").append(secureHealthCheckUrl).append("', ").append("namespace='")
.append(namespace).append("', ").append("hostname='").append(hostname).append("', ")
.append("preferIpAddress=").append(preferIpAddress).append(", ").append("initialStatus=")
.append(initialStatus).append(", ").append("defaultAddressResolutionOrder=")
.append(Arrays.toString(defaultAddressResolutionOrder)).append(", ").append("environment=")
.append(environment).append(", ").append("}").toString();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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,6 +22,7 @@ import java.util.Objects;
import com.netflix.appinfo.InstanceInfo;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.Assert;
@@ -80,7 +81,7 @@ public class EurekaServiceInstance implements ServiceInstance {
@Override
public URI getUri() {
return ServiceInstance.createUri(this);
return DefaultServiceInstance.getUri(this);
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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.
@@ -35,8 +35,8 @@ public class InstanceInfoFactory {
public InstanceInfo create(EurekaInstanceConfig config) {
LeaseInfo.Builder leaseInfoBuilder = LeaseInfo.Builder.newBuilder()
.setRenewalIntervalInSecs(config.getLeaseRenewalIntervalInSeconds())
.setDurationInSecs(config.getLeaseExpirationDurationInSeconds());
.setRenewalIntervalInSecs(config.getLeaseRenewalIntervalInSeconds())
.setDurationInSecs(config.getLeaseExpirationDurationInSeconds());
// Builder the instance information to be registered with eureka
// server
@@ -46,24 +46,19 @@ public class InstanceInfoFactory {
if (!namespace.endsWith(".")) {
namespace = namespace + ".";
}
builder.setNamespace(namespace)
.setAppName(config.getAppname())
.setInstanceId(config.getInstanceId())
.setAppGroupName(config.getAppGroupName())
.setDataCenterInfo(config.getDataCenterInfo())
.setIPAddr(config.getIpAddress())
.setHostName(config.getHostName(false))
.setPort(config.getNonSecurePort())
.enablePort(InstanceInfo.PortType.UNSECURE, config.isNonSecurePortEnabled())
.setSecurePort(config.getSecurePort())
.enablePort(InstanceInfo.PortType.SECURE, config.getSecurePortEnabled())
.setVIPAddress(config.getVirtualHostName())
.setSecureVIPAddress(config.getSecureVirtualHostName())
.setHomePageUrl(config.getHomePageUrlPath(), config.getHomePageUrl())
.setStatusPageUrl(config.getStatusPageUrlPath(), config.getStatusPageUrl())
.setHealthCheckUrls(config.getHealthCheckUrlPath(), config.getHealthCheckUrl(),
config.getSecureHealthCheckUrl())
.setASGName(config.getASGName());
builder.setNamespace(namespace).setAppName(config.getAppname()).setInstanceId(config.getInstanceId())
.setAppGroupName(config.getAppGroupName()).setDataCenterInfo(config.getDataCenterInfo())
.setIPAddr(config.getIpAddress()).setHostName(config.getHostName(false))
.setPort(config.getNonSecurePort())
.enablePort(InstanceInfo.PortType.UNSECURE, config.isNonSecurePortEnabled())
.setSecurePort(config.getSecurePort())
.enablePort(InstanceInfo.PortType.SECURE, config.getSecurePortEnabled())
.setVIPAddress(config.getVirtualHostName()).setSecureVIPAddress(config.getSecureVirtualHostName())
.setHomePageUrl(config.getHomePageUrlPath(), config.getHomePageUrl())
.setStatusPageUrl(config.getStatusPageUrlPath(), config.getStatusPageUrl())
.setHealthCheckUrls(config.getHealthCheckUrlPath(), config.getHealthCheckUrl(),
config.getSecureHealthCheckUrl())
.setASGName(config.getASGName());
// Start off with the STARTING state to avoid traffic
if (!config.isInstanceEnabledOnit()) {
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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.
@@ -18,34 +18,32 @@ package org.springframework.cloud.netflix.eureka;
import java.util.Objects;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.core5.http.io.SocketConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.web.client.RestClient;
import org.springframework.cloud.netflix.eureka.http.RestTemplateEurekaHttpClient;
import org.springframework.web.client.RestTemplate;
/**
* Properties for configuring timeouts used in {@link RestClient} required by
* {@link EurekaHttpClient}.
* Properties for configuring timeouts used in {@link RestTemplate} required by
* {@link RestTemplateEurekaHttpClient}.
*
* @author Jiwon Jeon
* @author Mooyong Lee
* @author Olga Maciaszek-Sharma
* @since 4.2.0
* @since 3.1.6
*/
@ConfigurationProperties("eureka.client.timeout")
public class TimeoutProperties {
@ConfigurationProperties("eureka.client.rest-template-timeout")
public class RestTemplateTimeoutProperties {
/**
* Default values are set to 180000, in keeping with {@link RequestConfig} and
* {@link SocketConfig} defaults.
*/
protected int connectTimeout = 180000; // 3 * MINUTES
private int connectTimeout = 3 * 60 * 1000;
protected int connectRequestTimeout = 180000; // 3 * MINUTES
private int connectRequestTimeout = 3 * 60 * 1000;
protected int socketTimeout = 180000; // 3 * MINUTES
private int socketTimeout = 3 * 60 * 1000;
public int getConnectTimeout() {
return connectTimeout;
@@ -80,7 +78,7 @@ public class TimeoutProperties {
return false;
}
TimeoutProperties that = (TimeoutProperties) o;
RestTemplateTimeoutProperties that = (RestTemplateTimeoutProperties) o;
return connectTimeout == that.connectTimeout && connectRequestTimeout == that.connectRequestTimeout
&& socketTimeout == that.socketTimeout;
@@ -91,4 +89,10 @@ public class TimeoutProperties {
return Objects.hash(connectTimeout, connectRequestTimeout, socketTimeout);
}
@Override
public String toString() {
return "RestTemplateTimeoutProperties{" + ", connectTimeout=" + connectTimeout + ", connectRequestTimeout="
+ connectRequestTimeout + ", socketTimeout=" + socketTimeout + '}';
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2017-present the original author or authors.
* Copyright 2017-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.
@@ -18,7 +18,6 @@ package org.springframework.cloud.netflix.eureka.config;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Set;
import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
import com.netflix.discovery.shared.transport.jersey.TransportClientFactories;
@@ -26,8 +25,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -38,30 +36,25 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.configuration.SSLContextFactory;
import org.springframework.cloud.configuration.TlsProperties;
import org.springframework.cloud.netflix.eureka.TimeoutProperties;
import org.springframework.cloud.netflix.eureka.RestTemplateTimeoutProperties;
import org.springframework.cloud.netflix.eureka.http.DefaultEurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.EurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.RestClientDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.http.RestClientTransportClientFactories;
import org.springframework.cloud.netflix.eureka.http.RestTemplateDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.http.RestTemplateTransportClientFactories;
import org.springframework.cloud.netflix.eureka.http.WebClientDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.http.WebClientTransportClientFactories;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
/**
* @author Daniel Lavoie
* @author Armin Krezovic
* @author Olga Maciaszek-Sharma
* @author Wonchul Heo
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({ TimeoutProperties.class })
@EnableConfigurationProperties(RestTemplateTimeoutProperties.class)
public class DiscoveryClientOptionalArgsConfiguration {
protected static final Log LOG = LogFactory.getLog(DiscoveryClientOptionalArgsConfiguration.class);
protected static final Log logger = LogFactory.getLog(DiscoveryClientOptionalArgsConfiguration.class);
@Bean
@ConfigurationProperties("eureka.client.tls")
@@ -69,8 +62,42 @@ public class DiscoveryClientOptionalArgsConfiguration {
return new TlsProperties();
}
// Visible for tests
public static void setupTLS(AbstractDiscoveryClientOptionalArgs<?> args, TlsProperties properties)
@Bean
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
@ConditionalOnMissingClass("org.glassfish.jersey.client.JerseyClient")
@ConditionalOnMissingBean(value = { AbstractDiscoveryClientOptionalArgs.class }, search = SearchStrategy.CURRENT)
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
havingValue = "false")
public RestTemplateDiscoveryClientOptionalArgs restTemplateDiscoveryClientOptionalArgs(TlsProperties tlsProperties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier)
throws GeneralSecurityException, IOException {
logger.info("Eureka HTTP Client uses RestTemplate.");
RestTemplateDiscoveryClientOptionalArgs result = new RestTemplateDiscoveryClientOptionalArgs(
eurekaClientHttpRequestFactorySupplier);
setupTLS(result, tlsProperties);
return result;
}
@Bean
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
@ConditionalOnMissingClass("org.glassfish.jersey.client.JerseyClient")
@ConditionalOnMissingBean(value = { TransportClientFactories.class }, search = SearchStrategy.CURRENT)
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
havingValue = "false")
public RestTemplateTransportClientFactories restTemplateTransportClientFactories(
RestTemplateDiscoveryClientOptionalArgs optionalArgs) {
return new RestTemplateTransportClientFactories(optionalArgs);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier(
RestTemplateTimeoutProperties restTemplateTimeoutProperties) {
return new DefaultEurekaClientHttpRequestFactorySupplier(restTemplateTimeoutProperties);
}
private static void setupTLS(AbstractDiscoveryClientOptionalArgs<?> args, TlsProperties properties)
throws GeneralSecurityException, IOException {
if (properties.isEnabled()) {
SSLContextFactory factory = new SSLContextFactory(properties);
@@ -79,35 +106,33 @@ public class DiscoveryClientOptionalArgsConfiguration {
}
@Configuration(proxyBeanMethods = false)
@Conditional(OnJerseyClientPresentAndEnabledCondition.class)
@ConditionalOnClass(name = "org.glassfish.jersey.client.JerseyClient")
@ConditionalOnBean(value = AbstractDiscoveryClientOptionalArgs.class, search = SearchStrategy.CURRENT)
static class DiscoveryClientOptionalArgsTlsConfiguration {
DiscoveryClientOptionalArgsTlsConfiguration(TlsProperties tlsProperties,
AbstractDiscoveryClientOptionalArgs optionalArgs) throws GeneralSecurityException, IOException {
if (LOG.isInfoEnabled()) {
LOG.info("Eureka HTTP Client uses Jersey");
}
logger.info("Eureka HTTP Client uses Jersey");
setupTLS(optionalArgs, tlsProperties);
}
}
@Configuration(proxyBeanMethods = false)
@Conditional(OnJerseyClientNotPresentOrNotEnabledCondition.class)
@ConditionalOnMissingClass("org.glassfish.jersey.client.JerseyClient")
@ConditionalOnClass(name = "org.springframework.web.reactive.function.client.WebClient")
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", havingValue = "true")
protected static class WebClientConfiguration {
@Autowired
private TlsProperties tlsProperties;
@Bean
@ConditionalOnMissingBean(
value = { AbstractDiscoveryClientOptionalArgs.class, RestClientDiscoveryClientOptionalArgs.class },
value = { AbstractDiscoveryClientOptionalArgs.class, RestTemplateDiscoveryClientOptionalArgs.class },
search = SearchStrategy.CURRENT)
public WebClientDiscoveryClientOptionalArgs webClientDiscoveryClientOptionalArgs(TlsProperties tlsProperties,
public WebClientDiscoveryClientOptionalArgs webClientDiscoveryClientOptionalArgs(
ObjectProvider<WebClient.Builder> builder) throws GeneralSecurityException, IOException {
if (LOG.isInfoEnabled()) {
LOG.info("Eureka HTTP Client uses WebClient.");
}
logger.info("Eureka HTTP Client uses WebClient.");
WebClientDiscoveryClientOptionalArgs result = new WebClientDiscoveryClientOptionalArgs(
builder::getIfAvailable);
setupTLS(result, tlsProperties);
@@ -123,114 +148,16 @@ public class DiscoveryClientOptionalArgsConfiguration {
}
@Configuration(proxyBeanMethods = false)
@Conditional(OnJerseyClientNotPresentOrNotEnabledCondition.class)
@ConditionalOnMissingClass("org.springframework.web.reactive.function.client.WebClient")
@Configuration
@ConditionalOnMissingClass({ "org.glassfish.jersey.client.JerseyClient",
"org.springframework.web.reactive.function.client.WebClient" })
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", havingValue = "true")
protected static class WebClientNotFoundConfiguration {
public WebClientNotFoundConfiguration() {
throw new IllegalStateException("eureka.client.webclient.enabled is true, "
+ "but WebClient is not on the classpath. Please add " + "spring-boot-webclient as a dependency.");
}
}
@Configuration(proxyBeanMethods = false)
@Conditional(OnRestClientPresentAndEnabledCondition.class)
protected static class RestClientConfiguration {
@Bean
@ConditionalOnMissingBean
EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier(
TimeoutProperties restClientTimeoutProperties,
Set<EurekaClientHttpRequestFactorySupplier.RequestConfigCustomizer> requestConfigCustomizers) {
return new DefaultEurekaClientHttpRequestFactorySupplier(restClientTimeoutProperties,
requestConfigCustomizers);
}
@Bean
@ConditionalOnMissingBean(value = { AbstractDiscoveryClientOptionalArgs.class },
search = SearchStrategy.CURRENT)
public RestClientDiscoveryClientOptionalArgs restClientDiscoveryClientOptionalArgs(TlsProperties tlsProperties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier,
ObjectProvider<RestClient.Builder> restClientBuilderProvider)
throws GeneralSecurityException, IOException {
if (LOG.isInfoEnabled()) {
LOG.info("Eureka HTTP Client uses RestClient.");
}
RestClientDiscoveryClientOptionalArgs result = new RestClientDiscoveryClientOptionalArgs(
eurekaClientHttpRequestFactorySupplier,
() -> restClientBuilderProvider.getIfAvailable(RestClient::builder));
setupTLS(result, tlsProperties);
return result;
}
@Bean
@ConditionalOnMissingBean(value = TransportClientFactories.class, search = SearchStrategy.CURRENT)
public RestClientTransportClientFactories restClientTransportClientFactories(
RestClientDiscoveryClientOptionalArgs args) {
return new RestClientTransportClientFactories(args);
}
}
static class OnJerseyClientPresentAndEnabledCondition extends AllNestedConditions {
OnJerseyClientPresentAndEnabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnClass(name = "org.glassfish.jersey.client.JerseyClient")
static class OnJerseyClientPresent {
}
@ConditionalOnProperty(value = "eureka.client.jersey.enabled", matchIfMissing = true)
static class OnJerseyClientEnabled {
}
}
static class OnJerseyClientNotPresentOrNotEnabledCondition extends AnyNestedCondition {
OnJerseyClientNotPresentOrNotEnabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnMissingClass("org.glassfish.jersey.client.JerseyClient")
static class OnJerseyClientMissing {
}
@ConditionalOnProperty(value = "eureka.client.jersey.enabled", havingValue = "false")
static class OnJerseyClientDisabled {
}
}
static class OnRestClientPresentAndEnabledCondition extends AllNestedConditions {
OnRestClientPresentAndEnabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnClass(name = "org.springframework.web.client.RestClient")
static class OnRestClientPresent {
}
@Conditional(OnJerseyClientNotPresentOrNotEnabledCondition.class)
static class OnJerseyClientNotPresentOrNotEnabled {
}
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
havingValue = "false")
static class OnWebClientDisabled {
throw new IllegalStateException(
"eureka.client.webclient.enabled is true, " + "but WebClient is not on the classpath. Please add "
+ "spring-boot-starter-webflux as a dependency.");
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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,8 +16,6 @@
package org.springframework.cloud.netflix.eureka.config;
import java.util.Set;
import com.netflix.discovery.EurekaClientConfig;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
@@ -28,18 +26,17 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.http.codec.autoconfigure.CodecsAutoConfiguration;
import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
import org.springframework.cloud.config.client.ConfigServerInstanceProvider;
import org.springframework.cloud.config.client.ConfigServicePropertySourceLocator;
import org.springframework.cloud.configuration.TlsProperties;
import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
import org.springframework.cloud.netflix.eureka.TimeoutProperties;
import org.springframework.cloud.netflix.eureka.http.DefaultEurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.EurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.RestClientEurekaHttpClient;
import org.springframework.cloud.netflix.eureka.http.RestClientTransportClientFactory;
import org.springframework.cloud.netflix.eureka.http.RestTemplateEurekaHttpClient;
import org.springframework.cloud.netflix.eureka.http.RestTemplateTransportClientFactory;
import org.springframework.cloud.netflix.eureka.http.WebClientEurekaHttpClient;
import org.springframework.cloud.netflix.eureka.http.WebClientTransportClientFactory;
import org.springframework.context.annotation.Bean;
@@ -47,22 +44,18 @@ import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.lang.Nullable;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Bootstrap configuration for config client that wants to look the config server up via
* Bootstrap configuration for config client that wants to lookup the config server via
* discovery.
*
* @author Dave Syer
* @author Armin Krezovic
* @author Wonchul Heo
* @author Olga Maciaszek-Sharma
*/
@ConditionalOnClass(ConfigServicePropertySourceLocator.class)
@Conditional(EurekaConfigServerBootstrapConfiguration.EurekaConfigServerBootstrapCondition.class)
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({ TimeoutProperties.class })
@EnableConfigurationProperties
public class EurekaConfigServerBootstrapConfiguration {
@Bean
@@ -71,6 +64,24 @@ public class EurekaConfigServerBootstrapConfiguration {
return new EurekaClientConfigBean();
}
@Bean
@ConditionalOnMissingBean(EurekaHttpClient.class)
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
havingValue = "false")
public RestTemplateEurekaHttpClient configDiscoveryRestTemplateEurekaHttpClient(EurekaClientConfigBean config,
Environment env, @Nullable TlsProperties properties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier) {
return (RestTemplateEurekaHttpClient) new RestTemplateTransportClientFactory(properties,
eurekaClientHttpRequestFactorySupplier)
.newClient(HostnameBasedUrlRandomizer.randomEndpoint(config, env));
}
@Bean
@ConditionalOnMissingBean
EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier() {
return new DefaultEurekaClientHttpRequestFactorySupplier();
}
@Bean
public ConfigServerInstanceProvider.Function eurekaConfigServerInstanceProvider(EurekaHttpClient client,
EurekaClientConfig config) {
@@ -78,7 +89,7 @@ public class EurekaConfigServerBootstrapConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ WebClient.class, WebClientAutoConfiguration.class, CodecsAutoConfiguration.class })
@ConditionalOnClass(name = "org.springframework.web.reactive.function.client.WebClient")
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", havingValue = "true")
@ImportAutoConfiguration({ CodecsAutoConfiguration.class, WebClientAutoConfiguration.class })
protected static class WebClientConfiguration {
@@ -88,52 +99,7 @@ public class EurekaConfigServerBootstrapConfiguration {
public WebClientEurekaHttpClient configDiscoveryWebClientEurekaHttpClient(EurekaClientConfigBean config,
ObjectProvider<WebClient.Builder> builder, Environment env) {
return (WebClientEurekaHttpClient) new WebClientTransportClientFactory(builder::getIfAvailable)
.newClient(HostnameBasedUrlRandomizer.randomEndpoint(config, env));
}
}
@Configuration(proxyBeanMethods = false)
@Conditional(RestClientConfiguration.OnRestClientPresentAndEnabledCondition.class)
static class RestClientConfiguration {
@Bean
@ConditionalOnMissingBean(EurekaHttpClient.class)
public RestClientEurekaHttpClient configDiscoveryRestClientEurekaHttpClient(EurekaClientConfigBean config,
@Nullable TlsProperties properties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier,
ObjectProvider<RestClient.Builder> restClientBuilderProvider, Environment env) {
return (RestClientEurekaHttpClient) new RestClientTransportClientFactory(properties,
eurekaClientHttpRequestFactorySupplier,
() -> restClientBuilderProvider.getIfAvailable(RestClient::builder))
.newClient(HostnameBasedUrlRandomizer.randomEndpoint(config, env));
}
@Bean
@ConditionalOnMissingBean
EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier(
TimeoutProperties timeoutProperties,
Set<EurekaClientHttpRequestFactorySupplier.RequestConfigCustomizer> requestConfigCustomizers) {
return new DefaultEurekaClientHttpRequestFactorySupplier(timeoutProperties, requestConfigCustomizers);
}
static class OnRestClientPresentAndEnabledCondition extends AllNestedConditions {
OnRestClientPresentAndEnabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnClass(name = "org.springframework.web.client.RestClient")
static class OnRestClientPresentCondition {
}
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
havingValue = "false")
static class OnWebClientDisabled {
}
.newClient(HostnameBasedUrlRandomizer.randomEndpoint(config, env));
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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,9 +20,9 @@ import java.util.Collections;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import org.springframework.boot.bootstrap.BootstrapContext;
import org.springframework.boot.bootstrap.BootstrapRegistry;
import org.springframework.boot.bootstrap.BootstrapRegistryInitializer;
import org.springframework.boot.BootstrapContext;
import org.springframework.boot.BootstrapRegistry;
import org.springframework.boot.BootstrapRegistryInitializer;
import org.springframework.boot.context.properties.bind.BindHandler;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.cloud.config.client.ConfigClientProperties;
@@ -30,10 +30,9 @@ import org.springframework.cloud.config.client.ConfigServerConfigDataLocationRes
import org.springframework.cloud.config.client.ConfigServerInstanceProvider;
import org.springframework.cloud.configuration.TlsProperties;
import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
import org.springframework.cloud.netflix.eureka.TimeoutProperties;
import org.springframework.cloud.netflix.eureka.http.DefaultEurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.EurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.RestClientTransportClientFactory;
import org.springframework.cloud.netflix.eureka.http.RestTemplateTransportClientFactory;
import org.springframework.util.ClassUtils;
public class EurekaConfigServerBootstrapper implements BootstrapRegistryInitializer {
@@ -58,12 +57,11 @@ public class EurekaConfigServerBootstrapper implements BootstrapRegistryInitiali
return (id) -> Collections.emptyList();
}
EurekaClientConfigBean config = context.get(EurekaClientConfigBean.class);
EurekaHttpClient httpClient = new RestClientTransportClientFactory(
EurekaHttpClient httpClient = new RestTemplateTransportClientFactory(
context.getOrElse(TlsProperties.class, null),
context.getOrElse(EurekaClientHttpRequestFactorySupplier.class,
new DefaultEurekaClientHttpRequestFactorySupplier(new TimeoutProperties(),
Collections.emptySet())))
.newClient(HostnameBasedUrlRandomizer.randomEndpoint(config, getPropertyResolver(context)));
new DefaultEurekaClientHttpRequestFactorySupplier())).newClient(
HostnameBasedUrlRandomizer.randomEndpoint(config, getPropertyResolver(context)));
return new EurekaConfigServerInstanceProvider(httpClient, config)::getInstances;
});
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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.
@@ -31,9 +31,6 @@ import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.netflix.eureka.EurekaServiceInstance;
import org.springframework.http.HttpStatus;
/**
* @author Tang Xiong
*/
public class EurekaConfigServerInstanceProvider {
private final Log log;
@@ -56,11 +53,7 @@ public class EurekaConfigServerInstanceProvider {
if (log.isDebugEnabled()) {
log.debug("eurekaConfigServerInstanceProvider finding instances for " + serviceId);
}
String remoteRegionsStr = config.fetchRegistryForRemoteRegions();
String[] remoteRegions = remoteRegionsStr == null ? null : remoteRegionsStr.split(",");
EurekaHttpResponse<Applications> response = config.getRegistryRefreshSingleVipAddress() == null
? client.getApplications(remoteRegions)
: client.getVip(config.getRegistryRefreshSingleVipAddress(), remoteRegions);
EurekaHttpResponse<Applications> response = client.getApplications(config.getRegion());
List<ServiceInstance> instances = new ArrayList<>();
if (!isSuccessful(response) || response.getEntity() == null) {
return instances;
@@ -1,5 +1,5 @@
/*
* Copyright 2017-present the original author or authors.
* Copyright 2017-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.
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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,13 +16,11 @@
package org.springframework.cloud.netflix.eureka.http;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
@@ -33,8 +31,7 @@ import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder;
import org.apache.hc.core5.http.io.SocketConfig;
import org.apache.hc.core5.util.Timeout;
import org.springframework.cloud.netflix.eureka.TimeoutProperties;
import org.springframework.cloud.netflix.eureka.http.EurekaClientHttpRequestFactorySupplier.RequestConfigCustomizer;
import org.springframework.cloud.netflix.eureka.RestTemplateTimeoutProperties;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.lang.Nullable;
@@ -43,13 +40,6 @@ import org.springframework.lang.Nullable;
* Supplier for the {@link ClientHttpRequestFactory} to be used by Eureka client that uses
* {@link HttpClients}.
*
* <p>
* This supplier is intentionally stateless: each call to {@link #get} builds a fresh
* {@link CloseableHttpClient}. Caching and lifecycle management of the shared client is
* the responsibility of the owning {@code TransportClientFactory}
* ({@link RestClientTransportClientFactory}), which is already created once per Eureka
* client and is the natural owner of that client's lifecycle.
*
* @author Marcin Grzejszczak
* @author Olga Maciaszek-Sharma
* @author Jiwon Jeon
@@ -57,37 +47,44 @@ import org.springframework.lang.Nullable;
*/
public class DefaultEurekaClientHttpRequestFactorySupplier implements EurekaClientHttpRequestFactorySupplier {
private final TimeoutProperties timeoutProperties;
private final RestTemplateTimeoutProperties restTemplateTimeoutProperties;
private final Set<RequestConfigCustomizer> requestConfigCustomizers;
/**
* @deprecated in favour of
* {@link DefaultEurekaClientHttpRequestFactorySupplier#DefaultEurekaClientHttpRequestFactorySupplier(RestTemplateTimeoutProperties)}
*/
@Deprecated
public DefaultEurekaClientHttpRequestFactorySupplier() {
this.restTemplateTimeoutProperties = new RestTemplateTimeoutProperties();
}
public DefaultEurekaClientHttpRequestFactorySupplier(TimeoutProperties timeoutProperties,
Set<RequestConfigCustomizer> requestConfigCustomizers) {
this.timeoutProperties = timeoutProperties;
this.requestConfigCustomizers = requestConfigCustomizers;
public DefaultEurekaClientHttpRequestFactorySupplier(RestTemplateTimeoutProperties restTemplateTimeoutProperties) {
this.restTemplateTimeoutProperties = restTemplateTimeoutProperties;
}
@Override
public ClientHttpRequestFactory get(SSLContext sslContext, @Nullable HostnameVerifier hostnameVerifier) {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
if (sslContext != null || hostnameVerifier != null || timeoutProperties != null) {
httpClientBuilder
.setConnectionManager(buildConnectionManager(sslContext, hostnameVerifier, timeoutProperties));
if (sslContext != null || hostnameVerifier != null || restTemplateTimeoutProperties != null) {
httpClientBuilder.setConnectionManager(
buildConnectionManager(sslContext, hostnameVerifier, restTemplateTimeoutProperties));
}
if (restTemplateTimeoutProperties != null) {
httpClientBuilder.setDefaultRequestConfig(buildRequestConfig());
}
httpClientBuilder.setDefaultRequestConfig(buildRequestConfig());
CloseableHttpClient httpClient = httpClientBuilder.build();
CloseableHttpClient httpClient = httpClientBuilder.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
return requestFactory;
}
private HttpClientConnectionManager buildConnectionManager(SSLContext sslContext, HostnameVerifier hostnameVerifier,
TimeoutProperties timeoutProperties) {
RestTemplateTimeoutProperties restTemplateTimeoutProperties) {
PoolingHttpClientConnectionManagerBuilder connectionManagerBuilder = PoolingHttpClientConnectionManagerBuilder
.create();
.create();
SSLConnectionSocketFactoryBuilder sslConnectionSocketFactoryBuilder = SSLConnectionSocketFactoryBuilder
.create();
.create();
if (sslContext != null) {
sslConnectionSocketFactoryBuilder.setSslContext(sslContext);
}
@@ -95,25 +92,20 @@ public class DefaultEurekaClientHttpRequestFactorySupplier implements EurekaClie
sslConnectionSocketFactoryBuilder.setHostnameVerifier(hostnameVerifier);
}
connectionManagerBuilder.setSSLSocketFactory(sslConnectionSocketFactoryBuilder.build());
if (timeoutProperties != null) {
if (restTemplateTimeoutProperties != null) {
connectionManagerBuilder.setDefaultSocketConfig(SocketConfig.custom()
.setSoTimeout(Timeout.of(timeoutProperties.getSocketTimeout(), TimeUnit.MILLISECONDS))
.build());
connectionManagerBuilder.setDefaultConnectionConfig(ConnectionConfig.custom()
.setConnectTimeout(Timeout.of(timeoutProperties.getConnectTimeout(), TimeUnit.MILLISECONDS))
.build());
.setSoTimeout(Timeout.of(restTemplateTimeoutProperties.getSocketTimeout(), TimeUnit.MILLISECONDS))
.build());
}
return connectionManagerBuilder.build();
}
private RequestConfig buildRequestConfig() {
RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
if (timeoutProperties != null) {
requestConfigBuilder.setConnectionRequestTimeout(
Timeout.of(timeoutProperties.getConnectRequestTimeout(), TimeUnit.MILLISECONDS));
}
requestConfigCustomizers.forEach(customizer -> customizer.customize(requestConfigBuilder));
return requestConfigBuilder.build();
return RequestConfig.custom()
.setConnectTimeout(Timeout.of(restTemplateTimeoutProperties.getConnectTimeout(), TimeUnit.MILLISECONDS))
.setConnectionRequestTimeout(
Timeout.of(restTemplateTimeoutProperties.getConnectRequestTimeout(), TimeUnit.MILLISECONDS))
.build();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2017-present the original author or authors.
* Copyright 2017-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.
@@ -24,8 +24,8 @@ import com.netflix.discovery.shared.Application;
import com.netflix.discovery.shared.Applications;
/**
* A simple wrapper class for {@link Applications} that insure proper Jackson
* serialization through the JsonProperties overwrites.
* A simple wrapper class for {@link Applications} that insure proprer Jackson
* serialization through the JsonPropert overwrites.
*
* @author Daniel Lavoie
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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.
@@ -19,8 +19,6 @@ package org.springframework.cloud.netflix.eureka.http;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import org.apache.hc.client5.http.config.RequestConfig;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.lang.Nullable;
@@ -40,17 +38,4 @@ public interface EurekaClientHttpRequestFactorySupplier {
*/
ClientHttpRequestFactory get(SSLContext sslContext, @Nullable HostnameVerifier hostnameVerifier);
/**
* Allows customising the {@link RequestConfig} of the underlying Apache HC5 instance.
*
* @author Olga Maciaszek-Sharma
* @since 4.2.1
*/
@FunctionalInterface
interface RequestConfigCustomizer {
void customize(RequestConfig.Builder builder);
}
}
@@ -1,147 +0,0 @@
/*
* Copyright 2017-present 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.netflix.eureka.http;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Optional;
import javax.net.ssl.SSLContext;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.converters.jackson.mixin.ApplicationsJsonMixIn;
import com.netflix.discovery.converters.jackson.mixin.InstanceInfoJsonMixIn;
import com.netflix.discovery.converters.jackson.serializer.InstanceInfoJsonBeanSerializer;
import com.netflix.discovery.shared.Applications;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import org.springframework.cloud.configuration.SSLContextFactory;
import org.springframework.cloud.configuration.TlsProperties;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.lang.Nullable;
/**
* Utility class for dealing with {@link EurekaHttpClient}.
*
* @author Daniel Lavoie
* @author Wonchul Heo
* @author Olga Maciaszek-Sharma
* @since 4.2.0
*/
final class EurekaHttpClientUtils {
private EurekaHttpClientUtils() {
throw new AssertionError("Must not instantiate constant utility class");
}
/**
* Provides the serialization configurations required by the Eureka Server. JSON
* content exchanged with eureka requires a root node matching the entity being
* serialized or deserialized. Achieved with
* {@link SerializationFeature#WRAP_ROOT_VALUE} and
* {@link DeserializationFeature#UNWRAP_ROOT_VALUE}.
* {@link PropertyNamingStrategies.SnakeCaseStrategy} is applied to the underlying
* {@link ObjectMapper}.
* @return a {@link MappingJackson2HttpMessageConverter} object
*/
static MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter() {
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper());
return converter;
}
/**
* Provides the serialization configurations required by the Eureka Server. JSON
* content exchanged with eureka requires a root node matching the entity being
* serialized or deserialized. Achieved with
* {@link SerializationFeature#WRAP_ROOT_VALUE} and
* {@link DeserializationFeature#UNWRAP_ROOT_VALUE}.
* {@link PropertyNamingStrategies.SnakeCaseStrategy} is applied to the underlying
* {@link ObjectMapper}.
* @return a {@link ObjectMapper} object
*/
static ObjectMapper objectMapper() {
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
final SimpleModule jsonModule = new SimpleModule();
jsonModule.setSerializerModifier(createJsonSerializerModifier());
objectMapper.registerModule(jsonModule);
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
objectMapper.addMixIn(Applications.class, ApplicationsJsonMixIn.class);
objectMapper.addMixIn(InstanceInfo.class, InstanceInfoJsonMixIn.class);
return objectMapper;
}
private static BeanSerializerModifier createJsonSerializerModifier() {
return new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc,
JsonSerializer<?> serializer) {
if (beanDesc.getBeanClass().isAssignableFrom(InstanceInfo.class)) {
return new InstanceInfoJsonBeanSerializer((BeanSerializerBase) serializer, false);
}
return serializer;
}
};
}
@Nullable
static UserInfo extractUserInfo(String serviceUrl) {
try {
final URI serviceURI = new URI(serviceUrl);
if (serviceURI.getUserInfo() != null) {
final String[] credentials = serviceURI.getUserInfo().split(":");
if (credentials.length == 2) {
return new UserInfo(credentials[0], credentials[1]);
}
}
}
catch (URISyntaxException ignore) {
}
return null;
}
static Optional<SSLContext> context(TlsProperties properties) {
if (properties == null || !properties.isEnabled()) {
return Optional.empty();
}
try {
return Optional.of(new SSLContextFactory(properties).createSSLContext());
}
catch (Exception e) {
throw new IllegalStateException(e);
}
}
record UserInfo(String username, String password) {
}
}
@@ -1,67 +0,0 @@
/*
* Copyright 2017-present 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.netflix.eureka.http;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.client.ClientHttpResponse;
/**
* Response that ignores body, specifically for 404 errors.
*
* @author Spencer Gibb
* @author Wonchul Heo
* @since 4.2.0
*/
class NotFoundHttpResponse implements ClientHttpResponse {
private final ClientHttpResponse response;
NotFoundHttpResponse(ClientHttpResponse response) {
this.response = response;
}
@Override
public HttpStatusCode getStatusCode() throws IOException {
return response.getStatusCode();
}
@Override
public String getStatusText() throws IOException {
return response.getStatusText();
}
@Override
public void close() {
response.close();
}
@Override
public InputStream getBody() throws IOException {
// ignore body on 404 for heartbeat, see gh-4145
return null;
}
@Override
public HttpHeaders getHeaders() {
return response.getHeaders();
}
}
@@ -1,55 +0,0 @@
/*
* Copyright 2017-present 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.netflix.eureka.http;
import java.util.function.Supplier;
import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
import jakarta.ws.rs.client.ClientRequestFilter;
import org.springframework.web.client.RestClient;
/**
* {@link RestClient} implementation of DiscoveryClientOptionalArgs that supports
* supplying {@link ClientRequestFilter}.
*
* @author Wonchul Heo
* @author Olga Maciaszek-Sharma
* @since 4.2.0
*/
public class RestClientDiscoveryClientOptionalArgs extends AbstractDiscoveryClientOptionalArgs<Void> {
private final EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier;
private final Supplier<RestClient.Builder> restClientBuilderSupplier;
public RestClientDiscoveryClientOptionalArgs(
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier,
Supplier<RestClient.Builder> restClientBuilderSupplier) {
this.eurekaClientHttpRequestFactorySupplier = eurekaClientHttpRequestFactorySupplier;
this.restClientBuilderSupplier = restClientBuilderSupplier;
}
EurekaClientHttpRequestFactorySupplier getEurekaClientHttpRequestFactorySupplier() {
return eurekaClientHttpRequestFactorySupplier;
}
Supplier<RestClient.Builder> getRestClientBuilderSupplier() {
return restClientBuilderSupplier;
}
}
@@ -1,238 +0,0 @@
/*
* Copyright 2017-present 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.netflix.eureka.http;
import java.net.URI;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
import com.netflix.discovery.shared.Application;
import com.netflix.discovery.shared.Applications;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import com.netflix.discovery.shared.transport.EurekaHttpResponse;
import com.netflix.discovery.shared.transport.EurekaHttpResponse.EurekaHttpResponseBuilder;
import com.netflix.discovery.util.StringUtil;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestClient;
import org.springframework.web.util.UriBuilder;
import static com.netflix.discovery.shared.transport.EurekaHttpResponse.anEurekaHttpResponse;
/**
* {@link RestClient} implementation of {@link EurekaHttpClient}.
*
* @author Wonchul Heo
* @since 4.2.0
*/
public class RestClientEurekaHttpClient implements EurekaHttpClient {
private final RestClient restClient;
public RestClientEurekaHttpClient(RestClient restClient) {
this.restClient = restClient;
}
@Override
public EurekaHttpResponse<Void> register(InstanceInfo info) {
final ResponseEntity<Void> response = restClient.post()
.uri(builder -> builder.pathSegment("apps", info.getAppName()).build())
.body(info)
.header(HttpHeaders.ACCEPT_ENCODING, "gzip")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.toBodilessEntity();
return eurekaHttpResponse(response);
}
@Override
public EurekaHttpResponse<Void> cancel(String appName, String id) {
final ResponseEntity<Void> response = restClient.delete()
.uri(builder -> builder.pathSegment("apps", appName, id).build())
.retrieve()
.toBodilessEntity();
return eurekaHttpResponse(response);
}
@Override
public EurekaHttpResponse<InstanceInfo> sendHeartBeat(String appName, String id, InstanceInfo info,
InstanceStatus overriddenStatus) {
final Function<UriBuilder, URI> uriFunction = builder -> builder.pathSegment("apps", appName, id)
.queryParam("status", info.getStatus().toString())
.queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString())
.queryParamIfPresent("overriddenstatus", Optional.ofNullable(overriddenStatus).map(InstanceStatus::name))
.build();
final ResponseEntity<InstanceInfo> response = restClient.put()
.uri(uriFunction)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.toEntity(InstanceInfo.class);
final EurekaHttpResponseBuilder<InstanceInfo> builder = anEurekaHttpResponse(statusCodeValueOf(response),
InstanceInfo.class)
.headers(headersOf(response));
final InstanceInfo entity = response.getBody();
if (entity != null) {
builder.entity(entity);
}
return builder.build();
}
@Override
public EurekaHttpResponse<Void> statusUpdate(String appName, String id, InstanceStatus newStatus,
InstanceInfo info) {
final Function<UriBuilder, URI> uriFunction = builder -> builder.pathSegment("apps", appName, id, "status")
.queryParam("value", newStatus.name())
.queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString())
.build();
final ResponseEntity<Void> response = restClient.put()
.uri(uriFunction)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.toBodilessEntity();
return eurekaHttpResponse(response);
}
@Override
public EurekaHttpResponse<Void> deleteStatusOverride(String appName, String id, InstanceInfo info) {
final Function<UriBuilder, URI> uriFunction = builder -> builder.pathSegment("apps", appName, id, "status")
.queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString())
.build();
final ResponseEntity<Void> response = restClient.delete()
.uri(uriFunction)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.toBodilessEntity();
return eurekaHttpResponse(response);
}
@Override
public EurekaHttpResponse<Applications> getApplications(String... regions) {
return getApplicationsInternal("/apps/", regions);
}
@Override
public EurekaHttpResponse<Applications> getDelta(String... regions) {
return getApplicationsInternal("/apps/delta", regions);
}
@Override
public EurekaHttpResponse<Applications> getVip(String vipAddress, String... regions) {
return getApplicationsInternal("/vips/" + vipAddress, regions);
}
@Override
public EurekaHttpResponse<Applications> getSecureVip(String secureVipAddress, String... regions) {
return getApplicationsInternal("/svips/" + secureVipAddress, regions);
}
@Override
public EurekaHttpResponse<Application> getApplication(String appName) {
final ResponseEntity<Application> response = restClient.get()
.uri("/apps/" + appName)
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.toEntity(Application.class);
final int statusCode = statusCodeValueOf(response);
final Application body = response.getBody();
final Application application = statusCode == HttpStatus.OK.value() && body != null ? body : null;
return anEurekaHttpResponse(statusCode, application).headers(headersOf(response)).build();
}
@Override
public EurekaHttpResponse<InstanceInfo> getInstance(String appName, String id) {
return getInstanceInternal("/apps/" + appName + '/' + id);
}
@Override
public EurekaHttpResponse<InstanceInfo> getInstance(String id) {
return getInstanceInternal("/instances/" + id);
}
@Override
public void shutdown() {
}
public RestClient getRestClient() {
return restClient;
}
private EurekaHttpResponse<Applications> getApplicationsInternal(String urlPath, String[] regions) {
final Function<UriBuilder, URI> uriFunction = builder -> builder
.queryParamIfPresent("regions",
Optional.ofNullable(regions).filter(it -> it.length > 0).map(StringUtil::join))
.build();
final ResponseEntity<Applications> response = restClient.get()
.uri(urlPath, uriFunction)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.toEntity(Applications.class);
final int statusCode = statusCodeValueOf(response);
final Applications body = response.getBody();
return anEurekaHttpResponse(statusCode, statusCode == HttpStatus.OK.value() && body != null ? body : null)
.headers(headersOf(response))
.build();
}
private EurekaHttpResponse<InstanceInfo> getInstanceInternal(String urlPath) {
final ResponseEntity<InstanceInfo> response = restClient.get()
.uri(urlPath)
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.toEntity(InstanceInfo.class);
final int statusCode = statusCodeValueOf(response);
final InstanceInfo body = response.getBody();
return anEurekaHttpResponse(statusCode, statusCode == HttpStatus.OK.value() && body != null ? body : null)
.headers(headersOf(response))
.build();
}
private static Map<String, String> headersOf(ResponseEntity<?> response) {
return response.getHeaders().toSingleValueMap();
}
private static int statusCodeValueOf(ResponseEntity<?> response) {
return response.getStatusCode().value();
}
private static EurekaHttpResponse<Void> eurekaHttpResponse(ResponseEntity<?> response) {
return anEurekaHttpResponse(statusCodeValueOf(response)).headers(headersOf(response)).build();
}
}
@@ -1,153 +0,0 @@
/*
* Copyright 2017-present 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.netflix.eureka.http;
import java.io.Closeable;
import java.io.IOException;
import java.util.Optional;
import java.util.function.Supplier;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import com.netflix.discovery.shared.resolver.EurekaEndpoint;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import com.netflix.discovery.shared.transport.TransportClientFactory;
import org.springframework.cloud.configuration.TlsProperties;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.support.BasicAuthenticationInterceptor;
import org.springframework.web.client.RestClient;
import org.springframework.web.util.UriComponentsBuilder;
import static org.springframework.cloud.netflix.eureka.http.EurekaHttpClientUtils.context;
import static org.springframework.cloud.netflix.eureka.http.EurekaHttpClientUtils.extractUserInfo;
import static org.springframework.cloud.netflix.eureka.http.EurekaHttpClientUtils.mappingJacksonHttpMessageConverter;
/**
* Provides the custom {@link RestClient} required by the
* {@link RestClientEurekaHttpClient}. Relies on Jackson for serialization and
* deserialization.
*
* <p>
* A single {@link ClientHttpRequestFactory} (and the underlying HTTP client it wraps) is
* lazily built on the first call to {@link #newClient} and reused for every subsequent
* call on this factory instance. Since each {@code RestClientTransportClientFactory} is
* already scoped to a single Eureka client, caching the request factory here - rather
* than in a potentially shared {@link EurekaClientHttpRequestFactorySupplier} - ties the
* shared client's lifecycle directly to this factory instance, so {@link #shutdown()}
* only ever closes a client owned exclusively by this factory.
*
* @author Wonchul Heo
* @author Olga Maciaszek-Sharma
* @since 4.2.0
*/
public class RestClientTransportClientFactory implements TransportClientFactory {
private final Optional<SSLContext> sslContext;
private final Optional<HostnameVerifier> hostnameVerifier;
private final EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier;
private final Supplier<RestClient.Builder> builderSupplier;
private ClientHttpRequestFactory cachedRequestFactory;
public RestClientTransportClientFactory(Optional<SSLContext> sslContext,
Optional<HostnameVerifier> hostnameVerifier,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier,
Supplier<RestClient.Builder> builderSupplier) {
this.sslContext = sslContext;
this.hostnameVerifier = hostnameVerifier;
this.eurekaClientHttpRequestFactorySupplier = eurekaClientHttpRequestFactorySupplier;
this.builderSupplier = builderSupplier;
}
public RestClientTransportClientFactory(TlsProperties tlsProperties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier,
Supplier<RestClient.Builder> builderSupplier) {
this(context(tlsProperties), Optional.empty(), eurekaClientHttpRequestFactorySupplier, builderSupplier);
}
public RestClientTransportClientFactory(TlsProperties tlsProperties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier) {
this(tlsProperties, eurekaClientHttpRequestFactorySupplier, RestClient::builder);
}
@Override
public EurekaHttpClient newClient(EurekaEndpoint endpoint) {
// we want a copy to modify. Don't change the original
final RestClient.Builder builder = builderSupplier.get().clone();
ClientHttpRequestFactory requestFactory = getOrCreateRequestFactory();
builder.requestFactory(requestFactory);
setUrl(builder, endpoint.getServiceUrl());
builder.messageConverters(converters -> converters.add(0, mappingJacksonHttpMessageConverter()));
builder.defaultStatusHandler(HttpStatusCode::isError, (req, res) -> {
});
builder.requestInterceptor((request, body, execution) -> {
final ClientHttpResponse response = execution.execute(request, body);
if (!response.getStatusCode().equals(HttpStatus.NOT_FOUND)) {
return response;
}
return new NotFoundHttpResponse(response);
});
return new RestClientEurekaHttpClient(builder.build());
}
private synchronized ClientHttpRequestFactory getOrCreateRequestFactory() {
if (this.cachedRequestFactory == null) {
this.cachedRequestFactory = this.eurekaClientHttpRequestFactorySupplier.get(this.sslContext.orElse(null),
this.hostnameVerifier.orElse(null));
}
return this.cachedRequestFactory;
}
@Override
public synchronized void shutdown() {
if (this.cachedRequestFactory instanceof HttpComponentsClientHttpRequestFactory httpRequestFactory
&& httpRequestFactory.getHttpClient() instanceof Closeable closeableHttpClient) {
try {
closeableHttpClient.close();
}
catch (IOException ex) {
// best-effort close during shutdown; nothing actionable if it fails
}
}
this.cachedRequestFactory = null;
}
private static void setUrl(RestClient.Builder builder, String serviceUrl) {
final String url = UriComponentsBuilder.fromUriString(serviceUrl).userInfo(null).toUriString();
final EurekaHttpClientUtils.UserInfo userInfo = extractUserInfo(serviceUrl);
if (userInfo != null) {
builder.requestInterceptor(new BasicAuthenticationInterceptor(userInfo.username(), userInfo.password()));
}
builder.baseUrl(url);
}
}
@@ -0,0 +1,33 @@
/*
* Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.eureka.http;
import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
/**
* @author Daniel Lavoie
*/
public class RestTemplateDiscoveryClientOptionalArgs extends AbstractDiscoveryClientOptionalArgs<Void> {
protected final EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier;
public RestTemplateDiscoveryClientOptionalArgs(
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier) {
this.eurekaClientHttpRequestFactorySupplier = eurekaClientHttpRequestFactorySupplier;
}
}
@@ -0,0 +1,220 @@
/*
* Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.eureka.http;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
import com.netflix.discovery.shared.Application;
import com.netflix.discovery.shared.Applications;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import com.netflix.discovery.shared.transport.EurekaHttpResponse;
import com.netflix.discovery.shared.transport.EurekaHttpResponse.EurekaHttpResponseBuilder;
import com.netflix.discovery.util.StringUtil;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import static com.netflix.discovery.shared.transport.EurekaHttpResponse.anEurekaHttpResponse;
/**
* @author Daniel Lavoie
*/
public class RestTemplateEurekaHttpClient implements EurekaHttpClient {
private final RestTemplate restTemplate;
private String serviceUrl;
public RestTemplateEurekaHttpClient(RestTemplate restTemplate, String serviceUrl) {
this.restTemplate = restTemplate;
this.serviceUrl = serviceUrl;
if (!serviceUrl.endsWith("/")) {
this.serviceUrl = this.serviceUrl + "/";
}
}
public String getServiceUrl() {
return this.serviceUrl;
}
public RestTemplate getRestTemplate() {
return restTemplate;
}
@Override
public EurekaHttpResponse<Void> register(InstanceInfo info) {
String urlPath = serviceUrl + "apps/" + info.getAppName();
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.ACCEPT_ENCODING, "gzip");
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
ResponseEntity<Void> response = restTemplate.exchange(urlPath, HttpMethod.POST, new HttpEntity<>(info, headers),
Void.class);
return anEurekaHttpResponse(response.getStatusCode().value()).headers(headersOf(response)).build();
}
@Override
public EurekaHttpResponse<Void> cancel(String appName, String id) {
String urlPath = serviceUrl + "apps/" + appName + '/' + id;
ResponseEntity<Void> response = restTemplate.exchange(urlPath, HttpMethod.DELETE, null, Void.class);
return anEurekaHttpResponse(response.getStatusCode().value()).headers(headersOf(response)).build();
}
@Override
public EurekaHttpResponse<InstanceInfo> sendHeartBeat(String appName, String id, InstanceInfo info,
InstanceStatus overriddenStatus) {
String urlPath = serviceUrl + "apps/" + appName + '/' + id + "?status=" + info.getStatus().toString()
+ "&lastDirtyTimestamp=" + info.getLastDirtyTimestamp().toString()
+ (overriddenStatus != null ? "&overriddenstatus=" + overriddenStatus.name() : "");
ResponseEntity<InstanceInfo> response = restTemplate.exchange(urlPath, HttpMethod.PUT, null,
InstanceInfo.class);
EurekaHttpResponseBuilder<InstanceInfo> eurekaResponseBuilder = anEurekaHttpResponse(
response.getStatusCode().value(), InstanceInfo.class).headers(headersOf(response));
if (response.hasBody()) {
eurekaResponseBuilder.entity(response.getBody());
}
return eurekaResponseBuilder.build();
}
@Override
public EurekaHttpResponse<Void> statusUpdate(String appName, String id, InstanceStatus newStatus,
InstanceInfo info) {
String urlPath = serviceUrl + "apps/" + appName + '/' + id + "/status?value=" + newStatus.name()
+ "&lastDirtyTimestamp=" + info.getLastDirtyTimestamp().toString();
ResponseEntity<Void> response = restTemplate.exchange(urlPath, HttpMethod.PUT, null, Void.class);
return anEurekaHttpResponse(response.getStatusCode().value()).headers(headersOf(response)).build();
}
@Override
public EurekaHttpResponse<Void> deleteStatusOverride(String appName, String id, InstanceInfo info) {
String urlPath = serviceUrl + "apps/" + appName + '/' + id + "/status?lastDirtyTimestamp="
+ info.getLastDirtyTimestamp().toString();
ResponseEntity<Void> response = restTemplate.exchange(urlPath, HttpMethod.DELETE, null, Void.class);
return anEurekaHttpResponse(response.getStatusCode().value()).headers(headersOf(response)).build();
}
@Override
public EurekaHttpResponse<Applications> getApplications(String... regions) {
return getApplicationsInternal("apps/", regions);
}
private EurekaHttpResponse<Applications> getApplicationsInternal(String urlPath, String[] regions) {
String url = serviceUrl + urlPath;
if (regions != null && regions.length > 0) {
url = url + (urlPath.contains("?") ? "&" : "?") + "regions=" + StringUtil.join(regions);
}
ResponseEntity<EurekaApplications> response = restTemplate.exchange(url, HttpMethod.GET, null,
EurekaApplications.class);
return anEurekaHttpResponse(response.getStatusCode().value(),
response.getStatusCode().value() == HttpStatus.OK.value() && response.hasBody()
? (Applications) response.getBody() : null).headers(headersOf(response)).build();
}
@Override
public EurekaHttpResponse<Applications> getDelta(String... regions) {
return getApplicationsInternal("apps/delta", regions);
}
@Override
public EurekaHttpResponse<Applications> getVip(String vipAddress, String... regions) {
return getApplicationsInternal("vips/" + vipAddress, regions);
}
@Override
public EurekaHttpResponse<Applications> getSecureVip(String secureVipAddress, String... regions) {
return getApplicationsInternal("svips/" + secureVipAddress, regions);
}
@Override
public EurekaHttpResponse<Application> getApplication(String appName) {
String urlPath = serviceUrl + "apps/" + appName;
ResponseEntity<Application> response = restTemplate.exchange(urlPath, HttpMethod.GET, null, Application.class);
Application application = response.getStatusCode().value() == HttpStatus.OK.value() && response.hasBody()
? response.getBody() : null;
return anEurekaHttpResponse(response.getStatusCode().value(), application).headers(headersOf(response)).build();
}
@Override
public EurekaHttpResponse<InstanceInfo> getInstance(String appName, String id) {
return getInstanceInternal("apps/" + appName + '/' + id);
}
@Override
public EurekaHttpResponse<InstanceInfo> getInstance(String id) {
return getInstanceInternal("instances/" + id);
}
private EurekaHttpResponse<InstanceInfo> getInstanceInternal(String urlPath) {
urlPath = serviceUrl + urlPath;
ResponseEntity<InstanceInfo> response = restTemplate.exchange(urlPath, HttpMethod.GET, null,
InstanceInfo.class);
return anEurekaHttpResponse(response.getStatusCode().value(),
response.getStatusCode().value() == HttpStatus.OK.value() && response.hasBody() ? response.getBody()
: null).headers(headersOf(response)).build();
}
@Override
public void shutdown() {
// Nothing to do
}
private static Map<String, String> headersOf(ResponseEntity<?> response) {
HttpHeaders httpHeaders = response.getHeaders();
if (httpHeaders == null || httpHeaders.isEmpty()) {
return Collections.emptyMap();
}
Map<String, String> headers = new HashMap<>();
for (Entry<String, List<String>> entry : httpHeaders.entrySet()) {
if (!entry.getValue().isEmpty()) {
headers.put(entry.getKey(), entry.getValue().get(0));
}
}
return headers;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2017-present the original author or authors.
* Copyright 2017-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.
@@ -28,31 +28,29 @@ import com.netflix.discovery.shared.transport.TransportClientFactory;
import com.netflix.discovery.shared.transport.jersey.TransportClientFactories;
/**
* @author Wonchul Heo
* @author Olga Maciaszek-Sharma
* @since 4.2.0
* @author Daniel Lavoie
*/
public class RestClientTransportClientFactories implements TransportClientFactories<Void> {
public class RestTemplateTransportClientFactories implements TransportClientFactories<Void> {
private final RestClientDiscoveryClientOptionalArgs args;
private final RestTemplateDiscoveryClientOptionalArgs args;
public RestClientTransportClientFactories(RestClientDiscoveryClientOptionalArgs args) {
public RestTemplateTransportClientFactories(RestTemplateDiscoveryClientOptionalArgs args) {
this.args = args;
}
@Override
public TransportClientFactory newTransportClientFactory(EurekaClientConfig clientConfig,
Collection<Void> additionalFilters, InstanceInfo myInstanceInfo) {
return new RestClientTransportClientFactory(args.getSSLContext(), args.getHostnameVerifier(),
args.getEurekaClientHttpRequestFactorySupplier(), args.getRestClientBuilderSupplier());
return new RestTemplateTransportClientFactory(this.args.getSSLContext(), this.args.getHostnameVerifier(),
this.args.eurekaClientHttpRequestFactorySupplier);
}
@Override
public TransportClientFactory newTransportClientFactory(final EurekaClientConfig clientConfig,
final Collection<Void> additionalFilters, final InstanceInfo myInstanceInfo,
final Optional<SSLContext> sslContext, final Optional<HostnameVerifier> hostnameVerifier) {
return new RestClientTransportClientFactory(sslContext, hostnameVerifier,
args.getEurekaClientHttpRequestFactorySupplier(), args.getRestClientBuilderSupplier());
return new RestTemplateTransportClientFactory(this.args.getSSLContext(), this.args.getHostnameVerifier(),
this.args.eurekaClientHttpRequestFactorySupplier);
}
}
@@ -0,0 +1,258 @@
/*
* Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.eureka.http;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Optional;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.converters.jackson.mixin.ApplicationsJsonMixIn;
import com.netflix.discovery.converters.jackson.mixin.InstanceInfoJsonMixIn;
import com.netflix.discovery.converters.jackson.serializer.InstanceInfoJsonBeanSerializer;
import com.netflix.discovery.shared.Applications;
import com.netflix.discovery.shared.resolver.EurekaEndpoint;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import com.netflix.discovery.shared.transport.TransportClientFactory;
import org.springframework.cloud.configuration.SSLContextFactory;
import org.springframework.cloud.configuration.TlsProperties;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.support.BasicAuthenticationInterceptor;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
/**
* Provides the custom {@link RestTemplate} required by the
* {@link RestTemplateEurekaHttpClient}. Relies on Jackson for serialization and
* deserialization.
*
* @author Daniel Lavoie
*/
public class RestTemplateTransportClientFactory implements TransportClientFactory {
private final Optional<SSLContext> sslContext;
private final Optional<HostnameVerifier> hostnameVerifier;
private final EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier;
public RestTemplateTransportClientFactory(TlsProperties tlsProperties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier) {
this.sslContext = context(tlsProperties);
this.hostnameVerifier = Optional.empty();
this.eurekaClientHttpRequestFactorySupplier = eurekaClientHttpRequestFactorySupplier;
}
private Optional<SSLContext> context(TlsProperties properties) {
if (properties == null || !properties.isEnabled()) {
return Optional.empty();
}
try {
return Optional.of(new SSLContextFactory(properties).createSSLContext());
}
catch (Exception e) {
throw new IllegalStateException(e);
}
}
public RestTemplateTransportClientFactory(Optional<SSLContext> sslContext,
Optional<HostnameVerifier> hostnameVerifier,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier) {
this.sslContext = sslContext;
this.hostnameVerifier = hostnameVerifier;
this.eurekaClientHttpRequestFactorySupplier = eurekaClientHttpRequestFactorySupplier;
}
public RestTemplateTransportClientFactory() {
this.sslContext = Optional.empty();
this.hostnameVerifier = Optional.empty();
this.eurekaClientHttpRequestFactorySupplier = new DefaultEurekaClientHttpRequestFactorySupplier();
}
@Override
public EurekaHttpClient newClient(EurekaEndpoint serviceUrl) {
return new RestTemplateEurekaHttpClient(restTemplate(serviceUrl.getServiceUrl()),
stripUserInfo(serviceUrl.getServiceUrl()));
}
// apache http client 5.2 fails with non-null userinfo
// basic auth added in restTemplate() below
private String stripUserInfo(String serviceUrl) {
return UriComponentsBuilder.fromUriString(serviceUrl).userInfo(null).toUriString();
}
private RestTemplate restTemplate(String serviceUrl) {
ClientHttpRequestFactory requestFactory = this.eurekaClientHttpRequestFactorySupplier
.get(this.sslContext.orElse(null), this.hostnameVerifier.orElse(null));
RestTemplate restTemplate = new RestTemplate(requestFactory);
try {
URI serviceURI = new URI(serviceUrl);
if (serviceURI.getUserInfo() != null) {
String[] credentials = serviceURI.getUserInfo().split(":");
if (credentials.length == 2) {
restTemplate.getInterceptors()
.add(new BasicAuthenticationInterceptor(credentials[0], credentials[1]));
}
}
}
catch (URISyntaxException ignore) {
}
restTemplate.getMessageConverters().add(0, mappingJacksonHttpMessageConverter());
restTemplate.setErrorHandler(new ErrorHandler());
restTemplate.getInterceptors().add((request, body, execution) -> {
ClientHttpResponse response = execution.execute(request, body);
if (!response.getStatusCode().equals(HttpStatus.NOT_FOUND)) {
return response;
}
return new NotFoundHttpResponse(response);
});
return restTemplate;
}
/**
* Provides the serialization configurations required by the Eureka Server. JSON
* content exchanged with eureka requires a root node matching the entity being
* serialized or deserialized. Achived with
* {@link SerializationFeature#WRAP_ROOT_VALUE} and
* {@link DeserializationFeature#UNWRAP_ROOT_VALUE}.
* {@link PropertyNamingStrategies.SnakeCaseStrategy} is applied to the underlying
* {@link ObjectMapper}.
* @return a {@link MappingJackson2HttpMessageConverter} object
*/
public MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(new ObjectMapper().setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE));
SimpleModule jsonModule = new SimpleModule();
jsonModule.setSerializerModifier(createJsonSerializerModifier());
converter.getObjectMapper().registerModule(jsonModule);
converter.getObjectMapper().configure(SerializationFeature.WRAP_ROOT_VALUE, true);
converter.getObjectMapper().configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
converter.getObjectMapper().addMixIn(Applications.class, ApplicationsJsonMixIn.class);
converter.getObjectMapper().addMixIn(InstanceInfo.class, InstanceInfoJsonMixIn.class);
return converter;
}
public static BeanSerializerModifier createJsonSerializerModifier() {
return new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc,
JsonSerializer<?> serializer) {
if (beanDesc.getBeanClass().isAssignableFrom(InstanceInfo.class)) {
return new InstanceInfoJsonBeanSerializer((BeanSerializerBase) serializer, false);
}
return serializer;
}
};
}
@Override
public void shutdown() {
}
/**
* Response that ignores body, specifically for 404 errors.
*/
private static class NotFoundHttpResponse implements ClientHttpResponse {
private final ClientHttpResponse response;
NotFoundHttpResponse(ClientHttpResponse response) {
this.response = response;
}
@Override
public HttpStatusCode getStatusCode() throws IOException {
return response.getStatusCode();
}
@Override
public int getRawStatusCode() throws IOException {
return response.getRawStatusCode();
}
@Override
public String getStatusText() throws IOException {
return response.getStatusText();
}
@Override
public void close() {
response.close();
}
@Override
public InputStream getBody() throws IOException {
// ignore body on 404 for heartbeat, see gh-4145
return null;
}
@Override
public HttpHeaders getHeaders() {
return response.getHeaders();
}
}
class ErrorHandler extends DefaultResponseErrorHandler {
@Override
protected boolean hasError(HttpStatusCode statusCode) {
/**
* When the Eureka server restarts and a client tries to sent a heartbeat the
* server will respond with a 404. By default RestTemplate will throw an
* exception in this case. What we want is to return the 404 to the upstream
* code so it will send another registration request to the server.
*/
if (statusCode.is4xxClientError()) {
return false;
}
return super.hasError(statusCode);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2017-present the original author or authors.
* Copyright 2017-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.
@@ -1,5 +1,5 @@
/*
* Copyright 2017-present the original author or authors.
* Copyright 2017-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 @@
package org.springframework.cloud.netflix.eureka.http;
import java.util.Map;
import java.util.Optional;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
@@ -43,7 +42,6 @@ import static com.netflix.discovery.shared.transport.EurekaHttpResponse.anEureka
/**
* @author Daniel Lavoie
* @author Haytham Mohamed
* @author Václav Plic
*/
public class WebClientEurekaHttpClient implements EurekaHttpClient {
@@ -55,48 +53,34 @@ public class WebClientEurekaHttpClient implements EurekaHttpClient {
@Override
public EurekaHttpResponse<Void> register(InstanceInfo info) {
return webClient.post()
.uri(uriBuilder -> uriBuilder.path("apps/{appName}").build(info.getAppName()))
.body(BodyInserters.fromValue(info))
.header(HttpHeaders.ACCEPT_ENCODING, "gzip")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.onStatus(HttpStatusCode::isError, this::ignoreError)
.toBodilessEntity()
.map(this::eurekaHttpResponse)
.block();
return webClient.post().uri("apps/" + info.getAppName()).body(BodyInserters.fromValue(info))
.header(HttpHeaders.ACCEPT_ENCODING, "gzip")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).retrieve()
.onStatus(HttpStatusCode::isError, this::ignoreError).toBodilessEntity().map(this::eurekaHttpResponse)
.block();
}
@Override
public EurekaHttpResponse<Void> cancel(String appName, String id) {
return webClient.delete()
.uri(uriBuilder -> uriBuilder.path("apps/{appName}/{id}").build(appName, id))
.retrieve()
.onStatus(HttpStatusCode::isError, this::ignoreError)
.toBodilessEntity()
.map(this::eurekaHttpResponse)
.block();
return webClient.delete().uri("apps/" + appName + '/' + id).retrieve()
.onStatus(HttpStatusCode::isError, this::ignoreError).toBodilessEntity().map(this::eurekaHttpResponse)
.block();
}
@Override
public EurekaHttpResponse<InstanceInfo> sendHeartBeat(String appName, String id, InstanceInfo info,
InstanceStatus overriddenStatus) {
String urlPath = "apps/" + appName + '/' + id + "?status=" + info.getStatus().toString()
+ "&lastDirtyTimestamp=" + info.getLastDirtyTimestamp().toString()
+ (overriddenStatus != null ? "&overriddenstatus=" + overriddenStatus.name() : "");
ResponseEntity<InstanceInfo> response = webClient.put()
.uri(uriBuilder -> uriBuilder.path("apps/{appName}/{id}")
.queryParam("status", info.getStatus().toString())
.queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString())
.build(appName, id))
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.onStatus(HttpStatusCode::isError, this::ignoreError)
.toEntity(InstanceInfo.class)
.block();
ResponseEntity<InstanceInfo> response = webClient.put().uri(urlPath)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).retrieve()
.onStatus(HttpStatusCode::isError, this::ignoreError).toEntity(InstanceInfo.class).block();
EurekaHttpResponseBuilder<InstanceInfo> builder = anEurekaHttpResponse(statusCodeValueOf(response),
InstanceInfo.class)
.headers(headersOf(response));
InstanceInfo.class).headers(headersOf(response));
InstanceInfo entity = response.getBody();
@@ -111,31 +95,22 @@ public class WebClientEurekaHttpClient implements EurekaHttpClient {
@Override
public EurekaHttpResponse<Void> statusUpdate(String appName, String id, InstanceStatus newStatus,
InstanceInfo info) {
return webClient.put()
.uri(uriBuilder -> uriBuilder.path("apps/{appName}/{id}/status")
.queryParam("value", newStatus.name())
.queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString())
.build(appName, id))
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.onStatus(HttpStatusCode::isError, this::ignoreError)
.toBodilessEntity()
.map(this::eurekaHttpResponse)
.block();
String urlPath = "apps/" + appName + '/' + id + "/status?value=" + newStatus.name() + "&lastDirtyTimestamp="
+ info.getLastDirtyTimestamp().toString();
return webClient.put().uri(urlPath).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.retrieve().onStatus(HttpStatusCode::isError, this::ignoreError).toBodilessEntity()
.map(this::eurekaHttpResponse).block();
}
@Override
public EurekaHttpResponse<Void> deleteStatusOverride(String appName, String id, InstanceInfo info) {
return webClient.delete()
.uri(uriBuilder -> uriBuilder.path("apps/{appName}/{id}/status")
.queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString())
.build(appName, id))
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.onStatus(HttpStatusCode::isError, this::ignoreError)
.toBodilessEntity()
.map(this::eurekaHttpResponse)
.block();
String urlPath = "apps/" + appName + '/' + id + "/status?lastDirtyTimestamp="
+ info.getLastDirtyTimestamp().toString();
return webClient.delete().uri(urlPath).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.retrieve().onStatus(HttpStatusCode::isError, this::ignoreError).toBodilessEntity()
.map(this::eurekaHttpResponse).block();
}
@Override
@@ -144,25 +119,23 @@ public class WebClientEurekaHttpClient implements EurekaHttpClient {
}
private EurekaHttpResponse<Applications> getApplicationsInternal(String urlPath, String[] regions) {
Optional<String> regionsParam = (regions != null && regions.length > 0) ? Optional.of(StringUtil.join(regions))
: Optional.empty();
String url = urlPath;
ResponseEntity<Applications> response = webClient.get()
.uri(uriBuilder -> uriBuilder.path(urlPath).queryParamIfPresent("regions", regionsParam).build())
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.onStatus(HttpStatusCode::isError, this::ignoreError)
.toEntity(Applications.class)
.block();
if (regions != null && regions.length > 0) {
url = url + (urlPath.contains("?") ? "&" : "?") + "regions=" + StringUtil.join(regions);
}
ResponseEntity<Applications> response = webClient.get().uri(url)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).retrieve()
.onStatus(HttpStatusCode::isError, this::ignoreError).toEntity(Applications.class).block();
int statusCode = statusCodeValueOf(response);
Applications body = response.getBody();
return anEurekaHttpResponse(statusCode, statusCode == HttpStatus.OK.value() && body != null ? body : null)
.headers(headersOf(response))
.build();
.headers(headersOf(response)).build();
}
@Override
@@ -183,13 +156,9 @@ public class WebClientEurekaHttpClient implements EurekaHttpClient {
@Override
public EurekaHttpResponse<Application> getApplication(String appName) {
ResponseEntity<Application> response = webClient.get()
.uri(uriBuilder -> uriBuilder.path("apps/{appName}").build(appName))
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.onStatus(HttpStatusCode::isError, this::ignoreError)
.toEntity(Application.class)
.block();
ResponseEntity<Application> response = webClient.get().uri("apps/" + appName)
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).retrieve()
.onStatus(HttpStatusCode::isError, this::ignoreError).toEntity(Application.class).block();
int statusCode = statusCodeValueOf(response);
Application body = response.getBody();
@@ -201,29 +170,24 @@ public class WebClientEurekaHttpClient implements EurekaHttpClient {
@Override
public EurekaHttpResponse<InstanceInfo> getInstance(String appName, String id) {
return getInstanceInternal("apps", appName, id);
return getInstanceInternal("apps/" + appName + '/' + id);
}
@Override
public EurekaHttpResponse<InstanceInfo> getInstance(String id) {
return getInstanceInternal("instances", id);
return getInstanceInternal("instances/" + id);
}
private EurekaHttpResponse<InstanceInfo> getInstanceInternal(String... pathSegments) {
ResponseEntity<InstanceInfo> response = webClient.get()
.uri(uriBuilder -> uriBuilder.pathSegment(pathSegments).build())
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.onStatus(HttpStatusCode::isError, this::ignoreError)
.toEntity(InstanceInfo.class)
.block();
private EurekaHttpResponse<InstanceInfo> getInstanceInternal(String urlPath) {
ResponseEntity<InstanceInfo> response = webClient.get().uri(urlPath)
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).retrieve()
.onStatus(HttpStatusCode::isError, this::ignoreError).toEntity(InstanceInfo.class).block();
int statusCode = statusCodeValueOf(response);
InstanceInfo body = response.getBody();
return anEurekaHttpResponse(statusCode, statusCode == HttpStatus.OK.value() && body != null ? body : null)
.headers(headersOf(response))
.build();
.headers(headersOf(response)).build();
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2017-present the original author or authors.
* Copyright 2017-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.
@@ -52,7 +52,7 @@ public class WebClientTransportClientFactories implements TransportClientFactori
public TransportClientFactory newTransportClientFactory(final EurekaClientConfig clientConfig,
final Collection<Void> additionalFilters, final InstanceInfo myInstanceInfo,
final Optional<SSLContext> sslContext, final Optional<HostnameVerifier> hostnameVerifier) {
return new WebClientTransportClientFactory(builder, sslContext, hostnameVerifier);
return new WebClientTransportClientFactory(builder);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2017-present the original author or authors.
* Copyright 2017-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,31 +16,33 @@
package org.springframework.cloud.netflix.eureka.http;
import java.util.Optional;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.function.Supplier;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLPeerUnverifiedException;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.converters.jackson.mixin.ApplicationsJsonMixIn;
import com.netflix.discovery.converters.jackson.mixin.InstanceInfoJsonMixIn;
import com.netflix.discovery.converters.jackson.serializer.InstanceInfoJsonBeanSerializer;
import com.netflix.discovery.shared.Applications;
import com.netflix.discovery.shared.resolver.EurekaEndpoint;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import com.netflix.discovery.shared.transport.TransportClientFactory;
import io.netty.channel.Channel;
import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.JdkSslContext;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
import reactor.netty.http.client.HttpClientSecurityUtils;
import reactor.netty.resources.ConnectionProvider;
import reactor.netty.resources.LoopResources;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.http.codec.ClientCodecConfigurer;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
@@ -48,10 +50,6 @@ import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.ExchangeFilterFunctions;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;
import static org.springframework.cloud.netflix.eureka.http.EurekaHttpClientUtils.extractUserInfo;
import static org.springframework.cloud.netflix.eureka.http.EurekaHttpClientUtils.objectMapper;
/**
* Provides the custom {@link WebClient.Builder} required by the
@@ -60,33 +58,13 @@ import static org.springframework.cloud.netflix.eureka.http.EurekaHttpClientUtil
*
* @author Daniel Lavoie
* @author Haytham Mohamed
* @author Armin Krezovic
* @author Wonchul Heo
* @author Olga Maciaszek-Sharma
*/
public class WebClientTransportClientFactory implements TransportClientFactory {
private final Supplier<WebClient.Builder> builderSupplier;
private final Optional<SSLContext> sslContext;
private final Optional<HostnameVerifier> hostnameVerifier;
private final ConnectionProvider connectionProvider;
private final LoopResources loopResources;
public WebClientTransportClientFactory(Supplier<WebClient.Builder> builderSupplier) {
this(builderSupplier, Optional.empty(), Optional.empty());
}
public WebClientTransportClientFactory(Supplier<WebClient.Builder> builderSupplier, Optional<SSLContext> sslContext,
Optional<HostnameVerifier> hostnameVerifier) {
this.builderSupplier = builderSupplier;
this.sslContext = sslContext;
this.hostnameVerifier = hostnameVerifier;
this.connectionProvider = ConnectionProvider.create("eureka-webclient");
this.loopResources = LoopResources.create("eureka-webclient");
}
@Override
@@ -96,64 +74,75 @@ public class WebClientTransportClientFactory implements TransportClientFactory {
setUrl(builder, endpoint.getServiceUrl());
setCodecs(builder);
builder.filter(http4XxErrorExchangeFilterFunction());
// Use dedicated Reactor Netty resources independent of the reactive web server
// to prevent RejectedExecutionException during graceful shutdown when the
// server's event loop terminates before DiscoveryClient deregisters.
HttpClient httpClient = HttpClient.create(this.connectionProvider).runOn(this.loopResources);
if (this.sslContext.isPresent()) {
JdkSslContext sslContext = new JdkSslContext(this.sslContext.get(), true, ClientAuth.NONE);
httpClient = httpClient.secure(sslContextSpec -> {
if (this.hostnameVerifier.isPresent()) {
HostnameVerifier verifier = this.hostnameVerifier.get();
sslContextSpec.sslContext(sslContext)
.handlerConfigurator(sslHandler -> sslHandler.handshakeFuture().addListener(handshake -> {
if (handshake.isSuccess()) {
SSLEngine engine = sslHandler.engine();
if (!verifier.verify(engine.getPeerHost(), engine.getSession())) {
Channel channel = (Channel) handshake.getNow();
channel.pipeline()
.fireExceptionCaught(new SSLPeerUnverifiedException(
"Hostname verification failed for " + engine.getPeerHost()));
channel.close();
}
}
}));
}
else {
sslContextSpec.sslContext(sslContext)
.handlerConfigurator(HttpClientSecurityUtils.HOSTNAME_VERIFICATION_CONFIGURER);
}
});
}
builder.clientConnector(new ReactorClientHttpConnector(httpClient));
return new WebClientEurekaHttpClient(builder.build());
}
private WebClient.Builder setUrl(WebClient.Builder builder, String serviceUrl) {
String url = UriComponentsBuilder.fromUriString(serviceUrl).userInfo(null).toUriString();
final EurekaHttpClientUtils.UserInfo userInfo = extractUserInfo(serviceUrl);
if (userInfo != null) {
builder.filter(ExchangeFilterFunctions.basicAuthentication(userInfo.username(), userInfo.password()));
String url = serviceUrl;
try {
URI serviceURI = new URI(serviceUrl);
if (serviceURI.getUserInfo() != null) {
String[] credentials = serviceURI.getUserInfo().split(":");
if (credentials.length == 2) {
builder.filter(ExchangeFilterFunctions.basicAuthentication(credentials[0], credentials[1]));
url = serviceUrl.replace(credentials[0] + ":" + credentials[1] + "@", "");
}
}
}
catch (URISyntaxException ignore) {
}
return builder.baseUrl(url);
}
private static BeanSerializerModifier createJsonSerializerModifier() {
return new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc,
JsonSerializer<?> serializer) {
if (beanDesc.getBeanClass().isAssignableFrom(InstanceInfo.class)) {
return new InstanceInfoJsonBeanSerializer((BeanSerializerBase) serializer, false);
}
return serializer;
}
};
}
private void setCodecs(WebClient.Builder builder) {
ObjectMapper objectMapper = objectMapper();
builder.codecs(configurer -> {
ClientCodecConfigurer.ClientDefaultCodecs defaults = configurer.defaultCodecs();
defaults.jacksonJsonDecoder(new Jackson2JsonDecoder(objectMapper, MediaType.APPLICATION_JSON));
defaults.jacksonJsonEncoder(new Jackson2JsonEncoder(objectMapper, MediaType.APPLICATION_JSON));
defaults.jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper, MediaType.APPLICATION_JSON));
defaults.jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper, MediaType.APPLICATION_JSON));
});
}
/**
* Provides the serialization configurations required by the Eureka Server. JSON
* content exchanged with eureka requires a root node matching the entity being
* serialized or deserialized. Achieved with
* {@link SerializationFeature#WRAP_ROOT_VALUE} and
* {@link DeserializationFeature#UNWRAP_ROOT_VALUE}.
* {@link PropertyNamingStrategies.SnakeCaseStrategy} is applied to the underlying
* {@link ObjectMapper}.
* @return a {@link ObjectMapper} object
*/
private ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
SimpleModule jsonModule = new SimpleModule();
jsonModule.setSerializerModifier(createJsonSerializerModifier());
objectMapper.registerModule(jsonModule);
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
objectMapper.addMixIn(Applications.class, ApplicationsJsonMixIn.class);
objectMapper.addMixIn(InstanceInfo.class, InstanceInfoJsonMixIn.class);
return objectMapper;
}
// Skip over 4xx http errors
private ExchangeFilterFunction http4XxErrorExchangeFilterFunction() {
return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
@@ -164,11 +153,9 @@ public class WebClientTransportClientFactory implements TransportClientFactory {
return Mono.just(newResponse);
}
if (clientResponse.statusCode().equals(HttpStatus.NOT_FOUND)) {
ClientResponse newResponse = clientResponse.mutate()
.statusCode(clientResponse.statusCode())
// ignore body on 404 for heartbeat, see gh-4145
.body(Flux.empty())
.build();
ClientResponse newResponse = clientResponse.mutate().statusCode(clientResponse.statusCode())
// ignore body on 404 for heartbeat, see gh-4145
.body(Flux.empty()).build();
return Mono.just(newResponse);
}
return Mono.just(clientResponse);
@@ -177,8 +164,6 @@ public class WebClientTransportClientFactory implements TransportClientFactory {
@Override
public void shutdown() {
this.connectionProvider.dispose();
this.loopResources.dispose();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2012-present the original author or authors.
* Copyright 2012-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.
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2017-present the original author or authors.
* Copyright 2017-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.
@@ -1,5 +1,5 @@
/*
* Copyright 2017-present the original author or authors.
* Copyright 2017-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.
@@ -1,5 +1,5 @@
/*
* Copyright 2017-present the original author or authors.
* Copyright 2017-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.
@@ -1,5 +1,5 @@
/*
* Copyright 2019-present the original author or authors.
* Copyright 2019-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.
@@ -52,16 +52,15 @@ public class EurekaReactiveDiscoveryClient implements ReactiveDiscoveryClient {
@Override
public Flux<ServiceInstance> getInstances(String serviceId) {
return Flux.defer(() -> Flux.fromIterable(eurekaClient.getInstancesByVipAddress(serviceId, false)))
.map(EurekaServiceInstance::new);
.map(EurekaServiceInstance::new);
}
@Override
public Flux<String> getServices() {
return Flux.defer(() -> Mono.justOrEmpty(eurekaClient.getApplications()))
.flatMapIterable(Applications::getRegisteredApplications)
.filter(application -> !application.getInstances().isEmpty())
.map(Application::getName)
.map(String::toLowerCase);
.flatMapIterable(Applications::getRegisteredApplications)
.filter(application -> !application.getInstances().isEmpty()).map(Application::getName)
.map(String::toLowerCase);
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2019-present the original author or authors.
* Copyright 2019-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.
@@ -61,7 +61,7 @@ public class EurekaReactiveDiscoveryClientConfiguration {
}
@Bean
@ConditionalOnClass(name = "org.springframework.boot.health.contributor.ReactiveHealthIndicator")
@ConditionalOnClass(name = "org.springframework.boot.actuate.health.ReactiveHealthIndicator")
@ConditionalOnDiscoveryHealthIndicatorEnabled
public ReactiveDiscoveryClientHealthIndicator eurekaReactiveDiscoveryClientHealthIndicator(
EurekaReactiveDiscoveryClient client, DiscoveryClientHealthIndicatorProperties properties) {
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present the original author or authors.
* 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.
@@ -22,7 +22,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.web.server.context.WebServerInitializedEvent;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.cloud.client.discovery.event.InstancePreRegisteredEvent;
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistration;
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.netflix.eureka.CloudEurekaClient;
import org.springframework.cloud.netflix.eureka.CloudEurekaInstanceConfig;
@@ -102,7 +102,7 @@ public class EurekaRegistration implements Registration {
@Override
public URI getUri() {
return ServiceInstance.createUri(this);
return DefaultServiceInstance.getUri(this);
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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,54 +17,25 @@
package org.springframework.cloud.netflix.eureka.serviceregistry;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.netflix.appinfo.InstanceInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
import static com.netflix.appinfo.InstanceInfo.InstanceStatus.UNKNOWN;
/**
* @author Spencer Gibb
* @author Robert Bleyl
*/
public class EurekaServiceRegistry implements ServiceRegistry<EurekaRegistration> {
private static final Log log = LogFactory.getLog(EurekaServiceRegistry.class);
private EurekaInstanceConfigBean eurekaInstanceConfigBean;
public EurekaServiceRegistry() {
}
public EurekaServiceRegistry(EurekaInstanceConfigBean eurekaInstanceConfigBean) {
this.eurekaInstanceConfigBean = eurekaInstanceConfigBean;
}
@Override
public void register(EurekaRegistration reg) {
if (eurekaInstanceConfigBean != null && eurekaInstanceConfigBean.isAsyncClientInitialization()) {
if (log.isDebugEnabled()) {
log.debug("Initializing client asynchronously...");
}
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(() -> {
maybeInitializeClient(reg);
if (log.isDebugEnabled()) {
log.debug("Asynchronous client initialization done.");
}
});
}
else {
maybeInitializeClient(reg);
}
maybeInitializeClient(reg);
if (log.isInfoEnabled()) {
log.info("Registering application " + reg.getApplicationInfoManager().getInfo().getAppName()
@@ -74,7 +45,7 @@ public class EurekaServiceRegistry implements ServiceRegistry<EurekaRegistration
reg.getApplicationInfoManager().setInstanceStatus(reg.getInstanceConfig().getInitialStatus());
reg.getHealthCheckHandler()
.ifAvailable(healthCheckHandler -> reg.getEurekaClient().registerHealthCheck(healthCheckHandler));
.ifAvailable(healthCheckHandler -> reg.getEurekaClient().registerHealthCheck(healthCheckHandler));
}
private void maybeInitializeClient(EurekaRegistration reg) {
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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.
@@ -18,12 +18,6 @@
"description": "Enables the use of WebClient for Eureka HTTP Client.",
"type": "java.lang.Boolean"
},
{
"defaultValue": true,
"name": "eureka.client.jersey.enabled",
"description": "Enables the use of JerseyClient for Eureka HTTP Client.",
"type": "java.lang.Boolean"
},
{
"defaultValue": 1,
"name": "eureka.instance.metadata-map.weight",
@@ -1,5 +1,5 @@
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaConfigServerBootstrapConfiguration
org.springframework.boot.bootstrap.BootstrapRegistryInitializer=\
org.springframework.boot.BootstrapRegistryInitializer=\
org.springframework.cloud.netflix.eureka.config.EurekaConfigServerBootstrapper
@@ -1,5 +1,5 @@
/*
* Copyright 2015-present the original author or authors.
* Copyright 2015-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,23 +37,20 @@ class ConditionalOnRefreshScopeTests {
@Test
void refreshScopeIncluded() {
new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(RefreshAutoConfiguration.class))
.withUserConfiguration(Beans.class)
.run(c -> {
assertThat(c).hasSingleBean(org.springframework.cloud.context.scope.refresh.RefreshScope.class);
assertThat(c.getBean("foo")).isEqualTo("foo");
});
.withUserConfiguration(Beans.class).run(c -> {
assertThat(c).hasSingleBean(org.springframework.cloud.context.scope.refresh.RefreshScope.class);
assertThat(c.getBean("foo")).isEqualTo("foo");
});
}
@Test
void refreshScopeIncludedAndPropertyDisabled() {
new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(RefreshAutoConfiguration.class))
.withPropertyValues("eureka.client.refresh.enable=false")
.withUserConfiguration(Beans.class)
.run(c -> {
assertThat(c).hasSingleBean(org.springframework.cloud.context.scope.refresh.RefreshScope.class);
assertThat(c).doesNotHaveBean("foo");
assertThat(c.getBean("bar")).isEqualTo("bar");
});
.withPropertyValues("eureka.client.refresh.enable=false").withUserConfiguration(Beans.class).run(c -> {
assertThat(c).hasSingleBean(org.springframework.cloud.context.scope.refresh.RefreshScope.class);
assertThat(c).doesNotHaveBean("foo");
assertThat(c.getBean("bar")).isEqualTo("bar");
});
}
@Test
@@ -64,11 +61,10 @@ class ConditionalOnRefreshScopeTests {
});
new ApplicationContextRunner().withUserConfiguration(Beans.class)
.withPropertyValues("eureka.client.refresh.enable=false")
.run(c -> {
assertThat(c).doesNotHaveBean("foo");
assertThat(c.getBean("bar")).isEqualTo("bar");
});
.withPropertyValues("eureka.client.refresh.enable=false").run(c -> {
assertThat(c).doesNotHaveBean("foo");
assertThat(c.getBean("bar")).isEqualTo("bar");
});
}
@Configuration(proxyBeanMethods = false)
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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.
@@ -118,7 +118,7 @@ class EurekaClientAutoConfigurationTests {
@Test
void shouldSetManagementAndJmxPortsInMetadataMap() {
TestPropertyValues.of("management.server.port=9999", "com.sun.management.jmxremote.port=6789")
.applyTo(this.context);
.applyTo(this.context);
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
@@ -128,10 +128,8 @@ class EurekaClientAutoConfigurationTests {
@Test
void shouldNotResetManagementAndJmxPortsInMetadataMap() {
TestPropertyValues
.of("management.server.port=9999", "eureka.instance.metadata-map.jmx.port=9898",
"eureka.instance.metadata-map.management.port=7878")
.applyTo(this.context);
TestPropertyValues.of("management.server.port=9999", "eureka.instance.metadata-map.jmx.port=9898",
"eureka.instance.metadata-map.management.port=7878").applyTo(this.context);
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
@@ -180,128 +178,112 @@ class EurekaClientAutoConfigurationTests {
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getStatusPageUrl().contains("9999")).as("Wrong status page: " + instance.getStatusPageUrl())
.isTrue();
.isTrue();
}
@Test
void statusPageUrlPathAndManagementPort() {
TestPropertyValues
.of("server.port=8989", "management.server.port=9999", "eureka.instance.statusPageUrlPath=/myStatusPage")
.applyTo(this.context);
TestPropertyValues.of("server.port=8989", "management.server.port=9999",
"eureka.instance.statusPageUrlPath=/myStatusPage").applyTo(this.context);
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getStatusPageUrl().contains("/myStatusPage"))
.as("Wrong status page: " + instance.getStatusPageUrl())
.isTrue();
.as("Wrong status page: " + instance.getStatusPageUrl()).isTrue();
}
@Test
void healthCheckUrlPathAndManagementPort() {
TestPropertyValues
.of("server.port=8989", "management.server.port=9999", "eureka.instance.healthCheckUrlPath=/myHealthCheck")
.applyTo(this.context);
TestPropertyValues.of("server.port=8989", "management.server.port=9999",
"eureka.instance.healthCheckUrlPath=/myHealthCheck").applyTo(this.context);
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getHealthCheckUrl().contains("/myHealthCheck"))
.as("Wrong health check: " + instance.getHealthCheckUrl())
.isTrue();
.as("Wrong health check: " + instance.getHealthCheckUrl()).isTrue();
}
@Test
void statusPageUrl_and_healthCheckUrl_do_not_contain_server_context_path() {
TestPropertyValues.of("server.port=8989", "management.server.port=9999", "server.contextPath=/service")
.applyTo(this.context);
.applyTo(this.context);
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getStatusPageUrl().endsWith(":9999/actuator/info"))
.as("Wrong status page: " + instance.getStatusPageUrl())
.isTrue();
.as("Wrong status page: " + instance.getStatusPageUrl()).isTrue();
assertThat(instance.getHealthCheckUrl().endsWith(":9999/actuator/health"))
.as("Wrong health check: " + instance.getHealthCheckUrl())
.isTrue();
.as("Wrong health check: " + instance.getHealthCheckUrl()).isTrue();
}
@Test
void statusPageUrl_and_healthCheckUrl_contain_management_context_path() {
TestPropertyValues.of("server.port=8989", "management.server.servlet.context-path=/management")
.applyTo(this.context);
.applyTo(this.context);
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getStatusPageUrl().endsWith(":8989/management/actuator/info"))
.as("Wrong status page: " + instance.getStatusPageUrl())
.isTrue();
.as("Wrong status page: " + instance.getStatusPageUrl()).isTrue();
assertThat(instance.getHealthCheckUrl().endsWith(":8989/management/actuator/health"))
.as("Wrong health check: " + instance.getHealthCheckUrl())
.isTrue();
.as("Wrong health check: " + instance.getHealthCheckUrl()).isTrue();
}
@Test
void statusPageUrl_and_healthCheckUrl_contain_management_context_path_random_port() {
TestPropertyValues.of("server.port=0", "management.server.servlet.context-path=/management")
.applyTo(this.context);
.applyTo(this.context);
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getStatusPageUrlPath().equals("/management/actuator/info"))
.as("Wrong status page: " + instance.getStatusPageUrlPath())
.isTrue();
.as("Wrong status page: " + instance.getStatusPageUrlPath()).isTrue();
assertThat(instance.getHealthCheckUrlPath().equals("/management/actuator/health"))
.as("Wrong health check: " + instance.getHealthCheckUrlPath())
.isTrue();
.as("Wrong health check: " + instance.getHealthCheckUrlPath()).isTrue();
}
@Test
void statusPageUrlPathAndManagementPortAndContextPath() {
TestPropertyValues
.of("server.port=8989", "management.server.port=9999", "management.server.servlet.context-path=/manage",
"eureka.instance.status-page-url-path=/myStatusPage")
.applyTo(this.context);
TestPropertyValues.of("server.port=8989", "management.server.port=9999",
"management.server.servlet.context-path=/manage", "eureka.instance.status-page-url-path=/myStatusPage")
.applyTo(this.context);
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getStatusPageUrl().endsWith(":9999/manage/myStatusPage"))
.as("Wrong status page: " + instance.getStatusPageUrl())
.isTrue();
.as("Wrong status page: " + instance.getStatusPageUrl()).isTrue();
}
@Test
void healthCheckUrlPathAndManagementPortAndContextPath() {
TestPropertyValues
.of("server.port=8989", "management.server.port=9999", "management.server.servlet.context-path=/manage",
"eureka.instance.health-check-url-path=/myHealthCheck")
.applyTo(this.context);
.of("server.port=8989", "management.server.port=9999", "management.server.servlet.context-path=/manage",
"eureka.instance.health-check-url-path=/myHealthCheck")
.applyTo(this.context);
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getHealthCheckUrl().endsWith(":9999/manage/myHealthCheck"))
.as("Wrong health check: " + instance.getHealthCheckUrl())
.isTrue();
.as("Wrong health check: " + instance.getHealthCheckUrl()).isTrue();
}
@Test
void statusPageUrlPathAndManagementPortAndContextPathKebobCase() {
TestPropertyValues
.of("server.port=8989", "management.server.port=9999", "management.server.servlet.context-path=/manage",
"eureka.instance.status-page-url-path=/myStatusPage")
.applyTo(this.context);
TestPropertyValues.of("server.port=8989", "management.server.port=9999",
"management.server.servlet.context-path=/manage", "eureka.instance.status-page-url-path=/myStatusPage")
.applyTo(this.context);
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getStatusPageUrl().endsWith(":9999/manage/myStatusPage"))
.as("Wrong status page: " + instance.getStatusPageUrl())
.isTrue();
.as("Wrong status page: " + instance.getStatusPageUrl()).isTrue();
}
@Test
void healthCheckUrlPathAndManagementPortAndContextPathKebobCase() {
TestPropertyValues
.of("server.port=8989", "management.server.port=9999", "management.server.servlet.context-path=/manage",
"eureka.instance.health-check-url-path=/myHealthCheck")
.applyTo(this.context);
.of("server.port=8989", "management.server.port=9999", "management.server.servlet.context-path=/manage",
"eureka.instance.health-check-url-path=/myHealthCheck")
.applyTo(this.context);
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getHealthCheckUrl().endsWith(":9999/manage/myHealthCheck"))
.as("Wrong health check: " + instance.getHealthCheckUrl())
.isTrue();
.as("Wrong health check: " + instance.getHealthCheckUrl()).isTrue();
}
@Test
@@ -311,11 +293,9 @@ class EurekaClientAutoConfigurationTests {
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getStatusPageUrl().endsWith(":8989/management/actuator/info"))
.as("Wrong status page: " + instance.getStatusPageUrl())
.isTrue();
.as("Wrong status page: " + instance.getStatusPageUrl()).isTrue();
assertThat(instance.getHealthCheckUrl().endsWith(":8989/management/actuator/health"))
.as("Wrong health check: " + instance.getHealthCheckUrl())
.isTrue();
.as("Wrong health check: " + instance.getHealthCheckUrl()).isTrue();
}
@Test
@@ -325,132 +305,111 @@ class EurekaClientAutoConfigurationTests {
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getStatusPageUrlPath().equals("/management/actuator/info"))
.as("Wrong status page: " + instance.getStatusPageUrlPath())
.isTrue();
.as("Wrong status page: " + instance.getStatusPageUrlPath()).isTrue();
assertThat(instance.getHealthCheckUrlPath().equals("/management/actuator/health"))
.as("Wrong health check: " + instance.getHealthCheckUrlPath())
.isTrue();
.as("Wrong health check: " + instance.getHealthCheckUrlPath()).isTrue();
}
@Test
void statusPageUrlPathAndManagementPortAndBasePath() {
TestPropertyValues
.of("server.port=8989", "management.server.port=9999", "management.server.base-path=/manage",
"eureka.instance.status-page-url-path=/myStatusPage")
.applyTo(this.context);
TestPropertyValues.of("server.port=8989", "management.server.port=9999", "management.server.base-path=/manage",
"eureka.instance.status-page-url-path=/myStatusPage").applyTo(this.context);
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getStatusPageUrl().endsWith(":9999/manage/myStatusPage"))
.as("Wrong status page: " + instance.getStatusPageUrl())
.isTrue();
.as("Wrong status page: " + instance.getStatusPageUrl()).isTrue();
}
@Test
void healthCheckUrlPathAndManagementPortAndBasePath() {
TestPropertyValues
.of("server.port=8989", "management.server.port=9999", "management.server.base-path=/manage",
"eureka.instance.health-check-url-path=/myHealthCheck")
.applyTo(this.context);
TestPropertyValues.of("server.port=8989", "management.server.port=9999", "management.server.base-path=/manage",
"eureka.instance.health-check-url-path=/myHealthCheck").applyTo(this.context);
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getHealthCheckUrl().endsWith(":9999/manage/myHealthCheck"))
.as("Wrong health check: " + instance.getHealthCheckUrl())
.isTrue();
.as("Wrong health check: " + instance.getHealthCheckUrl()).isTrue();
}
@Test
void statusPageUrlPathAndManagementPortAndBasePathKebobCase() {
TestPropertyValues
.of("server.port=8989", "management.server.port=9999", "management.server.base-path=/manage",
"eureka.instance.status-page-url-path=/myStatusPage")
.applyTo(this.context);
TestPropertyValues.of("server.port=8989", "management.server.port=9999", "management.server.base-path=/manage",
"eureka.instance.status-page-url-path=/myStatusPage").applyTo(this.context);
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getStatusPageUrl().endsWith(":9999/manage/myStatusPage"))
.as("Wrong status page: " + instance.getStatusPageUrl())
.isTrue();
.as("Wrong status page: " + instance.getStatusPageUrl()).isTrue();
}
@Test
void healthCheckUrlPathAndManagementPortAndBasePathKebobCase() {
TestPropertyValues
.of("server.port=8989", "management.server.port=9999", "management.server.base-path=/manage",
"eureka.instance.health-check-url-path=/myHealthCheck")
.applyTo(this.context);
TestPropertyValues.of("server.port=8989", "management.server.port=9999", "management.server.base-path=/manage",
"eureka.instance.health-check-url-path=/myHealthCheck").applyTo(this.context);
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getHealthCheckUrl().endsWith(":9999/manage/myHealthCheck"))
.as("Wrong health check: " + instance.getHealthCheckUrl())
.isTrue();
.as("Wrong health check: " + instance.getHealthCheckUrl()).isTrue();
}
@Test
void healthCheckUrlPathWithServerPortAndContextPathKebobCase() {
TestPropertyValues
.of("server.port=8989", "server.servlet.context-path=/servletContextPath",
"eureka.instance.health-check-url-path=${server.servlet.context-path:}/myHealthCheck")
.applyTo(this.context);
.of("server.port=8989", "server.servlet.context-path=/servletContextPath",
"eureka.instance.health-check-url-path=${server.servlet.context-path:}/myHealthCheck")
.applyTo(this.context);
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getHealthCheckUrl()).as("Wrong health check: " + instance.getHealthCheckUrl())
.endsWith(":8989/servletContextPath/myHealthCheck");
.endsWith(":8989/servletContextPath/myHealthCheck");
}
@Test
void statusPageUrlPathAndManagementPortKabobCase() {
TestPropertyValues
.of("server.port=8989", "management.server.port=9999", "eureka.instance.status-page-url-path=/myStatusPage")
.applyTo(this.context);
TestPropertyValues.of("server.port=8989", "management.server.port=9999",
"eureka.instance.status-page-url-path=/myStatusPage").applyTo(this.context);
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getStatusPageUrl().contains("/myStatusPage"))
.as("Wrong status page: " + instance.getStatusPageUrl())
.isTrue();
.as("Wrong status page: " + instance.getStatusPageUrl()).isTrue();
}
@Test
void statusPageUrlAndPreferIpAddress() {
TestPropertyValues
.of("server.port=8989", "management.server.port=9999", "eureka.instance.hostname=foo",
"eureka.instance.prefer-ip-address:true")
.applyTo(this.context);
TestPropertyValues.of("server.port=8989", "management.server.port=9999", "eureka.instance.hostname=foo",
"eureka.instance.prefer-ip-address:true").applyTo(this.context);
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getStatusPageUrl()).as("statusPageUrl is wrong")
.isEqualTo("http://" + instance.getIpAddress() + ":9999/actuator/info");
.isEqualTo("http://" + instance.getIpAddress() + ":9999/actuator/info");
assertThat(instance.getHealthCheckUrl()).as("healthCheckUrl is wrong")
.isEqualTo("http://" + instance.getIpAddress() + ":9999/actuator/health");
.isEqualTo("http://" + instance.getIpAddress() + ":9999/actuator/health");
}
@Test
void statusPageAndHealthCheckUrlsShouldSetUserDefinedIpAddress() {
TestPropertyValues
.of("server.port=8989", "management.server.port=9999", "eureka.instance.hostname=foo",
"eureka.instance.ip-address:192.168.13.90", "eureka.instance.prefer-ip-address:true")
.applyTo(this.context);
.of("server.port=8989", "management.server.port=9999", "eureka.instance.hostname=foo",
"eureka.instance.ip-address:192.168.13.90", "eureka.instance.prefer-ip-address:true")
.applyTo(this.context);
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getStatusPageUrl()).as("statusPageUrl is wrong")
.isEqualTo("http://192.168.13.90:9999/actuator/info");
.isEqualTo("http://192.168.13.90:9999/actuator/info");
assertThat(instance.getHealthCheckUrl()).as("healthCheckUrl is wrong")
.isEqualTo("http://192.168.13.90:9999/actuator/health");
.isEqualTo("http://192.168.13.90:9999/actuator/health");
}
@Test
void healthCheckUrlPathAndManagementPortKabobCase() {
TestPropertyValues
.of("server.port=8989", "management.server.port=9999",
"eureka.instance.health-check-url-path=/myHealthCheck")
.applyTo(this.context);
TestPropertyValues.of("server.port=8989", "management.server.port=9999",
"eureka.instance.health-check-url-path=/myHealthCheck").applyTo(this.context);
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getHealthCheckUrl().contains("/myHealthCheck"))
.as("Wrong health check: " + instance.getHealthCheckUrl())
.isTrue();
.as("Wrong health check: " + instance.getHealthCheckUrl()).isTrue();
}
@Test
@@ -460,8 +419,7 @@ class EurekaClientAutoConfigurationTests {
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getStatusPageUrl().contains("/myStatusPage"))
.as("Wrong status page: " + instance.getStatusPageUrl())
.isTrue();
.as("Wrong status page: " + instance.getStatusPageUrl()).isTrue();
}
@Test
@@ -471,36 +429,35 @@ class EurekaClientAutoConfigurationTests {
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getHealthCheckUrl().contains("/myHealthCheck"))
.as("Wrong health check: " + instance.getHealthCheckUrl())
.isTrue();
.as("Wrong health check: " + instance.getHealthCheckUrl()).isTrue();
}
@Test
void hostname() {
TestPropertyValues.of("server.port=8989", "management.server.port=9999", "eureka.instance.hostname=foo")
.applyTo(this.context);
.applyTo(this.context);
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context.getBean(EurekaInstanceConfigBean.class);
assertThat(instance.getStatusPageUrl().contains("foo")).as("Wrong status page: " + instance.getStatusPageUrl())
.isTrue();
.isTrue();
}
@Test
void refreshScopedBeans() {
setupContext(RefreshAutoConfiguration.class);
assertThat(this.context.getBeanDefinition("eurekaClient").getBeanClassName())
.startsWith(GenericScope.class.getName() + "$LockedScopedProxyFactoryBean");
.startsWith(GenericScope.class.getName() + "$LockedScopedProxyFactoryBean");
assertThat(this.context.getBeanDefinition("eurekaApplicationInfoManager").getBeanClassName())
.startsWith(GenericScope.class.getName() + "$LockedScopedProxyFactoryBean");
.startsWith(GenericScope.class.getName() + "$LockedScopedProxyFactoryBean");
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
void shouldReregisterHealthCheckHandlerAfterRefresh() throws Exception {
TestPropertyValues
.of("eureka.client.healthcheck.enabled=true", "spring.cloud.config.import-check.enabled=false",
"spring.cloud.refresh.additionalPropertySourcesToRetain=test")
.applyTo(this.context);
.of("eureka.client.healthcheck.enabled=true", "spring.cloud.config.import-check.enabled=false",
"spring.cloud.refresh.additionalPropertySourcesToRetain=test")
.applyTo(this.context);
setupContext(RefreshAutoConfiguration.class, AutoServiceRegistrationConfiguration.class);
EurekaClient oldEurekaClient = getLazyInitEurekaClient();
@@ -640,14 +597,13 @@ class EurekaClientAutoConfigurationTests {
@Test
void shouldNotHaveDiscoveryClientWhenBlockingDiscoveryDisabled() {
new ApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(UtilAutoConfiguration.class, DiscoveryClientOptionalArgsConfiguration.class,
EurekaClientAutoConfiguration.class, EurekaDiscoveryClientConfiguration.class))
.withPropertyValues("spring.cloud.discovery.blocking.enabled=false")
.run(context -> {
assertThat(context).doesNotHaveBean(DiscoveryClient.class);
assertThat(context).doesNotHaveBean(DiscoveryClientHealthIndicator.class);
});
.withConfiguration(AutoConfigurations.of(UtilAutoConfiguration.class,
DiscoveryClientOptionalArgsConfiguration.class, EurekaClientAutoConfiguration.class,
EurekaDiscoveryClientConfiguration.class))
.withPropertyValues("spring.cloud.discovery.blocking.enabled=false").run(context -> {
assertThat(context).doesNotHaveBean(DiscoveryClient.class);
assertThat(context).doesNotHaveBean(DiscoveryClientHealthIndicator.class);
});
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@@ -685,7 +641,7 @@ class EurekaClientAutoConfigurationTests {
private EurekaClient getLazyInitEurekaClient() throws Exception {
return (EurekaClient) ((Advised) this.context.getBean("eurekaClient", EurekaClient.class)).getTargetSource()
.getTarget();
.getTarget();
}
@Configuration(proxyBeanMethods = false)
@@ -1,5 +1,5 @@
/*
* Copyright 2013-present 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.
@@ -59,7 +59,7 @@ class EurekaClientConfigBeanTests {
this.context.register(PropertyPlaceholderAutoConfiguration.class, TestConfiguration.class);
this.context.refresh();
assertThat(this.context.getBean(EurekaClientConfigBean.class).getServiceUrl().toString())
.isEqualTo("{defaultZone=https://example.com}");
.isEqualTo("{defaultZone=https://example.com}");
assertThat(getEurekaServiceUrlsForDefaultZone()).isEqualTo("[https://example.com/]");
}
@@ -1,59 +0,0 @@
/*
* Copyright 2025-present 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.netflix.eureka;
import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.shared.Applications;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.Mockito.when;
/**
* @author Mohamed Macow
*/
@ExtendWith(MockitoExtension.class)
class EurekaDiscoveryClientTests {
@Mock
private EurekaClient eurekaClient;
@InjectMocks
private EurekaDiscoveryClient client;
@Test
void shouldCompleteProbeWhenClientHealthy() {
when(eurekaClient.getApplications()).thenReturn(new Applications());
assertThatCode(() -> client.probe()).doesNotThrowAnyException();
}
@Test
void shouldThrowProbeWhenClientThrows() {
RuntimeException eurekaException = new RuntimeException("exception");
when(eurekaClient.getApplications()).thenThrow(eurekaException);
assertThatExceptionOfType(eurekaException.getClass()).isThrownBy(() -> client.probe())
.withMessage(eurekaException.getMessage());
}
}

Some files were not shown because too many files have changed in this diff Show More