Compare commits

...
Author SHA1 Message Date
Spencer Gibb ac92b6f634 Update SNAPSHOT to 1.1.6.RELEASE 2016-09-08 09:50:49 -06:00
Niklas Herder 3f2c57cf33 Fix lastFetch status calculation.
Was previously milliseconds/seconds when calculating whether to display the message about reporting failures.
2016-09-08 09:40:58 -06:00
Spencer Gibb c93a80f550 Exclude commons-logging from archaius starter
fixes gh-126
2016-09-07 11:15:09 -06:00
Marcin Grzejszczak 89a63a095b Removed adoc footer 2016-09-07 18:03:06 +02:00
Marcin Grzejszczak 60cc0b78ce Updating mvnw for all projects 2016-09-07 17:29:02 +02:00
Spencer Gibb a6f750963e Merge pull request #1309 from garciafl/1.1.x
* garciafl-1.1.x:
  Remove RibbonCommand suffix
2016-09-01 09:08:01 -06:00
Garcia Florian d775c5cb18 Remove RibbonCommand suffix
This is a regression in HttpClientRibbonCommand

Fixes gh-1308
2016-09-01 09:01:16 -06:00
Spencer Gibb 4cb261cf76 Try and fix old static wonkiness in test. 2016-08-29 12:47:13 -06:00
Spencer Gibb d54022d22d Fix NPE when instance has no status page url.
fixes gh-1285
2016-08-29 12:27:25 -06:00
Ryan Baxter 2b64240a3c Merge pull request #1269 from william-tran/patch-1
Clarify peer replication setup of eureka servers (Fixes #1251)
2016-08-26 06:57:14 -04:00
Spencer Gibb 0b0a66f645 fix missing import. 2016-08-22 17:30:31 -06:00
Spencer Gibb 12faa11ad0 Make moving to https urls consistent.
This fixes a case where the ribbon property is set `ribbon.IsSecure=false`, but because the eureka secure port was enabled, the uri created by ribbon was prefixed with https when it shouldn't have.

All places where isSecure is need have been consolidated into RibbonUtils.

fixes gh-1270
2016-08-22 17:22:42 -06:00
Spencer Gibb b78242ce4f Feign URI tests updated to be more inclusive.
Previously with a `@FeignClient(name="http://bad_hostname")` would pass the start time test and fail later in runtime. The test now fails fast.

fixes gh-1257
2016-08-22 15:01:20 -06:00
Marcin Grzejszczak 07da94ce8c Updating ghpages for all projects 2016-08-22 13:32:14 +02:00
Marcin Grzejszczak 5cb21539b6 Updating ghpages for all projects 2016-08-19 11:46:26 +02:00
Marcin Grzejszczak bc2f89f358 Updating ghpages for all projects 2016-08-18 13:15:16 +02:00
Will Tran 4b40a45016 Fix for #1251
Example uses the same value of eureka.client.serviceUrl.defaultZone for all peers. The eureka server knows who it is and doesn't attempt to replicate to itself [1], so this simplification will work.

[1]https://github.com/Netflix/eureka/blob/v1.4.10/eureka-core/src/main/java/com/netflix/eureka/registry/PeerAwareInstanceRegistryImpl.java#L616-L619
2016-08-17 12:08:49 -04:00
Dave Syer cf76ee00f5 Remove extra (redundant) condition
A problem in Spring Boot means that the extra conditions are combined
with OR not AND, so it's a bug to have more than one until that
gets fixed (in an AnyNestedCondition).

See https://github.com/spring-projects/spring-boot/pull/6672
2016-08-17 14:02:39 +01:00
Dave Syer f9a3d9a660 Migrate test from master 2016-08-17 13:53:26 +01:00
Marcin Grzejszczak 39c66cfe7f Updating ghpages for all projects 2016-08-17 12:45:15 +02:00
Marcin Grzejszczak 4552b33a74 Deploying documentation to proper folder
What we're missing ATM is different documentation versions for different application versions. What this change does is that it's:

- finding out what is the current branch (e.g. 1.0.x)
- finding out out what is the name of the main adoc file (e.g. spring-cloud-sleuth)
- pulling the changes from gh-pages after checkout
- finding out what is the list of comma separated whitelisted branches (via the `docs.whitelisted.branches` prop)
- in gh-pages creating a folder with name of the branch  (e.g. /1.0.x)
copying all the docs/target/generated-docs/ to that folder
- if the branch from which we're calling the script is NOT master then we're changing the ${main.adoc}.html to index.html so that it's easier to access the docs (e.g. http://cloud.spring.io/spring-cloud-sleuth/1.0.x/)
2016-08-16 14:58:09 +02:00
Ryan Baxter 27e5d9e861 Fixing codacy link 2016-08-10 15:36:05 -04:00
Ryan Baxter c384bff5fc Added Codeacy badge to readme 2016-08-10 10:48:57 -04:00
Spencer Gibb ddfbfe8158 Update readme 2016-08-09 14:52:04 -06:00
Ryan Baxter d6ee25c648 Updated README with new badges 2016-08-09 16:03:39 -04:00
Ryan Baxter 92268dbe6f Support for builds with CircleCI 2016-08-09 15:33:48 -04:00
Ryan Baxter fd941835d3 Merge pull request #1224 from coveord/1.1.x
Content-length not properly set when using the FormBodyWrapperFilter
2016-08-09 11:15:42 -04:00
Jacques-Etienne Beaudet 8c4a49be98 Fix an issue where the FormBodyWrapperFilter would encode the form parameters differently than on the original request.
The FormBodyWrapperFilter handles the application/x-www-form-urlencoded. In the case of requests received by curl or javascript, the FormHttpMessageConverter will reencode the parameters differently (for example, '(' will be encoded while it's not with the javascript encodeURIComponent method).

While this doesn't create any problem, the content length was not properly set in HttpClientRibbonCommand. This causes the form params being stripped or the backend server would wait a long time for additional bytes depending on if the content length header was bigger/smaller than the actual data.
2016-08-03 14:50:03 -04:00
Ryan Baxter 9ef7dfdc53 Updated version to 1.1.6.BUILD-SNAPSHOT 2016-07-22 13:33:44 -04:00
Ryan Baxter da3d067481 Revert "Versions changes for 1.1.5.RELEASE"
This reverts commit 766f8bbab7.
2016-07-22 13:21:43 -04:00
Ryan Baxter 766f8bbab7 Versions changes for 1.1.5.RELEASE 2016-07-22 09:36:13 -04:00
Dave Syer 75dcd309e9 Actually fix the feign client validation issue
We need to still assert that the FeignClient is valid (no name and
value specified together for instance). Added missing tests.
2016-07-21 10:35:22 +01:00
Dave Syer c7eb5f570d Fix Feign validation for Spring 4.2 and 4.3
There is a utility method in Spring that we can use to merge all
the aliases. That way 4.2 and 4.3 behave the same way.

Fixes gh-1192
2016-07-21 08:26:43 +01:00
Dave Syer 2179675027 Use JUnit assertions 2016-07-21 08:26:43 +01:00
Dave Syer fd159673c4 Fix generic type signature (otherwise breaks with Spring 4.3) 2016-07-21 08:26:43 +01:00
Ryan Baxter 5d52c61c7c Merge pull request #1190 from denyska/sticky_cookie
Disable cookie management for RibbonLoadBalancingHttpClient Fixes gh-301
2016-07-15 09:48:05 -04:00
Denys Kurylenko 76baf2b914 Merge remote-tracking branch 'spring/1.1.x' into sticky_cookie 2016-07-14 17:59:28 -07:00
Denys Kurylenko 4f3b89d798 Disable cookie management for RibbonLoadBalancingHttpClient Fixes gh-301 2016-07-14 14:19:36 -07:00
Ryan Baxter d35b660eed Merge pull request #1187 from ryanjbaxter/1.1.x-upgrade-eureka-to-1.4.10
Upgrade Eureka to 1.4.10
2016-07-13 14:23:06 -04:00
Ryan Baxter fa783ccedd Upgrade Eureka to 1.4.10. Fixes #1186 2016-07-13 14:03:25 -04:00
Spencer Gibb 0cb8bf671b Remove phantom spring-cloud-netflix-zuul-server from dependencies pom
fixes gh-1175
2016-07-11 21:48:39 -06:00
Ryan Baxter 10eb85b7f0 Updated snapshot versions 2016-07-08 09:46:40 -04:00
Ryan Baxter b42ccc5d44 Revert "Updated dependencies and versions for 1.1.4 release"
This reverts commit 931f058068.
2016-07-08 09:31:26 -04:00
Ryan Baxter 931f058068 Updated dependencies and versions for 1.1.4 release 2016-07-07 16:48:59 -04:00
Ryan Baxter c1dd0a6b6d Merge pull request #1170 from ryanjbaxter/upgrade-eureka-to-1.4.9-1167
Upgrade Eureka to 1.4.9
2016-07-07 13:19:31 -04:00
Ryan Baxter c57a0e323c Upgrade Eureka to 1.4.9. Fixes #1167 2016-07-07 12:15:06 -04:00
Dave Syer e1dca45c10 Revert to snapshots 2016-06-30 08:09:22 -07:00
Dave Syer a8282dd0bd Update to 1.1.3.RELEASE 2016-06-30 08:07:45 -07:00
Spencer Gibb 9e3e749438 fix typo 2016-06-29 11:38:33 -06:00
Patrick Cornelißen 434305de19 Adds ability to disable hostname validation.
Adds property zuul.sslHostnameValidationEnabled to be able to disable the hostname validation in the simpleHostRoutingFilter

fixes gh-1057
2016-06-28 10:34:57 -06:00
Spencer Gibb a9c0fc1502 Split ProxyRequestHelper to function without Actuator.
Previously ProxyRequestHelper contained a TraceRepository field. This caused zuul apps to fail with a class not found exception if they excluded actuator. This splits TraceRepository functionality into a new TraceProxyRequestHelper that extends ProxyRequestHelper. Auto configuration creates the appropriate ProxyRequestHelper based on the existence or not of actuator classes.

fixes gh-1135
2016-06-24 16:03:59 -06:00
Spencer Gibb 9dafae6c01 add .shelf to .gitignore 2016-06-24 09:53:10 -06:00
Spencer Gibb f538444dee Add hystrix.stream.queue.size to customize queue size.
Also added descriptions to properties.

fixes gh-1131
2016-06-24 09:52:36 -06:00
Spencer Gibb 99beb9d045 Merge pull request #1127 from william-tran/schemeless-uri
* schemeless-uri:
  Without explicit client config of IsSecure, lookup from registration
2016-06-23 15:09:36 -06:00
Will Tran 4cada7c8cc Without explicit client config of IsSecure, lookup from registration
instead of defaulting to false.

Fixes gh-1126
2016-06-23 15:09:16 -06:00
Spencer Gibb 34dff916cf Change parent poms to snapshot. 2016-06-22 12:36:22 -06:00
Spencer Gibb 7e9faef2e7 Set entity contentLength to 0 for GET requests.
Zuul sets an empty entity for GET requests. The apache http client either sets transfer encoding to chunked or a content length header. This change sets the apache entity contentLength to 0 (not the header), to that in RequestContent the "content-length" header is set to 0 rathern than transfer encoding.

See gh-1042
2016-06-22 12:32:57 -06:00
Jérôme Mirc 476a5cb84c Fix hystrix dashboard TPS.
fixes gh-1113

See https://github.com/Netflix/Hystrix/issues/1251
2016-06-21 15:24:31 -06:00
Spencer Gibb cfa4302965 Set entity contentLength for HttpClientRibbonCommand.
If the content length isn't set on the entity, when http client determines which header to set, it sets transfer-encoding to chunked rather than content-length.

This fixes a missing content-length issue when using `HttpClientRibbonCommandFactory`.

See gh-1042
2016-06-20 12:42:33 -06:00
Spencer Gibb 5d6590da44 Angel eureka clients can use brixton eureka servers.
Updates so CloudJacksonJson extension is properly recognized by Eureka
Server as LegacyJacksonJson.

see gh-978
fixes gh-1111
2016-06-17 16:46:31 -06:00
73 changed files with 1807 additions and 664 deletions
+1
View File
@@ -14,3 +14,4 @@ _site/
*.ipr
*.iws
.factorypath
.shelf
+4 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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>
+3 -1
View File
@@ -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[]
+318 -42
View File
@@ -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
Vendored
+20 -15
View File
@@ -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} "$@"
+20 -34
View File
@@ -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>
+1 -1
View File
@@ -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>
@@ -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;
@@ -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) {
}
@@ -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
}
}
}
@@ -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
@@ -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) {
@@ -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);
}
}
@@ -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;
}
}
@@ -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);
}
@@ -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;
@@ -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
@@ -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) {
@@ -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>");
}
}
}
@@ -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(
@@ -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;
}
}
@@ -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);
@@ -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));
}
}
@@ -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 {
}
}
}
@@ -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() {
@@ -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);
}
}
}
@@ -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();
@@ -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;
}
}
}
@@ -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)));
}
}
@@ -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) {
@@ -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);
@@ -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();
+6 -11
View File
@@ -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>
+1 -1
View File
@@ -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>
@@ -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 {
}
}
}
@@ -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",
@@ -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;
@@ -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) {
@@ -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
+1 -1
View File
@@ -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>
@@ -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;
}
}
}
@@ -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")){
@@ -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) {
@@ -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 {
@@ -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);
}
}
+1 -1
View File
@@ -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>
@@ -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) {
+1 -1
View File
@@ -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>
@@ -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);
}
}
@@ -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;
}
@@ -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) {
@@ -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));
}
}
}
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+7 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>