mirror of
https://github.com/spring-cloud/spring-cloud-netflix.git
synced 2026-09-19 00:29:01 +00:00
Compare commits
60
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac92b6f634 | ||
|
|
3f2c57cf33 | ||
|
|
c93a80f550 | ||
|
|
89a63a095b | ||
|
|
60cc0b78ce | ||
|
|
a6f750963e | ||
|
|
d775c5cb18 | ||
|
|
4cb261cf76 | ||
|
|
d54022d22d | ||
|
|
2b64240a3c | ||
|
|
0b0a66f645 | ||
|
|
12faa11ad0 | ||
|
|
b78242ce4f | ||
|
|
07da94ce8c | ||
|
|
5cb21539b6 | ||
|
|
bc2f89f358 | ||
|
|
4b40a45016 | ||
|
|
cf76ee00f5 | ||
|
|
f9a3d9a660 | ||
|
|
39c66cfe7f | ||
|
|
4552b33a74 | ||
|
|
27e5d9e861 | ||
|
|
c384bff5fc | ||
|
|
ddfbfe8158 | ||
|
|
d6ee25c648 | ||
|
|
92268dbe6f | ||
|
|
fd941835d3 | ||
|
|
8c4a49be98 | ||
|
|
9ef7dfdc53 | ||
|
|
da3d067481 | ||
|
|
766f8bbab7 | ||
|
|
75dcd309e9 | ||
|
|
c7eb5f570d | ||
|
|
2179675027 | ||
|
|
fd159673c4 | ||
|
|
5d52c61c7c | ||
|
|
76baf2b914 | ||
|
|
4f3b89d798 | ||
|
|
d35b660eed | ||
|
|
fa783ccedd | ||
|
|
0cb8bf671b | ||
|
|
10eb85b7f0 | ||
|
|
b42ccc5d44 | ||
|
|
931f058068 | ||
|
|
c1dd0a6b6d | ||
|
|
c57a0e323c | ||
|
|
e1dca45c10 | ||
|
|
a8282dd0bd | ||
|
|
9e3e749438 | ||
|
|
434305de19 | ||
|
|
a9c0fc1502 | ||
|
|
9dafae6c01 | ||
|
|
f538444dee | ||
|
|
99beb9d045 | ||
|
|
4cada7c8cc | ||
|
|
34dff916cf | ||
|
|
7e9faef2e7 | ||
|
|
476a5cb84c | ||
|
|
cfa4302965 | ||
|
|
5d6590da44 |
@@ -14,3 +14,4 @@ _site/
|
||||
*.ipr
|
||||
*.iws
|
||||
.factorypath
|
||||
.shelf
|
||||
|
||||
+4
-2
@@ -1,6 +1,8 @@
|
||||
// Do not edit this file (e.g. go instead to docs/src/main/asciidoc)
|
||||
|
||||
image::https://travis-ci.org/spring-cloud/spring-cloud-netflix.svg?branch=master[Build Status, link=https://travis-ci.org/spring-cloud/spring-cloud-netflix]
|
||||
image::https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/1.1.x.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/1.1.x"]
|
||||
image::https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/1.1.x/graph/badge.svg["Codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/1.1.x"]
|
||||
image::https://api.codacy.com/project/badge/Grade/a6885a06921e4f72a0df0b7aabd6d118?branch=1.1.x["Codacy code quality", link="https://www.codacy.com/app/Spring-Cloud/spring-cloud-netflix?branch=1.1.x&utm_source=github.com&utm_medium=referral&utm_content=spring-cloud/spring-cloud-netflix&utm_campaign=Badge_Grade&bid=3538621"]
|
||||
|
||||
This project provides Netflix OSS integrations for Spring Boot apps through autoconfiguration
|
||||
and binding to the Spring Environment and other Spring programming model idioms. With a few
|
||||
@@ -157,7 +159,7 @@ follow the guidelines below.
|
||||
|
||||
=== Sign the Contributor License Agreement
|
||||
Before we accept a non-trivial patch or pull request we will need you to sign the
|
||||
https://support.springsource.com/spring_committer_signup[contributor's agreement].
|
||||
https://cla.pivotal.io/sign/spring[Contributor License Agreement].
|
||||
Signing the contributor's agreement does not grant anyone commit rights to the main
|
||||
repository, but it does mean that we can accept your contributions, and you will get an
|
||||
author credit if we do. Active contributors might be asked to join the core team, and
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
general:
|
||||
branches:
|
||||
ignore:
|
||||
- gh-pages # list of branches to ignore
|
||||
machine:
|
||||
java:
|
||||
version: oraclejdk8
|
||||
environment:
|
||||
_JAVA_OPTIONS: "-Xms1024m -Xmx2048m"
|
||||
dependencies:
|
||||
override:
|
||||
- ./mvnw -s .settings.xml -U --fail-never dependency:go-offline || true
|
||||
test:
|
||||
override:
|
||||
- ./mvnw -s .settings.xml clean install org.jacoco:jacoco-maven-plugin:prepare-agent install -U -P sonar -nsu --batch-mode -Dmaven.test.redirectTestOutputToFile=true -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn
|
||||
post:
|
||||
- find . -type f -regex ".*/spring-cloud-*.*/target/*.*" | cpio -pdm $CIRCLE_ARTIFACTS
|
||||
- mkdir -p $CIRCLE_TEST_REPORTS/junit/
|
||||
- find . -type f -regex ".*/target/.*-reports/.*" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \;
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
notify:
|
||||
webhooks:
|
||||
# A list of hook hashes, containing the url field
|
||||
# gitter hook
|
||||
- url: https://webhooks.gitter.im/e/22e6bb4eb945dd61ba54
|
||||
+2
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-docs</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
@@ -14,6 +14,7 @@
|
||||
<properties>
|
||||
<docs.main>spring-cloud-netflix</docs.main>
|
||||
<main.basedir>${basedir}/..</main.basedir>
|
||||
<docs.whitelisted.branches>1.0.x,1.1.x</docs.whitelisted.branches>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
image::https://travis-ci.org/spring-cloud/spring-cloud-netflix.svg?branch=master[Build Status, link=https://travis-ci.org/spring-cloud/spring-cloud-netflix]
|
||||
image::https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/1.1.x.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/1.1.x"]
|
||||
image::https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/1.1.x/graph/badge.svg["Codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/1.1.x"]
|
||||
image::https://api.codacy.com/project/badge/Grade/a6885a06921e4f72a0df0b7aabd6d118?branch=1.1.x["Codacy code quality", link="https://www.codacy.com/app/Spring-Cloud/spring-cloud-netflix?branch=1.1.x&utm_source=github.com&utm_medium=referral&utm_content=spring-cloud/spring-cloud-netflix&utm_campaign=Badge_Grade&bid=3538621"]
|
||||
|
||||
include::intro.adoc[]
|
||||
|
||||
|
||||
@@ -1,54 +1,330 @@
|
||||
#!/bin/bash -x
|
||||
|
||||
git remote set-url --push origin `git config remote.origin.url | sed -e 's/^git:/https:/'`
|
||||
set -e
|
||||
|
||||
if ! (git remote set-branches --add origin gh-pages && git fetch -q); then
|
||||
echo "No gh-pages, so not syncing"
|
||||
exit 0
|
||||
fi
|
||||
# Set default props like MAVEN_PATH, ROOT_FOLDER etc.
|
||||
function set_default_props() {
|
||||
# The script should be executed from the root folder
|
||||
ROOT_FOLDER=`pwd`
|
||||
echo "Current folder is ${ROOT_FOLDER}"
|
||||
|
||||
if ! [ -d docs/target/generated-docs ]; then
|
||||
echo "No gh-pages sources in docs/target/generated-docs, so not syncing"
|
||||
exit 0
|
||||
fi
|
||||
if [[ ! -e "${ROOT_FOLDER}/.git" ]]; then
|
||||
echo "You're not in the root folder of the project!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Find name of current branch
|
||||
###################################################################
|
||||
branch=$TRAVIS_BRANCH
|
||||
[ "$branch" == "" ] && branch=`git rev-parse --abbrev-ref HEAD`
|
||||
target=.
|
||||
if [ "$branch" != "master" ]; then target=./$branch; mkdir -p $target; fi
|
||||
# Prop that will let commit the changes
|
||||
COMMIT_CHANGES="no"
|
||||
MAVEN_PATH=${MAVEN_PATH:-}
|
||||
echo "Path to Maven is [${MAVEN_PATH}]"
|
||||
REPO_NAME=${PWD##*/}
|
||||
echo "Repo name is [${REPO_NAME}]"
|
||||
SPRING_CLOUD_STATIC_REPO=${SPRING_CLOUD_STATIC_REPO:-git@github.com:spring-cloud/spring-cloud-static.git}
|
||||
echo "Spring Cloud Static repo is [${SPRING_CLOUD_STATIC_REPO}"
|
||||
}
|
||||
|
||||
# Check if gh-pages exists and docs have been built
|
||||
function check_if_anything_to_sync() {
|
||||
git remote set-url --push origin `git config remote.origin.url | sed -e 's/^git:/https:/'`
|
||||
|
||||
if ! (git remote set-branches --add origin gh-pages && git fetch -q); then
|
||||
echo "No gh-pages, so not syncing"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! [ -d docs/target/generated-docs ] && ! [ "${BUILD}" == "yes" ]; then
|
||||
echo "No gh-pages sources in docs/target/generated-docs, so not syncing"
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
function retrieve_current_branch() {
|
||||
# Code getting the name of the current branch. For master we want to publish as we did until now
|
||||
# http://stackoverflow.com/questions/1593051/how-to-programmatically-determine-the-current-checked-out-git-branch
|
||||
# If there is a branch already passed will reuse it - otherwise will try to find it
|
||||
CURRENT_BRANCH=${BRANCH}
|
||||
if [[ -z "${CURRENT_BRANCH}" ]] ; then
|
||||
CURRENT_BRANCH=$(git symbolic-ref -q HEAD)
|
||||
CURRENT_BRANCH=${CURRENT_BRANCH##refs/heads/}
|
||||
CURRENT_BRANCH=${CURRENT_BRANCH:-HEAD}
|
||||
fi
|
||||
echo "Current branch is [${CURRENT_BRANCH}]"
|
||||
git checkout ${CURRENT_BRANCH} || echo "Failed to check the branch... continuing with the script"
|
||||
}
|
||||
|
||||
# Switches to the provided value of the release version. We always prefix it with `v`
|
||||
function switch_to_tag() {
|
||||
git checkout v${VERSION}
|
||||
}
|
||||
|
||||
# Build the docs if switch is on
|
||||
function build_docs_if_applicable() {
|
||||
if [[ "${BUILD}" == "yes" ]] ; then
|
||||
./mvnw clean install -P docs -pl docs -DskipTests
|
||||
fi
|
||||
}
|
||||
|
||||
# Get the name of the `docs.main` property
|
||||
# Get whitelisted branches - assumes that a `docs` module is available under `docs` profile
|
||||
function retrieve_doc_properties() {
|
||||
MAIN_ADOC_VALUE=$("${MAVEN_PATH}"mvn -q \
|
||||
-Dexec.executable="echo" \
|
||||
-Dexec.args='${docs.main}' \
|
||||
--non-recursive \
|
||||
org.codehaus.mojo:exec-maven-plugin:1.3.1:exec)
|
||||
echo "Extracted 'main.adoc' from Maven build [${MAIN_ADOC_VALUE}]"
|
||||
|
||||
|
||||
WHITELIST_PROPERTY=${WHITELIST_PROPERTY:-"docs.whitelisted.branches"}
|
||||
WHITELISTED_BRANCHES_VALUE=$("${MAVEN_PATH}"mvn -q \
|
||||
-Dexec.executable="echo" \
|
||||
-Dexec.args="\${${WHITELIST_PROPERTY}}" \
|
||||
org.codehaus.mojo:exec-maven-plugin:1.3.1:exec \
|
||||
-P docs \
|
||||
-pl docs)
|
||||
echo "Extracted '${WHITELIST_PROPERTY}' from Maven build [${WHITELISTED_BRANCHES_VALUE}]"
|
||||
}
|
||||
|
||||
# Stash any outstanding changes
|
||||
###################################################################
|
||||
git diff-index --quiet HEAD
|
||||
dirty=$?
|
||||
if [ "$dirty" != "0" ]; then git stash; fi
|
||||
function stash_changes() {
|
||||
git diff-index --quiet HEAD && dirty=$? || (echo "Failed to check if the current repo is dirty. Assuming that it is." && dirty="1")
|
||||
if [ "$dirty" != "0" ]; then git stash; fi
|
||||
}
|
||||
|
||||
# Switch to gh-pages branch to sync it with current branch
|
||||
###################################################################
|
||||
git checkout gh-pages
|
||||
|
||||
for f in docs/target/generated-docs/*; do
|
||||
file=${f#docs/target/generated-docs/*}
|
||||
if ! git ls-files -i -o --exclude-standard --directory | grep -q ^$file$; then
|
||||
# Not ignored...
|
||||
cp -rf $f $target
|
||||
git add -A $target/$file
|
||||
function add_docs_from_target() {
|
||||
local DESTINATION_REPO_FOLDER
|
||||
if [[ -z "${DESTINATION}" && -z "${CLONE}" ]] ; then
|
||||
DESTINATION_REPO_FOLDER=${ROOT_FOLDER}
|
||||
elif [[ "${CLONE}" == "yes" ]]; then
|
||||
mkdir -p ${ROOT_FOLDER}/target
|
||||
local clonedStatic=${ROOT_FOLDER}/target/spring-cloud-static
|
||||
if [[ ! -e "${clonedStatic}/.git" ]]; then
|
||||
echo "Cloning Spring Cloud Static to target"
|
||||
git clone ${SPRING_CLOUD_STATIC_REPO} ${clonedStatic} && git checkout gh-pages
|
||||
else
|
||||
echo "Spring Cloud Static already cloned - will pull changes"
|
||||
cd ${clonedStatic} && git checkout gh-pages && git pull origin gh-pages
|
||||
fi
|
||||
DESTINATION_REPO_FOLDER=${clonedStatic}/${REPO_NAME}
|
||||
mkdir -p ${DESTINATION_REPO_FOLDER}
|
||||
else
|
||||
if [[ ! -e "${DESTINATION}/.git" ]]; then
|
||||
echo "[${DESTINATION}] is not a git repository"
|
||||
exit 1
|
||||
fi
|
||||
DESTINATION_REPO_FOLDER=${DESTINATION}/${REPO_NAME}
|
||||
mkdir -p ${DESTINATION_REPO_FOLDER}
|
||||
echo "Destination was provided [${DESTINATION}]"
|
||||
fi
|
||||
cd ${DESTINATION_REPO_FOLDER}
|
||||
git checkout gh-pages
|
||||
git pull origin gh-pages
|
||||
|
||||
# Add git branches
|
||||
###################################################################
|
||||
if [[ -z "${VERSION}" ]] ; then
|
||||
copy_docs_for_current_version
|
||||
else
|
||||
copy_docs_for_provided_version
|
||||
fi
|
||||
commit_changes_if_applicable
|
||||
}
|
||||
|
||||
|
||||
# Copies the docs by using the retrieved properties from Maven build
|
||||
function copy_docs_for_current_version() {
|
||||
if [[ "${CURRENT_BRANCH}" == "master" ]] ; then
|
||||
echo -e "Current branch is master - will copy the current docs only to the root folder"
|
||||
for f in docs/target/generated-docs/*; do
|
||||
file=${f#docs/target/generated-docs/*}
|
||||
if ! git ls-files -i -o --exclude-standard --directory | grep -q ^$file$; then
|
||||
# Not ignored...
|
||||
cp -rf $f ${ROOT_FOLDER}/
|
||||
git add -A ${ROOT_FOLDER}/$file
|
||||
fi
|
||||
done
|
||||
COMMIT_CHANGES="yes"
|
||||
else
|
||||
echo -e "Current branch is [${CURRENT_BRANCH}]"
|
||||
# http://stackoverflow.com/questions/29300806/a-bash-script-to-check-if-a-string-is-present-in-a-comma-separated-list-of-strin
|
||||
if [[ ",${WHITELISTED_BRANCHES_VALUE}," = *",${CURRENT_BRANCH},"* ]] ; then
|
||||
mkdir -p ${ROOT_FOLDER}/${CURRENT_BRANCH}
|
||||
echo -e "Branch [${CURRENT_BRANCH}] is whitelisted! Will copy the current docs to the [${CURRENT_BRANCH}] folder"
|
||||
for f in docs/target/generated-docs/*; do
|
||||
file=${f#docs/target/generated-docs/*}
|
||||
if ! git ls-files -i -o --exclude-standard --directory | grep -q ^$file$; then
|
||||
# Not ignored...
|
||||
# We want users to access 1.0.0.RELEASE/ instead of 1.0.0.RELEASE/spring-cloud.sleuth.html
|
||||
if [[ "${file}" == "${MAIN_ADOC_VALUE}.html" ]] ; then
|
||||
# We don't want to copy the spring-cloud-sleuth.html
|
||||
# we want it to be converted to index.html
|
||||
cp -rf $f ${ROOT_FOLDER}/${CURRENT_BRANCH}/index.html
|
||||
git add -A ${ROOT_FOLDER}/${CURRENT_BRANCH}/index.html
|
||||
else
|
||||
cp -rf $f ${ROOT_FOLDER}/${CURRENT_BRANCH}
|
||||
git add -A ${ROOT_FOLDER}/${CURRENT_BRANCH}/$file
|
||||
fi
|
||||
fi
|
||||
done
|
||||
COMMIT_CHANGES="yes"
|
||||
else
|
||||
echo -e "Branch [${CURRENT_BRANCH}] is not on the white list! Check out the Maven [${WHITELIST_PROPERTY}] property in
|
||||
[docs] module available under [docs] profile. Won't commit any changes to gh-pages for this branch."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Copies the docs by using the explicitly provided version
|
||||
function copy_docs_for_provided_version() {
|
||||
local FOLDER=${DESTINATION_REPO_FOLDER}/${VERSION}
|
||||
mkdir -p ${FOLDER}
|
||||
echo -e "Current tag is [v${VERSION}] Will copy the current docs to the [${FOLDER}] folder"
|
||||
for f in ${ROOT_FOLDER}/docs/target/generated-docs/*; do
|
||||
file=${f#${ROOT_FOLDER}/docs/target/generated-docs/*}
|
||||
copy_docs_for_branch ${file} ${FOLDER}
|
||||
done
|
||||
COMMIT_CHANGES="yes"
|
||||
CURRENT_BRANCH="v${VERSION}"
|
||||
}
|
||||
|
||||
# Copies the docs from target to the provided destination
|
||||
# Params:
|
||||
# $1 - file from target
|
||||
# $2 - destination to which copy the files
|
||||
function copy_docs_for_branch() {
|
||||
local file=$1
|
||||
local destination=$2
|
||||
if ! git ls-files -i -o --exclude-standard --directory | grep -q ^${file}$; then
|
||||
# Not ignored...
|
||||
# We want users to access 1.0.0.RELEASE/ instead of 1.0.0.RELEASE/spring-cloud.sleuth.html
|
||||
if [[ ("${file}" == "${MAIN_ADOC_VALUE}.html") || ("${file}" == "${REPO_NAME}.html") ]] ; then
|
||||
# We don't want to copy the spring-cloud-sleuth.html
|
||||
# we want it to be converted to index.html
|
||||
cp -rf $f ${destination}/index.html
|
||||
git add -A ${destination}/index.html
|
||||
else
|
||||
cp -rf $f ${destination}
|
||||
git add -A ${destination}/$file
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function commit_changes_if_applicable() {
|
||||
if [[ "${COMMIT_CHANGES}" == "yes" ]] ; then
|
||||
COMMIT_SUCCESSFUL="no"
|
||||
git commit -a -m "Sync docs from ${CURRENT_BRANCH} to gh-pages" && COMMIT_SUCCESSFUL="yes" || echo "Failed to commit changes"
|
||||
|
||||
# Uncomment the following push if you want to auto push to
|
||||
# the gh-pages branch whenever you commit to master locally.
|
||||
# This is a little extreme. Use with care!
|
||||
###################################################################
|
||||
if [[ "${COMMIT_SUCCESSFUL}" == "yes" ]] ; then
|
||||
git push origin gh-pages
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Switch back to the previous branch and exit block
|
||||
function checkout_previous_branch() {
|
||||
# If -version was provided we need to come back to root project
|
||||
cd ${ROOT_FOLDER}
|
||||
git checkout ${CURRENT_BRANCH} || echo "Failed to check the branch... continuing with the script"
|
||||
if [ "$dirty" != "0" ]; then git stash pop; fi
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Assert if properties have been properly passed
|
||||
function assert_properties() {
|
||||
echo "VERSION [${VERSION}], DESTINATION [${DESTINATION}], CLONE [${CLONE}]"
|
||||
if [[ "${VERSION}" != "" && (-z "${DESTINATION}" && -z "${CLONE}") ]] ; then echo "Version was set but destination / clone was not!"; exit 1;fi
|
||||
if [[ ("${DESTINATION}" != "" && "${CLONE}" != "") && -z "${VERSION}" ]] ; then echo "Destination / clone was set but version was not!"; exit 1;fi
|
||||
if [[ "${DESTINATION}" != "" && "${CLONE}" == "yes" ]] ; then echo "Destination and clone was set. Pick one!"; exit 1;fi
|
||||
}
|
||||
|
||||
# Prints the usage
|
||||
function print_usage() {
|
||||
cat <<EOF
|
||||
The idea of this script is to update gh-pages branch with the generated docs. Without any options
|
||||
the script will work in the following manner:
|
||||
|
||||
- if there's no gh-pages / target for docs module then the script ends
|
||||
- for master branch the generated docs are copied to the root of gh-pages branch
|
||||
- for any other branch (if that branch is whitelisted) a subfolder with branch name is created
|
||||
and docs are copied there
|
||||
- if the version switch is passed (-v) then a tag with (v) prefix will be retrieved and a folder
|
||||
with that version number will be created in the gh-pages branch. WARNING! No whitelist verification will take place
|
||||
- if the destination switch is passed (-d) then the script will check if the provided dir is a git repo and then will
|
||||
switch to gh-pages of that repo and copy the generated docs to `docs/<project-name>/<version>`
|
||||
- if the destination switch is passed (-d) then the script will check if the provided dir is a git repo and then will
|
||||
switch to gh-pages of that repo and copy the generated docs to `docs/<project-name>/<version>`
|
||||
|
||||
USAGE:
|
||||
|
||||
You can use the following options:
|
||||
|
||||
-v|--version - the script will apply the whole procedure for a particular library version
|
||||
-d|--destination - the root of destination folder where the docs should be copied. You have to use the full path.
|
||||
E.g. point to spring-cloud-static folder. Can't be used with (-c)
|
||||
-b|--build - will run the standard build process after checking out the branch
|
||||
-c|--clone - will automatically clone the spring-cloud-static repo instead of providing the destination.
|
||||
Obviously can't be used with (-d)
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
# ==========================================
|
||||
# ____ ____ _____ _____ _____ _______
|
||||
# / ____|/ ____| __ \|_ _| __ \__ __|
|
||||
# | (___ | | | |__) | | | | |__) | | |
|
||||
# \___ \| | | _ / | | | ___/ | |
|
||||
# ____) | |____| | \ \ _| |_| | | |
|
||||
# |_____/ \_____|_| \_\_____|_| |_|
|
||||
#
|
||||
# ==========================================
|
||||
|
||||
while [[ $# > 0 ]]
|
||||
do
|
||||
key="$1"
|
||||
case ${key} in
|
||||
-v|--version)
|
||||
VERSION="$2"
|
||||
shift # past argument
|
||||
;;
|
||||
-d|--destination)
|
||||
DESTINATION="$2"
|
||||
shift # past argument
|
||||
;;
|
||||
-b|--build)
|
||||
BUILD="yes"
|
||||
;;
|
||||
-c|--clone)
|
||||
CLONE="yes"
|
||||
;;
|
||||
-h|--help)
|
||||
print_usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Invalid option: [$1]"
|
||||
print_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift # past argument or value
|
||||
done
|
||||
|
||||
git add -A README.adoc || echo "No change to README.adoc"
|
||||
git commit -a -m "Sync docs from $branch to gh-pages" || echo "Nothing committed"
|
||||
|
||||
# Uncomment the following push if you want to auto push to
|
||||
# the gh-pages branch whenever you commit to branch locally.
|
||||
# This is a little extreme. Use with care!
|
||||
###################################################################
|
||||
git push origin gh-pages || echo "Cannot push gh-pages"
|
||||
|
||||
# Finally, switch back to the current branch and exit block
|
||||
git checkout $branch
|
||||
if [ "$dirty" != "0" ]; then git stash pop; fi
|
||||
|
||||
exit 0
|
||||
assert_properties
|
||||
set_default_props
|
||||
check_if_anything_to_sync
|
||||
if [[ -z "${VERSION}" ]] ; then
|
||||
retrieve_current_branch
|
||||
else
|
||||
switch_to_tag
|
||||
fi
|
||||
build_docs_if_applicable
|
||||
retrieve_doc_properties
|
||||
stash_changes
|
||||
add_docs_from_target
|
||||
checkout_previous_branch
|
||||
@@ -3,6 +3,7 @@
|
||||
:github-raw: http://raw.github.com/{github-repo}/{github-tag}
|
||||
:github-code: http://github.com/{github-repo}/tree/{github-tag}
|
||||
:all: {asterisk}{asterisk}
|
||||
:nofooter:
|
||||
= Spring Cloud Netflix
|
||||
|
||||
include::intro.adoc[]
|
||||
@@ -119,7 +120,7 @@ respectively. This will make Eureka publish instance information
|
||||
showing an explicit preference for secure communication. The Spring
|
||||
Cloud `DiscoveryClient` will always return an `https://...` URI for a
|
||||
service configured this way, and the Eureka (native) instance
|
||||
information will have a secure health check URL.
|
||||
information will have a secure health check URL.
|
||||
|
||||
Because of the way
|
||||
Eureka works internally, it will still publish a non-secure URL for
|
||||
@@ -424,10 +425,37 @@ on a machine that knows its own hostname (it is looked up using
|
||||
`java.net.InetAddress` by default).
|
||||
|
||||
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 will synchronize
|
||||
the registrations amongst themselves. If the peers are physically
|
||||
separated (inside a data centre or between multiple data centres) then
|
||||
the system can in principle survive split-brain type failures.
|
||||
directly connected to each other, they will synchronize
|
||||
the registrations amongst themselves.
|
||||
|
||||
.application.yml (Three Peer Aware Eureka Servers)
|
||||
----
|
||||
eureka:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://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
|
||||
----
|
||||
|
||||
=== Prefer IP Address
|
||||
|
||||
@@ -1070,7 +1098,7 @@ failures will show up in Hystrix metrics, and once the circuit is open
|
||||
the proxy will not try to contact the service.
|
||||
|
||||
NOTE: the Zuul starter does not include a discovery client, so for
|
||||
routes based on service IDs you need to provide one of those
|
||||
routes based on service IDs you need to provide one of those
|
||||
on the classpath as well (e.g. Eureka is one choice).
|
||||
|
||||
To skip having a service automatically added, set
|
||||
|
||||
@@ -46,15 +46,6 @@ if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||
|
||||
fi
|
||||
|
||||
VERSION=$(awk '/<parent/ {l=4}; (l-- > 0) {$0=$0} 1' `dirname $0`/pom.xml| grep '<version' | head -1 | sed -e 's/.*>\(.*\)<.*/\1/')
|
||||
if echo $VERSION | egrep -q 'M|RC'; then
|
||||
echo Activating \"milestone\" profile for version=\"$VERSION\"
|
||||
echo $MAVEN_ARGS | grep -q milestone || MAVEN_ARGS="$MAVEN_ARGS -Pmilestone"
|
||||
else
|
||||
echo Deactivating \"milestone\" profile for version=\"$VERSION\"
|
||||
echo $MAVEN_ARGS | grep -q milestone && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pmilestone//')
|
||||
fi
|
||||
|
||||
# OS specific support. $var _must_ be set to either true or false.
|
||||
cygwin=false;
|
||||
darwin=false;
|
||||
@@ -66,27 +57,27 @@ case "`uname`" in
|
||||
#
|
||||
# Look for the Apple JDKs first to preserve the existing behaviour, and then look
|
||||
# for the new JDKs provided by Oracle.
|
||||
#
|
||||
#
|
||||
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
|
||||
#
|
||||
# Apple JDKs
|
||||
#
|
||||
export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
|
||||
fi
|
||||
|
||||
|
||||
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
|
||||
#
|
||||
# Apple JDKs
|
||||
#
|
||||
export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
|
||||
fi
|
||||
|
||||
|
||||
if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
|
||||
#
|
||||
# Oracle JDKs
|
||||
#
|
||||
export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
|
||||
#
|
||||
@@ -228,16 +219,30 @@ concat_lines() {
|
||||
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)}
|
||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||
|
||||
# Provide a "standardized" way to retrieve the CLI args that will
|
||||
# Provide a "standardized" way to retrieve the CLI args that will
|
||||
# work with both Windows and non-Windows executions.
|
||||
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
|
||||
export MAVEN_CMD_LINE_ARGS
|
||||
|
||||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
VERSION=$(exec "$JAVACMD" \
|
||||
$MAVEN_OPTS \
|
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||
-Dexec.executable="echo" -Dexec.args='${project.version}' ${WRAPPER_LAUNCHER} -q --non-recursive org.codehaus.mojo:exec-maven-plugin:1.3.1:exec )
|
||||
|
||||
if echo $VERSION | egrep -q 'M|RC'; then
|
||||
echo Activating \"milestone\" profile for version=\"$VERSION\"
|
||||
echo $MAVEN_ARGS | grep -q milestone || MAVEN_ARGS="$MAVEN_ARGS -Pmilestone"
|
||||
else
|
||||
echo Deactivating \"milestone\" profile for version=\"$VERSION\"
|
||||
echo $MAVEN_ARGS | grep -q milestone && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pmilestone//')
|
||||
fi
|
||||
|
||||
|
||||
exec "$JAVACMD" \
|
||||
$MAVEN_OPTS \
|
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||
${WRAPPER_LAUNCHER} ${MAVEN_ARGS} "$@"
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-build</artifactId>
|
||||
<version>1.1.1.RELEASE</version>
|
||||
<version>1.1.2.RELEASE</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Spring Cloud Netflix</name>
|
||||
<description>Spring Cloud Netflix</description>
|
||||
@@ -24,13 +24,6 @@
|
||||
<main.basedir>${basedir}</main.basedir>
|
||||
<netty.version>4.0.27.Final</netty.version>
|
||||
<jackson.version>2.7.3</jackson.version>
|
||||
|
||||
<!-- Sonar -->
|
||||
<surefire.plugin.version>2.19.1</surefire.plugin.version>
|
||||
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
|
||||
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
|
||||
<sonar.jacoco.reportPath>${project.basedir}/../target/jacoco.exec</sonar.jacoco.reportPath>
|
||||
<sonar.language>java</sonar.language>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
@@ -177,52 +170,45 @@
|
||||
</profile>
|
||||
<profile>
|
||||
<id>sonar</id>
|
||||
<activation>
|
||||
<activeByDefault>false</activeByDefault>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.7.4.201502262128</version>
|
||||
<configuration>
|
||||
<destFile>${sonar.jacoco.reportPath}</destFile>
|
||||
<append>true</append>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>agent</id>
|
||||
<id>pre-unit-test</id>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<propertyName>surefireArgLine</propertyName>
|
||||
<destFile>${project.build.directory}/jacoco.exec</destFile>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>post-unit-test</id>
|
||||
<phase>test</phase>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<!-- Sets the path to the file which contains the execution data. -->
|
||||
<dataFile>${project.build.directory}/jacoco.exec</dataFile>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<!-- Minimal supported version is 2.4 -->
|
||||
<version>${surefire.plugin.version}</version>
|
||||
<configuration>
|
||||
<properties>
|
||||
<property>
|
||||
<name>listener</name>
|
||||
<value>org.sonar.java.jacoco.JUnitListener</value>
|
||||
</property>
|
||||
</properties>
|
||||
<!-- Sets the VM argument line used when unit tests are run. -->
|
||||
<argLine>${surefireArgLine}</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.sonarsource.java</groupId>
|
||||
<artifactId>sonar-jacoco-listeners</artifactId>
|
||||
<version>3.8</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-core</artifactId>
|
||||
|
||||
+6
-4
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.netflix.endpoint;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
@@ -60,8 +61,8 @@ public abstract class ServletWrappingEndpoint implements InitializingBean,
|
||||
this.controller.setApplicationContext(applicationContext);
|
||||
}
|
||||
|
||||
protected ServletWrappingEndpoint(Class<?> servletClass, String servletName,
|
||||
String path, boolean sensitive, boolean enabled) {
|
||||
protected ServletWrappingEndpoint(Class<? extends Servlet> servletClass,
|
||||
String servletName, String path, boolean sensitive, boolean enabled) {
|
||||
this.controller.setServletClass(servletClass);
|
||||
this.controller.setServletName(servletName);
|
||||
this.path = path;
|
||||
@@ -76,12 +77,13 @@ public abstract class ServletWrappingEndpoint implements InitializingBean,
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public ServletWrappingController getController() {
|
||||
return controller;
|
||||
return this.controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return this.path;
|
||||
|
||||
+13
-12
@@ -40,6 +40,7 @@ import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.ClassMetadata;
|
||||
@@ -189,19 +190,12 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
||||
}
|
||||
|
||||
private void validate(Map<String, Object> attributes) {
|
||||
if (StringUtils.hasText((String) attributes.get("value"))) {
|
||||
Assert.isTrue(!StringUtils.hasText((String) attributes.get("name")),
|
||||
"Either name or value can be specified, but not both");
|
||||
Assert.isTrue(!StringUtils.hasText((String) attributes.get("serviceId")),
|
||||
"Either name (serviceId) or value can be specified, but not both");
|
||||
}
|
||||
if (StringUtils.hasText((String) attributes.get("name"))) {
|
||||
Assert.isTrue(!StringUtils.hasText((String) attributes.get("serviceId")),
|
||||
"Either name or serviceId can be specified, but not both");
|
||||
}
|
||||
AnnotationAttributes annotation = AnnotationAttributes.fromMap(attributes);
|
||||
// This blows up if an aliased property is overspecified
|
||||
annotation.getAliasedString("name", FeignClient.class, null);
|
||||
}
|
||||
|
||||
private String getName(Map<String, Object> attributes) {
|
||||
/* for testing */ String getName(Map<String, Object> attributes) {
|
||||
String name = (String) attributes.get("serviceId");
|
||||
if (!StringUtils.hasText(name)) {
|
||||
name = (String) attributes.get("name");
|
||||
@@ -216,7 +210,14 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
||||
|
||||
String host = null;
|
||||
try {
|
||||
host = new URI("http://" + name).getHost();
|
||||
String url;
|
||||
if (!name.startsWith("http://") && !name.startsWith("https://")) {
|
||||
url = "http://" + name;
|
||||
} else {
|
||||
url = name;
|
||||
}
|
||||
host = new URI(url).getHost();
|
||||
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
}
|
||||
|
||||
+5
-9
@@ -23,7 +23,6 @@ import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import com.netflix.client.AbstractLoadBalancerAwareClient;
|
||||
import com.netflix.client.ClientException;
|
||||
@@ -42,6 +41,8 @@ import feign.RequestTemplate;
|
||||
import feign.Response;
|
||||
import feign.Util;
|
||||
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttpsIfNeeded;
|
||||
|
||||
public class FeignLoadBalancer extends
|
||||
AbstractLoadBalancerAwareClient<FeignLoadBalancer.RibbonRequest, FeignLoadBalancer.RibbonResponse> {
|
||||
|
||||
@@ -98,13 +99,8 @@ public class FeignLoadBalancer extends
|
||||
|
||||
@Override
|
||||
public URI reconstructURIWithServer(Server server, URI original) {
|
||||
String scheme = original.getScheme();
|
||||
if (!"https".equals(scheme) && (this.serverIntrospector.isSecure(server)
|
||||
|| this.clientConfig.get(CommonClientConfigKey.IsSecure, false))) {
|
||||
original = UriComponentsBuilder.fromUri(original).scheme("https").build()
|
||||
.toUri();
|
||||
}
|
||||
return super.reconstructURIWithServer(server, original);
|
||||
URI uri = updateToHttpsIfNeeded(original, this.clientConfig, this.serverIntrospector, server);
|
||||
return super.reconstructURIWithServer(server, uri);
|
||||
}
|
||||
|
||||
static class RibbonRequest extends ClientRequest implements Cloneable {
|
||||
@@ -190,4 +186,4 @@ public class FeignLoadBalancer extends
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+8
-9
@@ -51,7 +51,8 @@ import com.sun.jersey.api.client.Client;
|
||||
import com.sun.jersey.client.apache4.ApacheHttpClient4;
|
||||
|
||||
import static com.netflix.client.config.CommonClientConfigKey.DeploymentContextBasedVipAddresses;
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonProperyUtils.setRibbonProperty;
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.setRibbonProperty;
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttpsIfNeeded;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
@@ -167,23 +168,21 @@ public class RibbonClientConfiguration {
|
||||
|
||||
static class OverrideRestClient extends RestClient {
|
||||
|
||||
private IClientConfig config;
|
||||
private ServerIntrospector serverIntrospector;
|
||||
|
||||
protected OverrideRestClient(IClientConfig ncc,
|
||||
protected OverrideRestClient(IClientConfig config,
|
||||
ServerIntrospector serverIntrospector) {
|
||||
super();
|
||||
this.config = config;
|
||||
this.serverIntrospector = serverIntrospector;
|
||||
initWithNiwsConfig(ncc);
|
||||
initWithNiwsConfig(this.config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI reconstructURIWithServer(Server server, URI original) {
|
||||
String scheme = original.getScheme();
|
||||
if (!"https".equals(scheme) && this.serverIntrospector.isSecure(server)) {
|
||||
original = UriComponentsBuilder.fromUri(original).scheme("https").build()
|
||||
.toUri();
|
||||
}
|
||||
return super.reconstructURIWithServer(server, original);
|
||||
URI uri = updateToHttpsIfNeeded(original, this.config, this.serverIntrospector, server);
|
||||
return super.reconstructURIWithServer(server, uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-6
@@ -29,7 +29,6 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
@@ -113,11 +112,8 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
|
||||
|
||||
private boolean isSecure(Server server, String serviceId) {
|
||||
IClientConfig config = this.clientFactory.getClientConfig(serviceId);
|
||||
if (config != null) {
|
||||
return config.get(CommonClientConfigKey.IsSecure, false);
|
||||
}
|
||||
|
||||
return serverIntrospector(serviceId).isSecure(server);
|
||||
ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
|
||||
return RibbonUtils.isSecure(config, serverIntrospector, server);
|
||||
}
|
||||
|
||||
protected Server getServer(String serviceId) {
|
||||
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
package org.springframework.cloud.netflix.ribbon;
|
||||
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.config.DynamicPropertyFactory;
|
||||
import com.netflix.config.DynamicStringProperty;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class RibbonProperyUtils {
|
||||
|
||||
public static final String VALUE_NOT_SET = "__not__set__";
|
||||
|
||||
public static final String DEFAULT_NAMESPACE = "ribbon";
|
||||
|
||||
public static void setRibbonProperty(String serviceId, String suffix, String value) {
|
||||
// how to set the namespace properly?
|
||||
String key = getRibbonKey(serviceId, suffix);
|
||||
DynamicStringProperty property = getProperty(key);
|
||||
if (property.get().equals(VALUE_NOT_SET)) {
|
||||
ConfigurationManager.getConfigInstance().setProperty(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static String getRibbonKey(String serviceId, String suffix) {
|
||||
return serviceId + "." + DEFAULT_NAMESPACE + "." + suffix;
|
||||
}
|
||||
|
||||
public static DynamicStringProperty getProperty(String key) {
|
||||
return DynamicPropertyFactory.getInstance().getStringProperty(key, VALUE_NOT_SET);
|
||||
}
|
||||
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package org.springframework.cloud.netflix.ribbon;
|
||||
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.client.config.IClientConfigKey;
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.config.DynamicPropertyFactory;
|
||||
import com.netflix.config.DynamicStringProperty;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class RibbonUtils {
|
||||
|
||||
public static final String VALUE_NOT_SET = "__not__set__";
|
||||
|
||||
public static final String DEFAULT_NAMESPACE = "ribbon";
|
||||
|
||||
public static void setRibbonProperty(String serviceId, String suffix, String value) {
|
||||
// how to set the namespace properly?
|
||||
String key = getRibbonKey(serviceId, suffix);
|
||||
DynamicStringProperty property = getProperty(key);
|
||||
if (property.get().equals(VALUE_NOT_SET)) {
|
||||
ConfigurationManager.getConfigInstance().setProperty(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static String getRibbonKey(String serviceId, String suffix) {
|
||||
return serviceId + "." + DEFAULT_NAMESPACE + "." + suffix;
|
||||
}
|
||||
|
||||
public static DynamicStringProperty getProperty(String key) {
|
||||
return DynamicPropertyFactory.getInstance().getStringProperty(key, VALUE_NOT_SET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if client is secure. If the supplied {@link IClientConfig} has the {@link CommonClientConfigKey#IsSecure}
|
||||
* set, return that value. Otherwise, query the supplied {@link ServerIntrospector}.
|
||||
* @param config the supplied client configuration.
|
||||
* @param serverIntrospector
|
||||
* @param server
|
||||
* @return true if the client is secure
|
||||
*/
|
||||
public static boolean isSecure(IClientConfig config, ServerIntrospector serverIntrospector, Server server) {
|
||||
if (config != null) {
|
||||
Boolean isSecure = config.get(CommonClientConfigKey.IsSecure);
|
||||
if (isSecure != null) {
|
||||
return isSecure;
|
||||
}
|
||||
}
|
||||
|
||||
return serverIntrospector.isSecure(server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the scheme to https if needed. If the uri doesn't start with https and
|
||||
* {@link #isSecure(IClientConfig, ServerIntrospector, Server)} is true, update the scheme.
|
||||
* This assumes the uri is already encoded to avoid double encoding.
|
||||
*
|
||||
* @param uri
|
||||
* @param config
|
||||
* @param serverIntrospector
|
||||
* @param server
|
||||
* @return
|
||||
*/
|
||||
public static URI updateToHttpsIfNeeded(URI uri, IClientConfig config, ServerIntrospector serverIntrospector, Server server) {
|
||||
String scheme = uri.getScheme();
|
||||
if (!"https".equals(scheme) && isSecure(config, serverIntrospector, server)) {
|
||||
return UriComponentsBuilder.fromUri(uri).scheme("https").build(true)
|
||||
.toUri();
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
}
|
||||
+16
-1
@@ -37,6 +37,7 @@ import lombok.Getter;
|
||||
public class RibbonApacheHttpRequest extends ClientRequest implements Cloneable {
|
||||
|
||||
private final String method;
|
||||
private Long contentLength;
|
||||
|
||||
private final MultiValueMap<String, String> headers;
|
||||
|
||||
@@ -44,11 +45,18 @@ public class RibbonApacheHttpRequest extends ClientRequest implements Cloneable
|
||||
|
||||
private final InputStream requestEntity;
|
||||
|
||||
public RibbonApacheHttpRequest(final String method, final URI uri,
|
||||
final Boolean retryable, final MultiValueMap<String, String> headers,
|
||||
final MultiValueMap<String, String> params, final InputStream requestEntity) {
|
||||
this(method, uri, retryable, headers, params, requestEntity, null);
|
||||
}
|
||||
|
||||
public RibbonApacheHttpRequest(final String method, final URI uri,
|
||||
final Boolean retryable, final MultiValueMap<String, String> headers,
|
||||
final MultiValueMap<String, String> params, final InputStream requestEntity) {
|
||||
final MultiValueMap<String, String> params, final InputStream requestEntity, Long contentLength) {
|
||||
|
||||
this.method = method;
|
||||
this.contentLength = contentLength;
|
||||
this.uri = uri;
|
||||
this.isRetriable = retryable;
|
||||
this.headers = headers;
|
||||
@@ -77,6 +85,13 @@ public class RibbonApacheHttpRequest extends ClientRequest implements Cloneable
|
||||
final BasicHttpEntity entity;
|
||||
entity = new BasicHttpEntity();
|
||||
entity.setContent(this.requestEntity);
|
||||
// if the entity contentLength isn't set, transfer-encoding will be set
|
||||
// to chunked in org.apache.http.protocol.RequestContent. See gh-1042
|
||||
if (contentLength != null) {
|
||||
entity.setContentLength(this.contentLength);
|
||||
} else if ("GET".equals(this.method)) {
|
||||
entity.setContentLength(0);
|
||||
}
|
||||
builder.setEntity(entity);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ import com.netflix.loadbalancer.ILoadBalancer;
|
||||
public class RibbonLoadBalancingHttpClient
|
||||
extends
|
||||
AbstractLoadBalancerAwareClient<RibbonApacheHttpRequest, RibbonApacheHttpResponse> {
|
||||
private final HttpClient delegate = HttpClientBuilder.create().build();
|
||||
private final HttpClient delegate = HttpClientBuilder.create().disableCookieManagement().build();
|
||||
|
||||
private int connectTimeout;
|
||||
|
||||
|
||||
+29
-14
@@ -21,6 +21,7 @@ import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.actuate.trace.TraceRepository;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.cloud.client.actuator.HasFeatures;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
|
||||
@@ -30,6 +31,7 @@ import org.springframework.cloud.client.discovery.event.ParentHeartbeatEvent;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
|
||||
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
|
||||
import org.springframework.cloud.netflix.zuul.filters.TraceProxyRequestHelper;
|
||||
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
|
||||
import org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocator;
|
||||
import org.springframework.cloud.netflix.zuul.filters.discovery.ServiceRouteMapper;
|
||||
@@ -52,9 +54,6 @@ import org.springframework.context.annotation.Configuration;
|
||||
@Configuration
|
||||
public class ZuulProxyConfiguration extends ZuulConfiguration {
|
||||
|
||||
@Autowired(required = false)
|
||||
private TraceRepository traces;
|
||||
|
||||
@Autowired
|
||||
private SpringClientFactory clientFactory;
|
||||
|
||||
@@ -108,17 +107,6 @@ public class ZuulProxyConfiguration extends ZuulConfiguration {
|
||||
return new SimpleHostRoutingFilter(helper, zuulProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ProxyRequestHelper proxyRequestHelper() {
|
||||
ProxyRequestHelper helper = new ProxyRequestHelper();
|
||||
if (this.traces != null) {
|
||||
helper.setTraces(this.traces);
|
||||
}
|
||||
helper.setIgnoredHeaders(this.zuulProperties.getIgnoredHeaders());
|
||||
helper.setTraceRequestBody(this.zuulProperties.isTraceRequestBody());
|
||||
return helper;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApplicationListener<ApplicationEvent> zuulDiscoveryRefreshRoutesListener() {
|
||||
return new ZuulDiscoveryRefreshListener();
|
||||
@@ -130,15 +118,42 @@ public class ZuulProxyConfiguration extends ZuulConfiguration {
|
||||
return new SimpleServiceRouteMapper();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingClass("org.springframework.boot.actuate.endpoint.Endpoint")
|
||||
protected static class NoActuatorConfiguration {
|
||||
|
||||
@Bean
|
||||
public ProxyRequestHelper proxyRequestHelper(ZuulProperties zuulProperties) {
|
||||
ProxyRequestHelper helper = new ProxyRequestHelper();
|
||||
helper.setIgnoredHeaders(zuulProperties.getIgnoredHeaders());
|
||||
helper.setTraceRequestBody(zuulProperties.isTraceRequestBody());
|
||||
return helper;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(Endpoint.class)
|
||||
protected static class RoutesEndpointConfiguration {
|
||||
|
||||
@Autowired(required = false)
|
||||
private TraceRepository traces;
|
||||
|
||||
@Bean
|
||||
public RoutesEndpoint zuulEndpoint(RouteLocator routeLocator) {
|
||||
return new RoutesEndpoint(routeLocator);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ProxyRequestHelper proxyRequestHelper(ZuulProperties zuulProperties) {
|
||||
TraceProxyRequestHelper helper = new TraceProxyRequestHelper();
|
||||
if (this.traces != null) {
|
||||
helper.setTraces(this.traces);
|
||||
}
|
||||
helper.setIgnoredHeaders(zuulProperties.getIgnoredHeaders());
|
||||
helper.setTraceRequestBody(zuulProperties.isTraceRequestBody());
|
||||
return helper;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ZuulDiscoveryRefreshListener
|
||||
|
||||
+5
-72
@@ -16,10 +16,11 @@
|
||||
|
||||
package org.springframework.cloud.netflix.zuul.filters;
|
||||
|
||||
import static org.springframework.http.HttpHeaders.CONTENT_ENCODING;
|
||||
import static org.springframework.http.HttpHeaders.CONTENT_LENGTH;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
@@ -33,7 +34,6 @@ import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.boot.actuate.trace.TraceRepository;
|
||||
import org.springframework.cloud.netflix.zuul.util.RequestUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
@@ -45,14 +45,12 @@ import org.springframework.web.util.WebUtils;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import com.netflix.zuul.util.HTTPRequestUtils;
|
||||
|
||||
import static org.springframework.http.HttpHeaders.CONTENT_ENCODING;
|
||||
import static org.springframework.http.HttpHeaders.CONTENT_LENGTH;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Marcos Barbero
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@CommonsLog
|
||||
public class ProxyRequestHelper {
|
||||
@@ -63,8 +61,6 @@ public class ProxyRequestHelper {
|
||||
*/
|
||||
public static final String IGNORED_HEADERS = "ignoredHeaders";
|
||||
|
||||
private TraceRepository traces;
|
||||
|
||||
private Set<String> ignoredHeaders = new LinkedHashSet<>();
|
||||
|
||||
private Set<String> sensitiveHeaders = new LinkedHashSet<>();
|
||||
@@ -85,10 +81,6 @@ public class ProxyRequestHelper {
|
||||
this.ignoredHeaders.addAll(ignoredHeaders);
|
||||
}
|
||||
|
||||
public void setTraces(TraceRepository traces) {
|
||||
this.traces = traces;
|
||||
}
|
||||
|
||||
public void setTraceRequestBody(boolean traceRequestBody) {
|
||||
this.traceRequestBody = traceRequestBody;
|
||||
}
|
||||
@@ -238,39 +230,10 @@ public class ProxyRequestHelper {
|
||||
MultiValueMap<String, String> headers, MultiValueMap<String, String> params,
|
||||
InputStream requestEntity) throws IOException {
|
||||
Map<String, Object> info = new LinkedHashMap<>();
|
||||
if (this.traces != null) {
|
||||
RequestContext context = RequestContext.getCurrentContext();
|
||||
info.put("method", verb);
|
||||
info.put("path", uri);
|
||||
info.put("query", getQueryString(params));
|
||||
info.put("remote", true);
|
||||
info.put("proxy", context.get("proxy"));
|
||||
Map<String, Object> trace = new LinkedHashMap<>();
|
||||
Map<String, Object> input = new LinkedHashMap<>();
|
||||
trace.put("request", input);
|
||||
info.put("headers", trace);
|
||||
for (Entry<String, List<String>> entry : headers.entrySet()) {
|
||||
Collection<String> collection = entry.getValue();
|
||||
Object value = collection;
|
||||
if (collection.size() < 2) {
|
||||
value = collection.isEmpty() ? "" : collection.iterator().next();
|
||||
}
|
||||
input.put(entry.getKey(), value);
|
||||
}
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
if (shouldDebugBody(ctx)) {
|
||||
// Prevent input stream from being read if it needs to go downstream
|
||||
if (requestEntity != null) {
|
||||
debugRequestEntity(info, ctx.getRequest().getInputStream());
|
||||
}
|
||||
}
|
||||
this.traces.add(info);
|
||||
return info;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/* for tests */ boolean shouldDebugBody(RequestContext ctx) {
|
||||
protected boolean shouldDebugBody(RequestContext ctx) {
|
||||
HttpServletRequest request = ctx.getRequest();
|
||||
if (!this.traceRequestBody || ctx.isChunkedRequestBody()
|
||||
|| RequestUtils.isZuulServletRequest()) {
|
||||
@@ -284,36 +247,6 @@ public class ProxyRequestHelper {
|
||||
|
||||
public void appendDebug(Map<String, Object> info, int status,
|
||||
MultiValueMap<String, String> headers) {
|
||||
if (this.traces != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> trace = (Map<String, Object>) info.get("headers");
|
||||
Map<String, Object> output = new LinkedHashMap<String, Object>();
|
||||
trace.put("response", output);
|
||||
for (Entry<String, List<String>> key : headers.entrySet()) {
|
||||
Collection<String> collection = key.getValue();
|
||||
Object value = collection;
|
||||
if (collection.size() < 2) {
|
||||
value = collection.isEmpty() ? "" : collection.iterator().next();
|
||||
}
|
||||
output.put(key.getKey(), value);
|
||||
}
|
||||
output.put("status", "" + status);
|
||||
}
|
||||
}
|
||||
|
||||
private void debugRequestEntity(Map<String, Object> info, InputStream inputStream)
|
||||
throws IOException {
|
||||
if (RequestContext.getCurrentContext().isChunkedRequestBody()) {
|
||||
info.put("body", "<chunked>");
|
||||
return;
|
||||
}
|
||||
char[] buffer = new char[4096];
|
||||
int count = new InputStreamReader(inputStream, Charset.forName("UTF-8"))
|
||||
.read(buffer, 0, buffer.length);
|
||||
if (count > 0) {
|
||||
String entity = new String(buffer).substring(0, count);
|
||||
info.put("body", entity.length() < 4096 ? entity : entity + "<truncated>");
|
||||
}
|
||||
}
|
||||
|
||||
public String getQueryString(MultiValueMap<String, String> params) {
|
||||
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.zuul.filters;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.boot.actuate.trace.TraceRepository;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@CommonsLog
|
||||
public class TraceProxyRequestHelper extends ProxyRequestHelper {
|
||||
|
||||
private TraceRepository traces;
|
||||
|
||||
public void setTraces(TraceRepository traces) {
|
||||
this.traces = traces;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> debug(String verb, String uri,
|
||||
MultiValueMap<String, String> headers, MultiValueMap<String, String> params,
|
||||
InputStream requestEntity) throws IOException {
|
||||
Map<String, Object> info = new LinkedHashMap<>();
|
||||
if (this.traces != null) {
|
||||
RequestContext context = RequestContext.getCurrentContext();
|
||||
info.put("method", verb);
|
||||
info.put("path", uri);
|
||||
info.put("query", getQueryString(params));
|
||||
info.put("remote", true);
|
||||
info.put("proxy", context.get("proxy"));
|
||||
Map<String, Object> trace = new LinkedHashMap<>();
|
||||
Map<String, Object> input = new LinkedHashMap<>();
|
||||
trace.put("request", input);
|
||||
info.put("headers", trace);
|
||||
debugHeaders(headers, input);
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
if (shouldDebugBody(ctx)) {
|
||||
// Prevent input stream from being read if it needs to go downstream
|
||||
if (requestEntity != null) {
|
||||
debugRequestEntity(info, ctx.getRequest().getInputStream());
|
||||
}
|
||||
}
|
||||
this.traces.add(info);
|
||||
return info;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
void debugHeaders(MultiValueMap<String, String> headers, Map<String, Object> map) {
|
||||
for (Entry<String, List<String>> entry : headers.entrySet()) {
|
||||
Collection<String> collection = entry.getValue();
|
||||
Object value = collection;
|
||||
if (collection.size() < 2) {
|
||||
value = collection.isEmpty() ? "" : collection.iterator().next();
|
||||
}
|
||||
map.put(entry.getKey(), value);
|
||||
}
|
||||
}
|
||||
|
||||
public void appendDebug(Map<String, Object> info, int status,
|
||||
MultiValueMap<String, String> headers) {
|
||||
if (this.traces != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> trace = (Map<String, Object>) info.get("headers");
|
||||
Map<String, Object> output = new LinkedHashMap<String, Object>();
|
||||
trace.put("response", output);
|
||||
debugHeaders(headers, output);
|
||||
output.put("status", "" + status);
|
||||
}
|
||||
}
|
||||
|
||||
private void debugRequestEntity(Map<String, Object> info, InputStream inputStream)
|
||||
throws IOException {
|
||||
if (RequestContext.getCurrentContext().isChunkedRequestBody()) {
|
||||
info.put("body", "<chunked>");
|
||||
return;
|
||||
}
|
||||
char[] buffer = new char[4096];
|
||||
int count = new InputStreamReader(inputStream, Charset.forName("UTF-8"))
|
||||
.read(buffer, 0, buffer.length);
|
||||
if (count > 0) {
|
||||
String entity = new String(buffer).substring(0, count);
|
||||
info.put("body", entity.length() < 4096 ? entity : entity + "<truncated>");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+7
-1
@@ -111,7 +111,7 @@ public class ZuulProperties {
|
||||
private boolean traceRequestBody = true;
|
||||
|
||||
/**
|
||||
* Flag to say that path elelents past the first semicolon can be dropped.
|
||||
* Flag to say that path elements past the first semicolon can be dropped.
|
||||
*/
|
||||
private boolean removeSemicolonContent = true;
|
||||
|
||||
@@ -125,6 +125,12 @@ public class ZuulProperties {
|
||||
private Set<String> sensitiveHeaders = new LinkedHashSet<>(
|
||||
Arrays.asList("Cookie", "Set-Cookie", "Authorization"));
|
||||
|
||||
/**
|
||||
* Flag to say whether the hostname for ssl connections should be verified or not. Default is true.
|
||||
* This should only be used in test setups!
|
||||
*/
|
||||
private boolean sslHostnameValidationEnabled =true;
|
||||
|
||||
public Set<String> getIgnoredHeaders() {
|
||||
Set<String> ignoredHeaders = new LinkedHashSet<>(this.ignoredHeaders);
|
||||
if (ClassUtils.isPresent(
|
||||
|
||||
+28
-6
@@ -53,11 +53,13 @@ import org.apache.http.config.Registry;
|
||||
import org.apache.http.config.RegistryBuilder;
|
||||
import org.apache.http.conn.socket.ConnectionSocketFactory;
|
||||
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
|
||||
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.InputStreamEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
@@ -66,7 +68,6 @@ import org.apache.http.protocol.HttpContext;
|
||||
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
|
||||
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
|
||||
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.Host;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -94,6 +95,8 @@ public class SimpleHostRoutingFilter extends ZuulFilter {
|
||||
private final Timer connectionManagerTimer = new Timer(
|
||||
"SimpleHostRoutingFilter.connectionManagerTimer", true);
|
||||
|
||||
private boolean sslHostnameValidationEnabled;
|
||||
|
||||
private ProxyRequestHelper helper;
|
||||
private Host hostProperties;
|
||||
private PoolingHttpClientConnectionManager connectionManager;
|
||||
@@ -115,6 +118,7 @@ public class SimpleHostRoutingFilter extends ZuulFilter {
|
||||
public SimpleHostRoutingFilter(ProxyRequestHelper helper, ZuulProperties properties) {
|
||||
this.helper = helper;
|
||||
this.hostProperties = properties.getHost();
|
||||
this.sslHostnameValidationEnabled = properties.isSslHostnameValidationEnabled();
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
@@ -204,11 +208,18 @@ public class SimpleHostRoutingFilter extends ZuulFilter {
|
||||
}
|
||||
} }, new SecureRandom());
|
||||
|
||||
final Registry<ConnectionSocketFactory> registry = RegistryBuilder
|
||||
RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder
|
||||
.<ConnectionSocketFactory> create()
|
||||
.register("http", PlainConnectionSocketFactory.INSTANCE)
|
||||
.register("https", new SSLConnectionSocketFactory(sslContext))
|
||||
.build();
|
||||
.register("http", PlainConnectionSocketFactory.INSTANCE);
|
||||
if (sslHostnameValidationEnabled) {
|
||||
registryBuilder.register("https",
|
||||
new SSLConnectionSocketFactory(sslContext));
|
||||
}
|
||||
else {
|
||||
registryBuilder.register("https", new SSLConnectionSocketFactory(
|
||||
sslContext, NoopHostnameVerifier.INSTANCE));
|
||||
}
|
||||
final Registry<ConnectionSocketFactory> registry = registryBuilder.build();
|
||||
|
||||
this.connectionManager = new PoolingHttpClientConnectionManager(registry);
|
||||
this.connectionManager
|
||||
@@ -228,7 +239,11 @@ public class SimpleHostRoutingFilter extends ZuulFilter {
|
||||
.setConnectTimeout(CONNECTION_TIMEOUT.get())
|
||||
.setCookieSpec(CookieSpecs.IGNORE_COOKIES).build();
|
||||
|
||||
return HttpClients.custom().setConnectionManager(newConnectionManager())
|
||||
HttpClientBuilder httpClientBuilder = HttpClients.custom();
|
||||
if (!sslHostnameValidationEnabled) {
|
||||
httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
|
||||
}
|
||||
return httpClientBuilder.setConnectionManager(newConnectionManager())
|
||||
.setDefaultRequestConfig(requestConfig)
|
||||
.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))
|
||||
.setRedirectStrategy(new RedirectStrategy() {
|
||||
@@ -363,4 +378,11 @@ public class SimpleHostRoutingFilter extends ZuulFilter {
|
||||
this.helper.addIgnoredHeaders(names);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the filter enables the validation for ssl hostnames.
|
||||
* @return
|
||||
*/
|
||||
boolean isSslHostnameValidationEnabled() {
|
||||
return sslHostnameValidationEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
+6
-3
@@ -88,8 +88,7 @@ public class HttpClientRibbonCommand extends HystrixCommand<ClientHttpResponse>
|
||||
.withExecutionIsolationSemaphoreMaxConcurrentRequests(value.get());
|
||||
return Setter
|
||||
.withGroupKey(HystrixCommandGroupKey.Factory.asKey("RibbonCommand"))
|
||||
.andCommandKey(
|
||||
HystrixCommandKey.Factory.asKey(commandKey + "RibbonCommand"))
|
||||
.andCommandKey(HystrixCommandKey.Factory.asKey(commandKey))
|
||||
.andCommandPropertiesDefaults(setter);
|
||||
}
|
||||
|
||||
@@ -100,10 +99,14 @@ public class HttpClientRibbonCommand extends HystrixCommand<ClientHttpResponse>
|
||||
|
||||
protected ClientHttpResponse forward() throws Exception {
|
||||
final RequestContext context = RequestContext.getCurrentContext();
|
||||
Long contentLength = null;
|
||||
if (context.getRequest().getContentLength() != -1) {
|
||||
contentLength = new Long(context.getRequest().getContentLength());
|
||||
}
|
||||
URI uriInstance = new URI(this.uri);
|
||||
RibbonApacheHttpRequest request = new RibbonApacheHttpRequest(this.method,
|
||||
uriInstance, this.retryable, this.headers, this.params,
|
||||
this.requestEntity);
|
||||
this.requestEntity, contentLength);
|
||||
final RibbonApacheHttpResponse response = this.client
|
||||
.executeWithLoadBalancer(request);
|
||||
context.set("ribbonResponse", response);
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class FeignClientsRegistrarTests {
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void badNameHttpPrefix() {
|
||||
testGetName("http://bad_hostname");
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void badNameHttpsPrefix() {
|
||||
testGetName("https://bad_hostname");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void badName() {
|
||||
testGetName("bad_hostname");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void badNameStartsWithHttp() {
|
||||
testGetName("http_bad_hostname");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void goodName() {
|
||||
String name = testGetName("good-name");
|
||||
assertThat("name was wrong", name, is("good-name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void goodNameHttpPrefix() {
|
||||
String name = testGetName("http://good-name");
|
||||
assertThat("name was wrong", name, is("http://good-name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void goodNameHttpsPrefix() {
|
||||
String name = testGetName("https://goodname");
|
||||
assertThat("name was wrong", name, is("https://goodname"));
|
||||
}
|
||||
|
||||
private String testGetName(String name) {
|
||||
FeignClientsRegistrar registrar = new FeignClientsRegistrar();
|
||||
return registrar.getName(Collections.<String, Object>singletonMap("name", name));
|
||||
}
|
||||
}
|
||||
+46
-1
@@ -39,6 +39,50 @@ public class FeignClientValidationTests {
|
||||
@Rule
|
||||
public ExpectedException expected = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void testNameAndValue() {
|
||||
this.expected.expectMessage("only one is permitted");
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||
NameAndValueConfiguration.class);
|
||||
assertNotNull(context.getBean(NameAndValueConfiguration.Client.class));
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(FeignAutoConfiguration.class)
|
||||
@EnableFeignClients(clients = NameAndValueConfiguration.Client.class)
|
||||
protected static class NameAndValueConfiguration {
|
||||
|
||||
@FeignClient(value = "foo", name = "bar")
|
||||
interface Client {
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/")
|
||||
String get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServiceIdAndValue() {
|
||||
this.expected.expectMessage("only one is permitted");
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||
NameAndValueConfiguration.class);
|
||||
assertNotNull(context.getBean(NameAndServiceIdConfiguration.Client.class));
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(FeignAutoConfiguration.class)
|
||||
@EnableFeignClients(clients = NameAndServiceIdConfiguration.Client.class)
|
||||
protected static class NameAndServiceIdConfiguration {
|
||||
|
||||
@FeignClient(serviceId = "foo", name = "bar")
|
||||
interface Client {
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/")
|
||||
String get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotLegalHostname() {
|
||||
this.expected.expectMessage("not legal hostname (foo_bar)");
|
||||
@@ -115,7 +159,8 @@ public class FeignClientValidationTests {
|
||||
return new Dummy();
|
||||
}
|
||||
|
||||
class Dummy { }
|
||||
class Dummy {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+24
@@ -18,6 +18,8 @@ import static org.mockito.Mockito.when;
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -101,6 +103,28 @@ public class FeignLoadBalancerTests {
|
||||
assertThat(uri, is(new URI("https://foo:7777/")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
public void testInsecureUriFromInsecureClientConfigToSecureServerIntrospector() {
|
||||
when(this.config.get(IsSecure)).thenReturn(false);
|
||||
this.feignLoadBalancer = new FeignLoadBalancer(this.lb, this.config,
|
||||
new ServerIntrospector() {
|
||||
@Override
|
||||
public boolean isSecure(Server server) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getMetadata(Server server) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
Server server = new Server("foo", 7777);
|
||||
URI uri = this.feignLoadBalancer.reconstructURIWithServer(server,
|
||||
new URI("http://foo/"));
|
||||
assertThat(uri, is(new URI("http://foo:7777/")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
public void testSecureUriFromClientConfigOverride() {
|
||||
|
||||
+290
-292
@@ -1,292 +1,290 @@
|
||||
package org.springframework.cloud.netflix.resttemplate;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.boot.test.WebIntegrationTest;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.netflix.client.RetryHandler;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.AvailabilityFilteringRule;
|
||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.IPing;
|
||||
import com.netflix.loadbalancer.IRule;
|
||||
import com.netflix.loadbalancer.LoadBalancerBuilder;
|
||||
import com.netflix.loadbalancer.LoadBalancerStats;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import com.netflix.loadbalancer.ServerStats;
|
||||
import com.netflix.niws.client.http.HttpClientLoadBalancerErrorHandler;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = RestTemplateRetryTest.Application.class)
|
||||
@WebIntegrationTest(randomPort = true, value = {
|
||||
"spring.application.name=resttemplatetest",
|
||||
"logging.level.org.springframework.cloud.netflix.resttemplate=DEBUG",
|
||||
"badClients.ribbon.MaxAutoRetries=0",
|
||||
"badClients.ribbon.OkToRetryOnAllOperations=true", "ribbon.http.client.enabled" })
|
||||
@DirtiesContext
|
||||
public class RestTemplateRetryTest {
|
||||
|
||||
final private static Log logger = LogFactory.getLog(RestTemplateRetryTest.class);
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@Autowired
|
||||
private RestTemplate testClient;
|
||||
|
||||
public RestTemplateRetryTest() {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@RibbonClient(name = "badClients", configuration = LocalBadClientConfiguration.class)
|
||||
public static class Application {
|
||||
|
||||
private AtomicInteger hits = new AtomicInteger(1);
|
||||
private AtomicInteger retryHits = new AtomicInteger(1);
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/ping")
|
||||
public int ping() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/good")
|
||||
public int good() {
|
||||
int lValue = hits.getAndIncrement();
|
||||
return lValue;
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/timeout")
|
||||
public int timeout() throws Exception {
|
||||
int lValue = retryHits.getAndIncrement();
|
||||
|
||||
// Force the good server to have 2 consecutive errors a couple of times.
|
||||
if (lValue == 2 || lValue == 3 || lValue == 5 || lValue == 6) {
|
||||
Thread.sleep(500);
|
||||
}
|
||||
return lValue;
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/null")
|
||||
public int isNull() throws Exception {
|
||||
throw new NullPointerException("Null");
|
||||
}
|
||||
|
||||
@LoadBalanced
|
||||
@Bean
|
||||
RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
// Force Ribbon configuration by making one call.
|
||||
testClient.getForObject("http://badClients/ping", Integer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullPointer() throws Exception {
|
||||
|
||||
LoadBalancerStats stats = LocalBadClientConfiguration.balancer
|
||||
.getLoadBalancerStats();
|
||||
ServerStats badServer1Stats = stats
|
||||
.getSingleServerStat(LocalBadClientConfiguration.badServer);
|
||||
ServerStats badServer2Stats = stats
|
||||
.getSingleServerStat(LocalBadClientConfiguration.badServer2);
|
||||
ServerStats goodServerStats = stats
|
||||
.getSingleServerStat(LocalBadClientConfiguration.goodServer);
|
||||
|
||||
badServer1Stats.clearSuccessiveConnectionFailureCount();
|
||||
badServer2Stats.clearSuccessiveConnectionFailureCount();
|
||||
long targetConnectionCount = goodServerStats.getTotalRequestsCount() + 10;
|
||||
|
||||
// A null pointer should NOT trigger a circuit breaker.
|
||||
for (int index = 0; index < 10; index++) {
|
||||
try {
|
||||
testClient.getForObject("http://badClients/null", Integer.class);
|
||||
}
|
||||
catch (Exception exception) {
|
||||
}
|
||||
}
|
||||
logServerStats(LocalBadClientConfiguration.badServer);
|
||||
logServerStats(LocalBadClientConfiguration.badServer2);
|
||||
logServerStats(LocalBadClientConfiguration.goodServer);
|
||||
|
||||
Assert.isTrue(badServer1Stats.isCircuitBreakerTripped());
|
||||
Assert.isTrue(badServer2Stats.isCircuitBreakerTripped());
|
||||
Assert.isTrue(goodServerStats.getTotalRequestsCount() == targetConnectionCount);
|
||||
|
||||
// Wait for any timeout thread to finish.
|
||||
|
||||
}
|
||||
|
||||
private void logServerStats(Server server) {
|
||||
LoadBalancerStats stats = LocalBadClientConfiguration.balancer
|
||||
.getLoadBalancerStats();
|
||||
ServerStats serverStats = stats.getSingleServerStat(server);
|
||||
logger.debug("Server : " + server.toString() + " : Total Count == "
|
||||
+ serverStats.getTotalRequestsCount() + ", Failure Count == "
|
||||
+ serverStats.getFailureCount() + ", Successive Connection Failure == "
|
||||
+ serverStats.getSuccessiveConnectionFailureCount()
|
||||
+ ", Circuit Breaker ? == " + serverStats.isCircuitBreakerTripped());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestRetries() {
|
||||
|
||||
LoadBalancerStats stats = LocalBadClientConfiguration.balancer
|
||||
.getLoadBalancerStats();
|
||||
ServerStats badServer1Stats = stats
|
||||
.getSingleServerStat(LocalBadClientConfiguration.badServer);
|
||||
ServerStats badServer2Stats = stats
|
||||
.getSingleServerStat(LocalBadClientConfiguration.badServer2);
|
||||
ServerStats goodServerStats = stats
|
||||
.getSingleServerStat(LocalBadClientConfiguration.goodServer);
|
||||
|
||||
badServer1Stats.clearSuccessiveConnectionFailureCount();
|
||||
badServer2Stats.clearSuccessiveConnectionFailureCount();
|
||||
long targetConnectionCount = goodServerStats.getTotalRequestsCount() + 20;
|
||||
|
||||
int hits = 0;
|
||||
|
||||
for (int index = 0; index < 20; index++) {
|
||||
hits = testClient.getForObject("http://badClients/good", Integer.class);
|
||||
}
|
||||
|
||||
logServerStats(LocalBadClientConfiguration.badServer);
|
||||
logServerStats(LocalBadClientConfiguration.badServer2);
|
||||
logServerStats(LocalBadClientConfiguration.goodServer);
|
||||
|
||||
Assert.isTrue(badServer1Stats.isCircuitBreakerTripped());
|
||||
Assert.isTrue(badServer2Stats.isCircuitBreakerTripped());
|
||||
Assert.isTrue(goodServerStats.getTotalRequestsCount() == targetConnectionCount);
|
||||
Assert.isTrue(hits == 20);
|
||||
System.out.println("Retry Hits: " + hits);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestRetriesWithReadTimeout() throws Exception {
|
||||
|
||||
LoadBalancerStats stats = LocalBadClientConfiguration.balancer
|
||||
.getLoadBalancerStats();
|
||||
ServerStats badServer1Stats = stats
|
||||
.getSingleServerStat(LocalBadClientConfiguration.badServer);
|
||||
ServerStats badServer2Stats = stats
|
||||
.getSingleServerStat(LocalBadClientConfiguration.badServer2);
|
||||
ServerStats goodServerStats = stats
|
||||
.getSingleServerStat(LocalBadClientConfiguration.goodServer);
|
||||
|
||||
badServer1Stats.clearSuccessiveConnectionFailureCount();
|
||||
badServer2Stats.clearSuccessiveConnectionFailureCount();
|
||||
Assert.isTrue(!badServer1Stats.isCircuitBreakerTripped());
|
||||
Assert.isTrue(!badServer2Stats.isCircuitBreakerTripped());
|
||||
|
||||
int hits = 0;
|
||||
|
||||
for (int index = 0; index < 15; index++) {
|
||||
hits = testClient.getForObject("http://badClients/timeout", Integer.class);
|
||||
}
|
||||
logServerStats(LocalBadClientConfiguration.badServer);
|
||||
logServerStats(LocalBadClientConfiguration.badServer2);
|
||||
logServerStats(LocalBadClientConfiguration.goodServer);
|
||||
|
||||
Assert.isTrue(badServer1Stats.isCircuitBreakerTripped());
|
||||
Assert.isTrue(badServer2Stats.isCircuitBreakerTripped());
|
||||
Assert.isTrue(!goodServerStats.isCircuitBreakerTripped());
|
||||
|
||||
// 15 + 4 timeouts. See the endpoint for timeout conditions.
|
||||
Assert.isTrue(hits == 19);
|
||||
|
||||
// Wait for any timeout thread to finish.
|
||||
Thread.sleep(600);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Load balancer with fixed server list for "local" pointing to localhost
|
||||
// and some bogus servers are thrown in to test retry
|
||||
@Configuration
|
||||
class LocalBadClientConfiguration {
|
||||
|
||||
static BaseLoadBalancer balancer;
|
||||
static Server goodServer;
|
||||
static Server badServer;
|
||||
static Server badServer2;
|
||||
|
||||
public LocalBadClientConfiguration() {
|
||||
}
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@Bean
|
||||
public IRule loadBalancerRule() {
|
||||
// This is a good place to try different load balancing rules and how those rules
|
||||
// behave in failure
|
||||
// states: BestAvailableRule, WeightedResponseTimeRule, etc
|
||||
|
||||
// This rule just uses a round robin and will skip servers that are in circuit
|
||||
// breaker state.
|
||||
return new AvailabilityFilteringRule();
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
|
||||
ServerList<Server> serverList, IRule rule, IPing ping) {
|
||||
|
||||
goodServer = new Server("localhost", this.port);
|
||||
badServer = new Server("mybadhost", 10001);
|
||||
badServer2 = new Server("localhost", -1);
|
||||
|
||||
balancer = LoadBalancerBuilder
|
||||
.newBuilder()
|
||||
.withClientConfig(config)
|
||||
.withRule(rule)
|
||||
.withPing(ping)
|
||||
.buildFixedServerListLoadBalancer(
|
||||
Arrays.asList(badServer, badServer2, goodServer));
|
||||
return balancer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RetryHandler retryHandler() {
|
||||
return new OverrideRetryHandler();
|
||||
}
|
||||
|
||||
static class OverrideRetryHandler extends HttpClientLoadBalancerErrorHandler {
|
||||
public OverrideRetryHandler() {
|
||||
circuitRelated.add(UnknownHostException.class);
|
||||
retriable.add(UnknownHostException.class);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
package org.springframework.cloud.netflix.resttemplate;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.boot.test.WebIntegrationTest;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.SocketUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.netflix.client.RetryHandler;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.AvailabilityFilteringRule;
|
||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.IPing;
|
||||
import com.netflix.loadbalancer.IRule;
|
||||
import com.netflix.loadbalancer.LoadBalancerBuilder;
|
||||
import com.netflix.loadbalancer.LoadBalancerStats;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import com.netflix.loadbalancer.ServerStats;
|
||||
import com.netflix.niws.client.http.HttpClientLoadBalancerErrorHandler;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = RestTemplateRetryTests.Application.class)
|
||||
@WebIntegrationTest(randomPort = true, value = {
|
||||
"spring.application.name=resttemplatetest",
|
||||
"logging.level.org.springframework.cloud.netflix.resttemplate=DEBUG",
|
||||
"badClients.ribbon.MaxAutoRetries=0",
|
||||
"badClients.ribbon.OkToRetryOnAllOperations=true", "ribbon.http.client.enabled" })
|
||||
@DirtiesContext
|
||||
public class RestTemplateRetryTests {
|
||||
|
||||
final private static Log logger = LogFactory.getLog(RestTemplateRetryTests.class);
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@Autowired
|
||||
private RestTemplate testClient;
|
||||
|
||||
public RestTemplateRetryTests() {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
// Force Ribbon configuration by making one call.
|
||||
this.testClient.getForObject("http://badClients/ping", Integer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullPointer() throws Exception {
|
||||
|
||||
LoadBalancerStats stats = LocalBadClientConfiguration.balancer
|
||||
.getLoadBalancerStats();
|
||||
ServerStats badServer1Stats = stats
|
||||
.getSingleServerStat(LocalBadClientConfiguration.badServer);
|
||||
ServerStats badServer2Stats = stats
|
||||
.getSingleServerStat(LocalBadClientConfiguration.badServer2);
|
||||
ServerStats goodServerStats = stats
|
||||
.getSingleServerStat(LocalBadClientConfiguration.goodServer);
|
||||
|
||||
badServer1Stats.clearSuccessiveConnectionFailureCount();
|
||||
badServer2Stats.clearSuccessiveConnectionFailureCount();
|
||||
long targetConnectionCount = goodServerStats.getTotalRequestsCount() + 10;
|
||||
|
||||
// A null pointer should NOT trigger a circuit breaker.
|
||||
for (int index = 0; index < 10; index++) {
|
||||
try {
|
||||
this.testClient.getForObject("http://badClients/null", Integer.class);
|
||||
}
|
||||
catch (Exception exception) {
|
||||
}
|
||||
}
|
||||
logServerStats(LocalBadClientConfiguration.badServer);
|
||||
logServerStats(LocalBadClientConfiguration.badServer2);
|
||||
logServerStats(LocalBadClientConfiguration.goodServer);
|
||||
|
||||
assertTrue(badServer1Stats.isCircuitBreakerTripped());
|
||||
assertTrue(badServer2Stats.isCircuitBreakerTripped());
|
||||
assertEquals(targetConnectionCount, goodServerStats.getTotalRequestsCount());
|
||||
|
||||
// Wait for any timeout thread to finish.
|
||||
|
||||
}
|
||||
|
||||
private void logServerStats(Server server) {
|
||||
LoadBalancerStats stats = LocalBadClientConfiguration.balancer
|
||||
.getLoadBalancerStats();
|
||||
ServerStats serverStats = stats.getSingleServerStat(server);
|
||||
logger.debug("Server : " + server.toString() + " : Total Count == "
|
||||
+ serverStats.getTotalRequestsCount() + ", Failure Count == "
|
||||
+ serverStats.getFailureCount() + ", Successive Connection Failure == "
|
||||
+ serverStats.getSuccessiveConnectionFailureCount()
|
||||
+ ", Circuit Breaker ? == " + serverStats.isCircuitBreakerTripped());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestRetries() {
|
||||
|
||||
LoadBalancerStats stats = LocalBadClientConfiguration.balancer
|
||||
.getLoadBalancerStats();
|
||||
ServerStats badServer1Stats = stats
|
||||
.getSingleServerStat(LocalBadClientConfiguration.badServer);
|
||||
ServerStats badServer2Stats = stats
|
||||
.getSingleServerStat(LocalBadClientConfiguration.badServer2);
|
||||
ServerStats goodServerStats = stats
|
||||
.getSingleServerStat(LocalBadClientConfiguration.goodServer);
|
||||
|
||||
badServer1Stats.clearSuccessiveConnectionFailureCount();
|
||||
badServer2Stats.clearSuccessiveConnectionFailureCount();
|
||||
long targetConnectionCount = goodServerStats.getTotalRequestsCount() + 20;
|
||||
|
||||
int hits = 0;
|
||||
|
||||
for (int index = 0; index < 20; index++) {
|
||||
hits = this.testClient.getForObject("http://badClients/good", Integer.class);
|
||||
}
|
||||
|
||||
logServerStats(LocalBadClientConfiguration.badServer);
|
||||
logServerStats(LocalBadClientConfiguration.badServer2);
|
||||
logServerStats(LocalBadClientConfiguration.goodServer);
|
||||
|
||||
assertTrue(badServer1Stats.isCircuitBreakerTripped());
|
||||
assertTrue(badServer2Stats.isCircuitBreakerTripped());
|
||||
assertEquals(targetConnectionCount, goodServerStats.getTotalRequestsCount());
|
||||
assertEquals(20, hits);
|
||||
logger.debug("Retry Hits: " + hits);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestRetriesWithReadTimeout() throws Exception {
|
||||
|
||||
LoadBalancerStats stats = LocalBadClientConfiguration.balancer
|
||||
.getLoadBalancerStats();
|
||||
ServerStats badServer1Stats = stats
|
||||
.getSingleServerStat(LocalBadClientConfiguration.badServer);
|
||||
ServerStats badServer2Stats = stats
|
||||
.getSingleServerStat(LocalBadClientConfiguration.badServer2);
|
||||
ServerStats goodServerStats = stats
|
||||
.getSingleServerStat(LocalBadClientConfiguration.goodServer);
|
||||
|
||||
badServer1Stats.clearSuccessiveConnectionFailureCount();
|
||||
badServer2Stats.clearSuccessiveConnectionFailureCount();
|
||||
assertTrue(!badServer1Stats.isCircuitBreakerTripped());
|
||||
assertTrue(!badServer2Stats.isCircuitBreakerTripped());
|
||||
|
||||
int hits = 0;
|
||||
|
||||
for (int index = 0; index < 15; index++) {
|
||||
hits = this.testClient.getForObject("http://badClients/timeout",
|
||||
Integer.class);
|
||||
}
|
||||
logServerStats(LocalBadClientConfiguration.badServer);
|
||||
logServerStats(LocalBadClientConfiguration.badServer2);
|
||||
logServerStats(LocalBadClientConfiguration.goodServer);
|
||||
|
||||
assertTrue(badServer1Stats.isCircuitBreakerTripped());
|
||||
assertTrue(badServer2Stats.isCircuitBreakerTripped());
|
||||
assertTrue(!goodServerStats.isCircuitBreakerTripped());
|
||||
|
||||
// 15 + 4 timeouts. See the endpoint for timeout conditions.
|
||||
assertEquals(19, hits);
|
||||
|
||||
// Wait for any timeout thread to finish.
|
||||
Thread.sleep(600);
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@RibbonClient(name = "badClients", configuration = LocalBadClientConfiguration.class)
|
||||
public static class Application {
|
||||
|
||||
private AtomicInteger hits = new AtomicInteger(1);
|
||||
private AtomicInteger retryHits = new AtomicInteger(1);
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/ping")
|
||||
public int ping() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/good")
|
||||
public int good() {
|
||||
int lValue = this.hits.getAndIncrement();
|
||||
return lValue;
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/timeout")
|
||||
public int timeout() throws Exception {
|
||||
int lValue = this.retryHits.getAndIncrement();
|
||||
|
||||
// Force the good server to have 2 consecutive errors a couple of times.
|
||||
if (lValue == 2 || lValue == 3 || lValue == 5 || lValue == 6) {
|
||||
Thread.sleep(500);
|
||||
}
|
||||
return lValue;
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/null")
|
||||
public int isNull() throws Exception {
|
||||
throw new NullPointerException("Null");
|
||||
}
|
||||
|
||||
@LoadBalanced
|
||||
@Bean
|
||||
RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Load balancer with fixed server list for "local" pointing to localhost
|
||||
// and some bogus servers are thrown in to test retry
|
||||
@Configuration
|
||||
class LocalBadClientConfiguration {
|
||||
|
||||
static BaseLoadBalancer balancer;
|
||||
static Server goodServer;
|
||||
static Server badServer;
|
||||
static Server badServer2;
|
||||
|
||||
public LocalBadClientConfiguration() {
|
||||
}
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@Bean
|
||||
public IRule loadBalancerRule() {
|
||||
// This is a good place to try different load balancing rules and how those rules
|
||||
// behave in failure states: BestAvailableRule, WeightedResponseTimeRule, etc
|
||||
|
||||
// This rule just uses a round robin and will skip servers that are in circuit
|
||||
// breaker state.
|
||||
return new AvailabilityFilteringRule();
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
|
||||
ServerList<Server> serverList, IRule rule, IPing ping) {
|
||||
|
||||
goodServer = new Server("localhost", this.port);
|
||||
badServer = new Server("mybadhost", 10001);
|
||||
badServer2 = new Server("localhost", SocketUtils.findAvailableTcpPort());
|
||||
|
||||
balancer = LoadBalancerBuilder.newBuilder().withClientConfig(config)
|
||||
.withRule(rule).withPing(ping).buildFixedServerListLoadBalancer(
|
||||
Arrays.asList(badServer, badServer2, goodServer));
|
||||
return balancer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RetryHandler retryHandler() {
|
||||
return new OverrideRetryHandler();
|
||||
}
|
||||
|
||||
static class OverrideRetryHandler extends HttpClientLoadBalancerErrorHandler {
|
||||
public OverrideRetryHandler() {
|
||||
this.circuitRelated.add(UnknownHostException.class);
|
||||
this.retriable.add(UnknownHostException.class);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+33
-1
@@ -112,7 +112,7 @@ public class RibbonLoadBalancerClientTests {
|
||||
public void testReconstructUriWithSecureClientConfig() {
|
||||
RibbonServer server = getRibbonServer();
|
||||
IClientConfig config = mock(IClientConfig.class);
|
||||
when(config.get(CommonClientConfigKey.IsSecure, false)).thenReturn(true);
|
||||
when(config.get(CommonClientConfigKey.IsSecure)).thenReturn(true);
|
||||
when(clientFactory.getClientConfig(server.getServiceId())).thenReturn(config);
|
||||
|
||||
RibbonLoadBalancerClient client = getRibbonLoadBalancerClient(server);
|
||||
@@ -124,6 +124,33 @@ public class RibbonLoadBalancerClientTests {
|
||||
assertEquals("https", uri.getScheme());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
public void testReconstructSecureUriWithoutScheme() {
|
||||
testReconstructSchemelessUriWithoutClientConfig(getSecureRibbonServer(), "https");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
public void testReconstructUnsecureSchemelessUri() {
|
||||
testReconstructSchemelessUriWithoutClientConfig(getRibbonServer(), "http");
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void testReconstructSchemelessUriWithoutClientConfig(RibbonServer server, String expectedScheme) {
|
||||
IClientConfig config = mock(IClientConfig.class);
|
||||
when(config.get(CommonClientConfigKey.IsSecure)).thenReturn(null);
|
||||
when(clientFactory.getClientConfig(server.getServiceId())).thenReturn(config);
|
||||
|
||||
RibbonLoadBalancerClient client = getRibbonLoadBalancerClient(server);
|
||||
ServiceInstance serviceInstance = client.choose(server.getServiceId());
|
||||
URI uri = client.reconstructURI(serviceInstance,
|
||||
new URI("//" + server.getServiceId()));
|
||||
assertEquals(server.getHost(), uri.getHost());
|
||||
assertEquals(server.getPort(), uri.getPort());
|
||||
assertEquals(expectedScheme, uri.getScheme());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChoose() {
|
||||
RibbonServer server = getRibbonServer();
|
||||
@@ -206,6 +233,11 @@ public class RibbonLoadBalancerClientTests {
|
||||
Collections.singletonMap("mykey", "myvalue"));
|
||||
}
|
||||
|
||||
protected RibbonServer getSecureRibbonServer() {
|
||||
return new RibbonServer("testService", new Server("myhost", 8443), false,
|
||||
Collections.singletonMap("mykey", "myvalue"));
|
||||
}
|
||||
|
||||
protected void verifyServerStats() {
|
||||
verify(this.serverStats).incrementActiveRequestsCount();
|
||||
verify(this.serverStats).decrementActiveRequestsCount();
|
||||
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.ribbon;
|
||||
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.netflix.client.config.DefaultClientConfigImpl;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.isSecure;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class RibbonUtilsTests {
|
||||
|
||||
private static final ServerIntrospector NON_SECURE_INTROSPECTOR = new StaticServerIntrospector(false);
|
||||
private static final ServerIntrospector SECURE_INTROSPECTOR = new StaticServerIntrospector(true);
|
||||
private static final Server SERVER = new Server("localhost", 8080);
|
||||
private static final DefaultClientConfigImpl SECURE_CONFIG = getConfig(true);
|
||||
private static final DefaultClientConfigImpl NON_SECURE_CONFIG = getConfig(false);
|
||||
private static final DefaultClientConfigImpl NO_IS_SECURE_CONFIG = new DefaultClientConfigImpl();
|
||||
|
||||
@Test
|
||||
public void noRibbonPropSecureIntrospector() {
|
||||
boolean secure = isSecure(NO_IS_SECURE_CONFIG, SECURE_INTROSPECTOR, SERVER);
|
||||
Assert.assertThat("isSecure was wrong", secure, is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noRibbonPropNonSecureIntrospector() {
|
||||
boolean secure = isSecure(NO_IS_SECURE_CONFIG, NON_SECURE_INTROSPECTOR, SERVER);
|
||||
Assert.assertThat("isSecure was wrong", secure, is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSecureRibbonPropSecureIntrospector() {
|
||||
boolean secure = isSecure(SECURE_CONFIG, SECURE_INTROSPECTOR, SERVER);
|
||||
Assert.assertThat("isSecure was wrong", secure, is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonSecureRibbonPropNonSecureIntrospector() {
|
||||
boolean secure = isSecure(NON_SECURE_CONFIG, NON_SECURE_INTROSPECTOR, SERVER);
|
||||
Assert.assertThat("isSecure was wrong", secure, is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSecureRibbonPropNonSecureIntrospector() {
|
||||
boolean secure = isSecure(SECURE_CONFIG, NON_SECURE_INTROSPECTOR, SERVER);
|
||||
Assert.assertThat("isSecure was wrong", secure, is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonSecureRibbonPropSecureIntrospector() {
|
||||
boolean secure = isSecure(NON_SECURE_CONFIG, SECURE_INTROSPECTOR, SERVER);
|
||||
Assert.assertThat("isSecure was wrong", secure, is(false));
|
||||
}
|
||||
|
||||
static DefaultClientConfigImpl getConfig(boolean value) {
|
||||
DefaultClientConfigImpl config = new DefaultClientConfigImpl();
|
||||
config.setProperty(CommonClientConfigKey.IsSecure, value);
|
||||
return config;
|
||||
}
|
||||
|
||||
static class StaticServerIntrospector implements ServerIntrospector {
|
||||
|
||||
final boolean secure;
|
||||
|
||||
public StaticServerIntrospector(boolean secure) {
|
||||
this.secure = secure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure(Server server) {
|
||||
return this.secure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getMetadata(Server server) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.ribbon.apache;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpEntityEnclosingRequest;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.junit.Test;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class RibbonApacheHttpRequestTests {
|
||||
|
||||
@Test
|
||||
public void testNullEntity() throws Exception {
|
||||
URI uri = URI.create("http://example.com");
|
||||
LinkedMultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
|
||||
headers.add("my-header", "my-value");
|
||||
LinkedMultiValueMap<String, String> params = new LinkedMultiValueMap<>();
|
||||
params.add("myparam", "myparamval");
|
||||
RibbonApacheHttpRequest httpRequest = new RibbonApacheHttpRequest("GET", uri, false,
|
||||
headers, params, null);
|
||||
|
||||
HttpUriRequest request = httpRequest.toRequest(RequestConfig.custom().build());
|
||||
|
||||
assertThat("request is wrong type", request, is(not(instanceOf(HttpEntityEnclosingRequest.class))));
|
||||
assertThat("uri is wrong", request.getURI().toString(), startsWith(uri.toString()));
|
||||
assertThat("my-header is missing", request.getFirstHeader("my-header"), is(notNullValue()));
|
||||
assertThat("my-header is wrong", request.getFirstHeader("my-header").getValue(), is(equalTo("my-value")));
|
||||
assertThat("myparam is missing", request.getURI().getQuery(), is(equalTo("myparam=myparamval")));
|
||||
}
|
||||
|
||||
@Test
|
||||
// this situation happens, see https://github.com/spring-cloud/spring-cloud-netflix/issues/1042#issuecomment-227723877
|
||||
public void testEmptyEntityGet() throws Exception {
|
||||
String entityValue = "";
|
||||
testEntity(entityValue, new ByteArrayInputStream(entityValue.getBytes()), false, "GET");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonEmptyEntityPost() throws Exception {
|
||||
String entityValue = "abcd";
|
||||
testEntity(entityValue, new ByteArrayInputStream(entityValue.getBytes()), true, "POST");
|
||||
}
|
||||
|
||||
void testEntity(String entityValue, ByteArrayInputStream requestEntity, boolean addContentLengthHeader, String method) throws IOException {
|
||||
String lengthString = String.valueOf(entityValue.length());
|
||||
Long length = null;
|
||||
URI uri = URI.create("http://example.com");
|
||||
LinkedMultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
|
||||
if (addContentLengthHeader) {
|
||||
headers.add("Content-Length", lengthString);
|
||||
length = (long) entityValue.length();
|
||||
}
|
||||
RibbonApacheHttpRequest httpRequest = new RibbonApacheHttpRequest(method, uri, false,
|
||||
headers, new LinkedMultiValueMap<String, String>(), requestEntity,
|
||||
length);
|
||||
|
||||
HttpUriRequest request = httpRequest.toRequest(RequestConfig.custom().build());
|
||||
|
||||
assertThat("request is wrong type", request, is(instanceOf(HttpEntityEnclosingRequest.class)));
|
||||
assertThat("uri is wrong", request.getURI().toString(), startsWith(uri.toString()));
|
||||
if (addContentLengthHeader) {
|
||||
assertThat("Content-Length is missing", request.getFirstHeader("Content-Length"), is(notNullValue()));
|
||||
assertThat("Content-Length is wrong", request.getFirstHeader("Content-Length").getValue(),
|
||||
is(equalTo(lengthString)));
|
||||
}
|
||||
|
||||
HttpEntityEnclosingRequest entityRequest = (HttpEntityEnclosingRequest) request;
|
||||
assertThat("entity is missing", entityRequest.getEntity(), is(notNullValue()));
|
||||
HttpEntity entity = entityRequest.getEntity();
|
||||
assertThat("contentLength is wrong", entity.getContentLength(), is(equalTo((long)entityValue.length())));
|
||||
assertThat("content is missing", entity.getContent(), is(notNullValue()));
|
||||
String string = StreamUtils.copyToString(entity.getContent(), Charset.forName("UTF-8"));
|
||||
assertThat("content is wrong", string, is(equalTo(entityValue)));
|
||||
}
|
||||
}
|
||||
+84
-2
@@ -16,9 +16,15 @@
|
||||
|
||||
package org.springframework.cloud.netflix.zuul;
|
||||
|
||||
import com.netflix.client.RetryHandler;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
|
||||
@@ -28,6 +34,8 @@ import org.springframework.boot.test.WebIntegrationTest;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommandFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -43,15 +51,23 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.http.HttpHeaders.SET_COOKIE;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = SampleHttpClientZuulProxyApplication.class)
|
||||
@WebIntegrationTest(randomPort = true, value = {
|
||||
"zuul.routes.other: /test/**=http://localhost:7777/local",
|
||||
"zuul.routes.another: /another/twolevel/**", "zuul.routes.simple: /simple/**" })
|
||||
"zuul.routes.another: /another/twolevel/**", "zuul.routes.simple: /simple/**",
|
||||
"zuul.routes.singleton: /singleton/**", "zuul.routes.singleton.sensitiveHeaders: "})
|
||||
@DirtiesContext
|
||||
public class SampleZuulProxyWithHttpClientTests extends ZuulProxyTestBase {
|
||||
|
||||
@@ -99,6 +115,33 @@ public class SampleZuulProxyWithHttpClientTests extends ZuulProxyTestBase {
|
||||
assertEquals("Patched 1!", result.getBody());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ribbonLoadBalancingHttpClientCookiePolicy() {
|
||||
ResponseEntity<String> result = new TestRestTemplate().exchange(
|
||||
"http://localhost:" + this.port + "/simple/downstream_cookie", HttpMethod.POST,
|
||||
new HttpEntity<>((Void) null), String.class);
|
||||
assertEquals(HttpStatus.OK, result.getStatusCode());
|
||||
assertEquals("Cookie 434354454!", result.getBody());
|
||||
assertNull(result.getHeaders().getFirst(SET_COOKIE));
|
||||
|
||||
|
||||
// if new instance of RibbonLoadBalancingHttpClient is getting created every time
|
||||
// and HttpClient is not reused then there are no concerns for the shared cookie storage
|
||||
// but since https://github.com/spring-cloud/spring-cloud-netflix/issues/1150 is on the way a
|
||||
result = new TestRestTemplate().exchange(
|
||||
"http://localhost:" + this.port + "/singleton/downstream_cookie", HttpMethod.POST,
|
||||
new HttpEntity<>((Void) null), String.class);
|
||||
assertEquals(HttpStatus.OK, result.getStatusCode());
|
||||
assertEquals("Cookie 434354454!", result.getBody());
|
||||
assertEquals("jsessionid=434354454", result.getHeaders().getFirst(SET_COOKIE));
|
||||
|
||||
result = new TestRestTemplate().exchange(
|
||||
"http://localhost:" + this.port + "/singleton/downstream_cookie", HttpMethod.GET,
|
||||
new HttpEntity<>((Void) null), String.class);
|
||||
assertEquals(HttpStatus.OK, result.getStatusCode());
|
||||
assertEquals("Cookie null!", result.getBody());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ribbonCommandFactoryOverridden() {
|
||||
assertTrue("ribbonCommandFactory not a MyRibbonCommandFactory",
|
||||
@@ -107,6 +150,30 @@ public class SampleZuulProxyWithHttpClientTests extends ZuulProxyTestBase {
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Load balancer with fixed server list and defined ribbon rest client
|
||||
@Configuration
|
||||
class SingletonRibbonClientConfiguration {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port;
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RibbonLoadBalancingHttpClient ribbonClient(IClientConfig config, ILoadBalancer loadBalancer, RetryHandler retryHandler)
|
||||
{
|
||||
final RibbonLoadBalancingHttpClient client = new RibbonLoadBalancingHttpClient();
|
||||
client.setLoadBalancer(loadBalancer);
|
||||
client.setRetryHandler(retryHandler);
|
||||
client.initWithNiwsConfig(config);
|
||||
return client;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't use @SpringBootApplication because we don't want to component scan
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@@ -114,7 +181,8 @@ public class SampleZuulProxyWithHttpClientTests extends ZuulProxyTestBase {
|
||||
@EnableZuulProxy
|
||||
@RibbonClients({
|
||||
@RibbonClient(name = "simple", configuration = SimpleRibbonClientConfiguration.class),
|
||||
@RibbonClient(name = "another", configuration = AnotherRibbonClientConfiguration.class) })
|
||||
@RibbonClient(name = "another", configuration = AnotherRibbonClientConfiguration.class),
|
||||
@RibbonClient(name = "singleton", configuration = SingletonRibbonClientConfiguration.class)})
|
||||
class SampleHttpClientZuulProxyApplication extends ZuulProxyTestBase.AbstractZuulProxyApplication {
|
||||
|
||||
public static void main(final String[] args) {
|
||||
@@ -126,6 +194,20 @@ class SampleHttpClientZuulProxyApplication extends ZuulProxyTestBase.AbstractZuu
|
||||
return "Patched " + id + "!";
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/downstream_cookie", method = RequestMethod.POST)
|
||||
public String setDownstreamCookie(HttpServletResponse response)
|
||||
{
|
||||
response.addCookie(new Cookie("jsessionid", "434354454"));
|
||||
return "Cookie 434354454!";
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/downstream_cookie", method = RequestMethod.GET)
|
||||
public String readDownstreamCookie(HttpServletRequest request)
|
||||
{
|
||||
final Cookie cookie = WebUtils.getCookie(request, "jsessionid");
|
||||
return "Cookie " + cookie + "!";
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RibbonCommandFactory<?> ribbonCommandFactory(
|
||||
final SpringClientFactory clientFactory) {
|
||||
|
||||
+2
-2
@@ -72,7 +72,7 @@ public class ProxyRequestHelperTests {
|
||||
request.addHeader("multiName", "multiValue2");
|
||||
RequestContext.getCurrentContext().setRequest(request);
|
||||
|
||||
ProxyRequestHelper helper = new ProxyRequestHelper();
|
||||
TraceProxyRequestHelper helper = new TraceProxyRequestHelper();
|
||||
this.traceRepository = new InMemoryTraceRepository();
|
||||
helper.setTraces(this.traceRepository);
|
||||
|
||||
@@ -172,7 +172,7 @@ public class ProxyRequestHelperTests {
|
||||
request.addHeader("multiName", "multiValue1");
|
||||
request.addHeader("multiName", "multiValue2");
|
||||
|
||||
ProxyRequestHelper helper = new ProxyRequestHelper();
|
||||
TraceProxyRequestHelper helper = new TraceProxyRequestHelper();
|
||||
helper.setTraces(this.traceRepository);
|
||||
|
||||
MultiValueMap<String, String> headers = helper.buildZuulRequestHeaders(request);
|
||||
|
||||
+17
@@ -28,6 +28,8 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.boot.test.EnvironmentTestUtils.addEnvironment;
|
||||
|
||||
/**
|
||||
@@ -54,6 +56,21 @@ public class SimpleHostRoutingFilterTests {
|
||||
assertEquals(10, connMgr.getDefaultMaxPerRoute());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateSslHostnamesByDefault() {
|
||||
setupContext();
|
||||
assertTrue("Hostname verification should be enabled by default",
|
||||
getFilter().isSslHostnameValidationEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validationOfSslHostnamesCanBeDisabledViaProperty() {
|
||||
addEnvironment(this.context, "zuul.sslHostnameValidationEnabled=false");
|
||||
setupContext();
|
||||
assertFalse("Hostname verification should be disabled via property",
|
||||
getFilter().isSslHostnameValidationEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultPropertiesAreApplied() {
|
||||
setupContext();
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-dependencies-parent</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>1.1.1.RELEASE</version>
|
||||
<version>1.1.2.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-dependencies</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>spring-cloud-netflix-dependencies</name>
|
||||
<description>Spring Cloud Netflix Dependencies</description>
|
||||
<properties>
|
||||
<spring-cloud-commons.version>1.1.2.BUILD-SNAPSHOT</spring-cloud-commons.version>
|
||||
<spring-cloud-config.version>1.1.2.BUILD-SNAPSHOT</spring-cloud-config.version>
|
||||
<spring-cloud-stream.version>1.0.3.BUILD-SNAPSHOT</spring-cloud-stream.version>
|
||||
<spring-cloud-commons.version>1.1.2.RELEASE</spring-cloud-commons.version>
|
||||
<spring-cloud-config.version>1.1.3.RELEASE</spring-cloud-config.version>
|
||||
<spring-cloud-stream.version>1.0.2.RELEASE</spring-cloud-stream.version>
|
||||
<archaius.version>0.7.4</archaius.version>
|
||||
<eureka.version>1.4.8</eureka.version>
|
||||
<eureka.version>1.4.10</eureka.version>
|
||||
<feign.version>8.16.2</feign.version>
|
||||
<hystrix.version>1.5.3</hystrix.version>
|
||||
<ribbon.version>2.2.0</ribbon.version>
|
||||
@@ -162,11 +162,6 @@
|
||||
<artifactId>spring-cloud-netflix-turbine-stream</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-zuul-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-dependencies</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
|
||||
|
||||
+2
-16
@@ -26,7 +26,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
|
||||
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
@@ -204,7 +203,8 @@ public class EurekaClientAutoConfiguration {
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnRefreshScopeCondition.class)
|
||||
@ConditionalOnClass(RefreshScope.class)
|
||||
@ConditionalOnBean(RefreshAutoConfiguration.class)
|
||||
@interface ConditionalOnRefreshScope {
|
||||
|
||||
}
|
||||
@@ -219,24 +219,10 @@ public class EurekaClientAutoConfiguration {
|
||||
static class MissingClass {
|
||||
}
|
||||
|
||||
@ConditionalOnClass(RefreshScope.class)
|
||||
@ConditionalOnMissingBean(RefreshAutoConfiguration.class)
|
||||
static class MissingScope {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class OnRefreshScopeCondition extends AllNestedConditions {
|
||||
|
||||
public OnRefreshScopeCondition() {
|
||||
super(ConfigurationPhase.REGISTER_BEAN);
|
||||
}
|
||||
|
||||
@ConditionalOnClass(RefreshScope.class)
|
||||
@ConditionalOnBean(RefreshAutoConfiguration.class)
|
||||
static class FoundScope {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ public class EurekaHealthIndicator implements DiscoveryHealthIndicator {
|
||||
status = new Status("UP",
|
||||
"Eureka discovery client has not yet successfully connected to a Eureka server");
|
||||
}
|
||||
else if (lastFetch > clientConfig.getRegistryFetchIntervalSeconds() * 2) {
|
||||
else if (lastFetch > clientConfig.getRegistryFetchIntervalSeconds() * 2000) {
|
||||
status = new Status("UP",
|
||||
"Eureka discovery client is reporting failures to connect to a Eureka server");
|
||||
builder.withDetail("renewalPeriod",
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ import com.netflix.niws.loadbalancer.NIWSDiscoveryPing;
|
||||
|
||||
import static com.netflix.client.config.CommonClientConfigKey.DeploymentContextBasedVipAddresses;
|
||||
import static com.netflix.client.config.CommonClientConfigKey.EnableZoneAffinity;
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonProperyUtils.setRibbonProperty;
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.setRibbonProperty;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
|
||||
+2
-4
@@ -18,7 +18,6 @@ package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Matchers;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.aop.scope.ScopedProxyFactoryBean;
|
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||
@@ -32,7 +31,6 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import com.netflix.discovery.shared.transport.jersey.EurekaJerseyClient;
|
||||
import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
|
||||
import com.sun.jersey.client.apache4.ApacheHttpClient4;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -114,8 +112,8 @@ public class EurekaClientAutoConfigurationTests {
|
||||
EnvironmentTestUtils.addEnvironment(this.context, "server.port=8989",
|
||||
"eureka.client.serviceUrl.defaultZone=http://user:foo@example.com:80/eureka");
|
||||
setupContext(MockClientConfiguration.class);
|
||||
//ApacheHttpClient4 http = this.context.getBean(ApacheHttpClient4.class);
|
||||
//Mockito.verify(http).addFilter(Matchers.any(HTTPBasicAuthFilter.class));
|
||||
// ApacheHttpClient4 http = this.context.getBean(ApacheHttpClient4.class);
|
||||
// Mockito.verify(http).addFilter(Matchers.any(HTTPBasicAuthFilter.class));
|
||||
}
|
||||
|
||||
private void testNonSecurePort(String propName) {
|
||||
|
||||
+4
-4
@@ -36,10 +36,10 @@ import com.netflix.niws.loadbalancer.DiscoveryEnabledServer;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonProperyUtils.VALUE_NOT_SET;
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonProperyUtils.getProperty;
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonProperyUtils.getRibbonKey;
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonProperyUtils.setRibbonProperty;
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.VALUE_NOT_SET;
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.getProperty;
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.getRibbonKey;
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.setRibbonProperty;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-eureka-server</artifactId>
|
||||
|
||||
+27
-13
@@ -11,7 +11,9 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.Version;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectReader;
|
||||
import com.fasterxml.jackson.databind.ObjectWriter;
|
||||
@@ -36,9 +38,13 @@ public class CloudJacksonJson extends LegacyJacksonJson {
|
||||
|
||||
protected final CloudJacksonCodec codec = new CloudJacksonCodec();
|
||||
|
||||
public CloudJacksonCodec getCodec() {
|
||||
return codec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String codecName() {
|
||||
return getCodecName(this.getClass());
|
||||
return getCodecName(LegacyJacksonJson.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -120,25 +126,26 @@ public class CloudJacksonJson extends LegacyJacksonJson {
|
||||
|
||||
static class CloudInstanceInfoSerializer extends InstanceInfoSerializer {
|
||||
@Override
|
||||
public void serialize(InstanceInfo info, JsonGenerator jgen,
|
||||
public void serialize(final InstanceInfo info, JsonGenerator jgen,
|
||||
SerializerProvider provider) throws IOException {
|
||||
|
||||
if (info.getInstanceId() == null && info.getMetadata() != null) {
|
||||
String instanceId = calculateInstanceId(info);
|
||||
info = new InstanceInfo.Builder(info).setInstanceId(instanceId).build();
|
||||
}
|
||||
|
||||
super.serialize(info, jgen, provider);
|
||||
InstanceInfo updated = updateIfNeeded(info);
|
||||
super.serialize(updated, jgen, provider);
|
||||
}
|
||||
}
|
||||
|
||||
private String calculateInstanceId(InstanceInfo info) {
|
||||
static InstanceInfo updateIfNeeded(final InstanceInfo info) {
|
||||
if (info.getInstanceId() == null && info.getMetadata() != null) {
|
||||
String instanceId = info.getMetadata().get("instanceId");
|
||||
String hostName = info.getHostName();
|
||||
if (instanceId != null && StringUtils.hasText(hostName) && !instanceId.startsWith(hostName)) {
|
||||
instanceId = hostName + ":" + instanceId;
|
||||
if (StringUtils.hasText(instanceId)) {
|
||||
// backwards compatibility for Angel
|
||||
if (StringUtils.hasText(info.getHostName()) && !instanceId.startsWith(info.getHostName())) {
|
||||
instanceId = info.getHostName()+":"+instanceId;
|
||||
}
|
||||
return new InstanceInfo.Builder(info).setInstanceId(instanceId).build();
|
||||
}
|
||||
return instanceId == null ? hostName : instanceId;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
static class CloudInstanceInfoDeserializer extends InstanceInfoDeserializer {
|
||||
@@ -146,5 +153,12 @@ public class CloudJacksonJson extends LegacyJacksonJson {
|
||||
protected CloudInstanceInfoDeserializer(ObjectMapper mapper) {
|
||||
super(mapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstanceInfo deserialize(JsonParser jp, DeserializationContext context) throws IOException {
|
||||
InstanceInfo info = super.deserialize(jp, context);
|
||||
InstanceInfo updated = updateIfNeeded(info);
|
||||
return updated;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -233,8 +233,10 @@ public class EurekaController {
|
||||
LinkedHashMap<String, Object> instance = new LinkedHashMap<>();
|
||||
instances.add(instance);
|
||||
instance.put("id", p.first());
|
||||
instance.put("url", p.second());
|
||||
instance.put("isHref", p.second().startsWith("http"));
|
||||
String url = p.second();
|
||||
instance.put("url", url);
|
||||
boolean isHref = url != null && url.startsWith("http");
|
||||
instance.put("isHref", isHref);
|
||||
/*
|
||||
* String id = p.first(); String url = p.second(); if(url != null &&
|
||||
* url.startsWith("http")){
|
||||
|
||||
+8
-2
@@ -52,6 +52,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
|
||||
import com.netflix.appinfo.ApplicationInfoManager;
|
||||
import com.netflix.discovery.EurekaClient;
|
||||
import com.netflix.discovery.EurekaClientConfig;
|
||||
import com.netflix.discovery.converters.EurekaJacksonCodec;
|
||||
import com.netflix.discovery.converters.wrappers.CodecWrapper;
|
||||
import com.netflix.discovery.converters.wrappers.CodecWrappers;
|
||||
import com.netflix.eureka.DefaultEurekaServerContext;
|
||||
@@ -102,6 +103,7 @@ public class EurekaServerConfiguration extends WebMvcConfigurerAdapter {
|
||||
|
||||
@Value("${eureka.server.defaultOpenForTrafficCount:1}")
|
||||
private int defaultOpenForTrafficCount;
|
||||
public static final CloudJacksonJson JACKSON_JSON = new CloudJacksonJson();
|
||||
|
||||
@Bean
|
||||
public HasFeatures eurekaServerFeature() {
|
||||
@@ -128,15 +130,19 @@ public class EurekaServerConfiguration extends WebMvcConfigurerAdapter {
|
||||
return new EurekaController(this.applicationInfoManager);
|
||||
}
|
||||
|
||||
static {
|
||||
CodecWrappers.registerWrapper(JACKSON_JSON);
|
||||
EurekaJacksonCodec.setInstance(JACKSON_JSON.getCodec());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ServerCodecs serverCodecs() {
|
||||
CodecWrappers.registerWrapper(new CloudJacksonJson());
|
||||
return new CloudServerCodecs(this.eurekaServerConfig);
|
||||
}
|
||||
|
||||
private static CodecWrapper getFullJson(EurekaServerConfig serverConfig) {
|
||||
CodecWrapper codec = CodecWrappers.getCodec(serverConfig.getJsonCodecName());
|
||||
return codec == null ? CodecWrappers.getCodec(CloudJacksonJson.class) : codec;
|
||||
return codec == null ? CodecWrappers.getCodec(JACKSON_JSON.codecName()) : codec;
|
||||
}
|
||||
|
||||
private static CodecWrapper getFullXml(EurekaServerConfig serverConfig) {
|
||||
|
||||
+25
@@ -1,11 +1,23 @@
|
||||
package org.springframework.cloud.netflix.eureka.server;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.cloud.netflix.eureka.server.EurekaControllerTest.setInstance;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
import com.netflix.appinfo.ApplicationInfoManager;
|
||||
import com.netflix.appinfo.DataCenterInfo;
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.appinfo.MyDataCenterInfo;
|
||||
import com.netflix.eureka.cluster.PeerEurekaNode;
|
||||
import com.netflix.eureka.cluster.PeerEurekaNodes;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.netflix.eureka.util.StatusInfo;
|
||||
@@ -20,6 +32,19 @@ public class EurekaControllerReplicasTest {
|
||||
String authList2 = authList1+",http://user2:pwd2@test2.com";
|
||||
|
||||
String empty = new String();
|
||||
|
||||
private ApplicationInfoManager original;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
this.original = ApplicationInfoManager.getInstance();
|
||||
setInstance(mock(ApplicationInfoManager.class));
|
||||
}
|
||||
|
||||
@After
|
||||
public void teardown() throws Exception {
|
||||
setInstance(this.original);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterReplicasNoAuth() throws Exception {
|
||||
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka.server;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import com.netflix.appinfo.ApplicationInfoManager;
|
||||
import com.netflix.appinfo.DataCenterInfo;
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.appinfo.MyDataCenterInfo;
|
||||
import com.netflix.discovery.shared.Application;
|
||||
import com.netflix.eureka.EurekaServerContext;
|
||||
import com.netflix.eureka.EurekaServerContextHolder;
|
||||
import com.netflix.eureka.cluster.PeerEurekaNode;
|
||||
import com.netflix.eureka.cluster.PeerEurekaNodes;
|
||||
import com.netflix.eureka.registry.PeerAwareInstanceRegistry;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class EurekaControllerTest {
|
||||
|
||||
private ApplicationInfoManager infoManager;
|
||||
private ApplicationInfoManager original;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
PeerEurekaNodes peerEurekaNodes = mock(PeerEurekaNodes.class);
|
||||
when(peerEurekaNodes.getPeerNodesView()).thenReturn(Collections.<PeerEurekaNode>emptyList());
|
||||
|
||||
InstanceInfo instanceInfo = InstanceInfo.Builder.newBuilder()
|
||||
.setAppName("test")
|
||||
.setDataCenterInfo(new MyDataCenterInfo(DataCenterInfo.Name.MyOwn))
|
||||
.build();
|
||||
|
||||
this.infoManager = mock(ApplicationInfoManager.class);
|
||||
this.original = ApplicationInfoManager.getInstance();
|
||||
setInstance(this.infoManager);
|
||||
when(this.infoManager.getInfo()).thenReturn(instanceInfo);
|
||||
|
||||
Application myapp = new Application("myapp");
|
||||
myapp.addInstance(InstanceInfo.Builder.newBuilder()
|
||||
.setAppName("myapp")
|
||||
.setDataCenterInfo(new MyDataCenterInfo(DataCenterInfo.Name.MyOwn))
|
||||
.setInstanceId("myapp:1")
|
||||
.build());
|
||||
|
||||
ArrayList<Application> applications = new ArrayList<>();
|
||||
applications.add(myapp);
|
||||
|
||||
PeerAwareInstanceRegistry registry = mock(PeerAwareInstanceRegistry.class);
|
||||
when(registry.getSortedApplications()).thenReturn(applications);
|
||||
|
||||
EurekaServerContext serverContext = mock(EurekaServerContext.class);
|
||||
EurekaServerContextHolder.initialize(serverContext);
|
||||
when(serverContext.getRegistry()).thenReturn(registry);
|
||||
when(serverContext.getPeerEurekaNodes()).thenReturn(peerEurekaNodes);
|
||||
when(serverContext.getApplicationInfoManager()).thenReturn(this.infoManager);
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
public void teardown() throws Exception {
|
||||
setInstance(this.original);
|
||||
}
|
||||
|
||||
static void setInstance(ApplicationInfoManager infoManager) throws IllegalAccessException {
|
||||
Field instance = ReflectionUtils.findField(ApplicationInfoManager.class, "instance");
|
||||
ReflectionUtils.makeAccessible(instance);
|
||||
instance.set(null, infoManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatus() throws Exception {
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
|
||||
EurekaController controller = new EurekaController(infoManager);
|
||||
|
||||
controller.status(new MockHttpServletRequest("GET", "/"), model);
|
||||
|
||||
Map<String, Object> app = getFirst(model, "apps");
|
||||
Map<String, Object> instanceInfo = getFirst(app, "instanceInfos");
|
||||
Map<String, Object> instance = getFirst(instanceInfo, "instances");
|
||||
|
||||
assertThat("id was wrong", (String)instance.get("id"), is(equalTo("myapp:1")));
|
||||
assertThat("url was not null", instance.get("url"), is(nullValue()));
|
||||
assertThat("isHref was wrong", (Boolean)instance.get("isHref"), is(false));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> getFirst(Map<String, Object> model, String key) {
|
||||
List<Map<String, Object>> apps = (List<Map<String, Object>>) model.get(key);
|
||||
assertThat(key +" was wrong size", apps, is(hasSize(1)));
|
||||
return apps.get(0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-hystrix-amqp</artifactId>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<properties>
|
||||
|
||||
-3
@@ -114,9 +114,6 @@
|
||||
convertAvg(data, "errorPercentage", true);
|
||||
convertAvg(data, "latencyExecute_mean", false);
|
||||
convertAvg(data, "latencyTotal_mean", false);
|
||||
|
||||
// the following will break when it becomes a compound string if the property is dynamically changed
|
||||
convertAvg(data, "propertyValue_metricsRollingStatisticalWindowInMilliseconds", false);
|
||||
}
|
||||
|
||||
function convertAvg(data, key, decimal) {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-hystrix-stream</artifactId>
|
||||
|
||||
+9
-2
@@ -23,11 +23,14 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.client.actuator.HasFeatures;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.annotation.Output;
|
||||
import org.springframework.cloud.stream.config.BindingProperties;
|
||||
import org.springframework.cloud.stream.config.ChannelBindingServiceProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
import com.netflix.hystrix.HystrixCircuitBreaker;
|
||||
@@ -57,6 +60,10 @@ public class HystrixStreamAutoConfiguration {
|
||||
@Autowired
|
||||
private HystrixStreamProperties properties;
|
||||
|
||||
@Autowired
|
||||
@Output(HystrixStreamClient.OUTPUT)
|
||||
private MessageChannel outboundChannel;
|
||||
|
||||
@Bean
|
||||
public HasFeatures hystrixStreamQueueFeature() {
|
||||
return HasFeatures.namedFeature("Hystrix Stream (Queue)",
|
||||
@@ -87,8 +94,8 @@ public class HystrixStreamAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HystrixStreamTask hystrixStreamTask() {
|
||||
return new HystrixStreamTask();
|
||||
public HystrixStreamTask hystrixStreamTask(DiscoveryClient discoveryClient) {
|
||||
return new HystrixStreamTask(this.outboundChannel, discoveryClient, this.properties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+14
@@ -28,14 +28,28 @@ import lombok.Data;
|
||||
@Data
|
||||
public class HystrixStreamProperties {
|
||||
|
||||
/** Flag to indicate that Hystrix Stream is enabled. Default is true. */
|
||||
private boolean enabled = true;
|
||||
|
||||
/** Flag to indicate to prefix metric names with serviceId. Default is true. */
|
||||
private boolean prefixMetricName = true;
|
||||
|
||||
/** Flag to indicate to send the id field in the metrics. Default is true */
|
||||
private boolean sendId = true;
|
||||
|
||||
/** The destination of the stream. Destination as defined by Spring Cloud Stream. Defaults to springCloudHystrixStream */
|
||||
private String destination = HystrixConstants.HYSTRIX_STREAM_DESTINATION;
|
||||
|
||||
/** The content type of the messages. Defaults to application/json */
|
||||
private String contentType = "application/json";
|
||||
|
||||
/** How often (in ms) to send messages to the stream. Defaults to 500. */
|
||||
private long sendRate = 500;
|
||||
|
||||
/** How often to put messages in the queue. This queue drains to the stream. Defaults to 500. */
|
||||
private long gatherRate = 500;
|
||||
|
||||
/** The size of the metrics queue. This queue drains to the stream. Defaults to 1000. */
|
||||
private int size = 1000;
|
||||
|
||||
}
|
||||
|
||||
+11
-11
@@ -23,10 +23,8 @@ import java.util.Collection;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.stream.annotation.Output;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
@@ -55,24 +53,26 @@ import lombok.extern.apachecommons.CommonsLog;
|
||||
@CommonsLog
|
||||
public class HystrixStreamTask implements ApplicationContextAware {
|
||||
|
||||
@Autowired
|
||||
@Output(HystrixStreamClient.OUTPUT)
|
||||
private MessageChannel outboundChannel;
|
||||
|
||||
@Autowired
|
||||
private DiscoveryClient discoveryClient;
|
||||
|
||||
private HystrixStreamProperties properties;
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
@Autowired
|
||||
private HystrixStreamProperties properties;
|
||||
|
||||
// Visible for testing
|
||||
final LinkedBlockingQueue<String> jsonMetrics = new LinkedBlockingQueue<>(
|
||||
1000);
|
||||
final LinkedBlockingQueue<String> jsonMetrics;
|
||||
|
||||
private final JsonFactory jsonFactory = new JsonFactory();
|
||||
|
||||
public HystrixStreamTask(MessageChannel outboundChannel, DiscoveryClient discoveryClient, HystrixStreamProperties properties) {
|
||||
this.outboundChannel = outboundChannel;
|
||||
this.discoveryClient = discoveryClient;
|
||||
this.properties = properties;
|
||||
this.jsonMetrics = new LinkedBlockingQueue<>(properties.getSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
@@ -95,7 +95,7 @@ public class HystrixStreamTask implements ApplicationContextAware {
|
||||
// TODO: remove the explicit content type when s-c-stream can handle that for us
|
||||
this.outboundChannel.send(MessageBuilder.withPayload(json)
|
||||
.setHeader(MessageHeaders.CONTENT_TYPE,
|
||||
"application/json")
|
||||
this.properties.getContentType())
|
||||
.build());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
|
||||
+2
-1
@@ -61,8 +61,9 @@ public class HystrixStreamTaskTests {
|
||||
new HystrixPropertiesCommandDefault(hystrixCommandKey, HystrixCommandProperties.defaultSetter()));
|
||||
given(this.discoveryClient.getLocalServiceInstance()).willReturn(this.serviceInstance);
|
||||
|
||||
this.hystrixStreamTask.setApplicationContext(this.context);
|
||||
this.hystrixStreamTask.gatherMetrics();
|
||||
|
||||
assertThat(this.hystrixStreamTask.jsonMetrics.isEmpty(), is(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-sidecar</artifactId>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-spectator</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-turbine-stream</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-turbine</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-archaius</artifactId>
|
||||
@@ -36,6 +36,12 @@
|
||||
<dependency>
|
||||
<groupId>commons-configuration</groupId>
|
||||
<artifactId>commons-configuration</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-atlas</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-eureka-server</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-eureka</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-feign</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-hystrix</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-ribbon</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-spectator</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-turbine-amqp</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-turbine-stream</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-turbine</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.1.3.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-zuul</artifactId>
|
||||
|
||||
Reference in New Issue
Block a user