mirror of
https://github.com/spring-cloud/spring-cloud-netflix.git
synced 2026-09-17 23:59:04 +00:00
Compare commits
174
Commits
v1.0.0.RELEASE
...
1.0.x
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
360dad3669 | ||
|
|
2c9c59798b | ||
|
|
ec2cab236c | ||
|
|
9e9cfdaedb | ||
|
|
e5e4701680 | ||
|
|
81a34cbcc4 | ||
|
|
9baf55b2a6 | ||
|
|
3e770881b5 | ||
|
|
81d2f2c3b5 | ||
|
|
337ab429e6 | ||
|
|
cc724c77fa | ||
|
|
a54bb6b059 | ||
|
|
0e23a67149 | ||
|
|
3190b668a3 | ||
|
|
020f05ebfe | ||
|
|
86b4c5fb16 | ||
|
|
afe8f4d0ef | ||
|
|
b59bc5fb05 | ||
|
|
4a049fb8fc | ||
|
|
8b5f6b3ccb | ||
|
|
e0b0f74316 | ||
|
|
7976719a1d | ||
|
|
1b2d498754 | ||
|
|
5743dec1f6 | ||
|
|
b618c3a5f4 | ||
|
|
3f0814fd0d | ||
|
|
ae3da1099d | ||
|
|
5010ad1156 | ||
|
|
4136e397de | ||
|
|
bf1e0d6d44 | ||
|
|
bc807aae9e | ||
|
|
727d8be2dc | ||
|
|
f98a7d5bc5 | ||
|
|
5dccd03d73 | ||
|
|
f904b52a1d | ||
|
|
ac86e7798e | ||
|
|
13a3ea071d | ||
|
|
43a5241d3c | ||
|
|
7fc9811975 | ||
|
|
b3ca7430b0 | ||
|
|
00cc64c6c5 | ||
|
|
c905822005 | ||
|
|
56fae9330f | ||
|
|
c1cb0025c1 | ||
|
|
89ebf1fa3f | ||
|
|
ad86d48c6a | ||
|
|
2c688062e7 | ||
|
|
f4a69e43eb | ||
|
|
3519ecf224 | ||
|
|
72cecacea7 | ||
|
|
e17766e916 | ||
|
|
29841f9f57 | ||
|
|
06d4d1e158 | ||
|
|
a903b5d58c | ||
|
|
25651f4835 | ||
|
|
bab74edf89 | ||
|
|
652e4b96b3 | ||
|
|
34eb36323a | ||
|
|
12bee7772f | ||
|
|
283c0f7600 | ||
|
|
0b925e5afa | ||
|
|
80206e6b38 | ||
|
|
58284a4508 | ||
|
|
2ba5c4b9ec | ||
|
|
69eaa27ccc | ||
|
|
440779bcf0 | ||
|
|
0d468b3930 | ||
|
|
0f2fbf8e1c | ||
|
|
b746ad5504 | ||
|
|
a63b825690 | ||
|
|
ce96c83401 | ||
|
|
aa002b56c4 | ||
|
|
fa84108ebf | ||
|
|
a23131f094 | ||
|
|
a0d72b3cbc | ||
|
|
23cde30063 | ||
|
|
899a577980 | ||
|
|
682ceec402 | ||
|
|
68c731d668 | ||
|
|
1a6602149d | ||
|
|
dbef27ac6b | ||
|
|
28f890989d | ||
|
|
bd808a76fa | ||
|
|
2559b76747 | ||
|
|
4255179354 | ||
|
|
bd5d00c082 | ||
|
|
8bb578ad97 | ||
|
|
3bb1562b7f | ||
|
|
68b0c91fa7 | ||
|
|
8fa9b71579 | ||
|
|
532cfd9bda | ||
|
|
94081f82ce | ||
|
|
ed692c3836 | ||
|
|
e3b42e79b6 | ||
|
|
688b577d63 | ||
|
|
85b7ccf618 | ||
|
|
eed9b996fb | ||
|
|
3809bcbc90 | ||
|
|
0587df4ae2 | ||
|
|
53c0e0a246 | ||
|
|
bad44ec8ab | ||
|
|
fde4741333 | ||
|
|
c77322348c | ||
|
|
c3f5678854 | ||
|
|
5166994f7c | ||
|
|
ce6d286d70 | ||
|
|
df8c637a57 | ||
|
|
e2fa45f960 | ||
|
|
b28fd07cdb | ||
|
|
22c261c8ce | ||
|
|
3e98c78c12 | ||
|
|
6dce075e99 | ||
|
|
3d9e998a01 | ||
|
|
55a8195a67 | ||
|
|
936a3b334e | ||
|
|
1799809562 | ||
|
|
38e25f390f | ||
|
|
a765839771 | ||
|
|
20da63c238 | ||
|
|
408910e893 | ||
|
|
45449cfb84 | ||
|
|
611c0b5c1c | ||
|
|
9699664e09 | ||
|
|
3279ae601f | ||
|
|
e1b6ddf563 | ||
|
|
242419726d | ||
|
|
3794213aaf | ||
|
|
6a0737dacf | ||
|
|
873854810a | ||
|
|
555b1f1c06 | ||
|
|
5961931caf | ||
|
|
814f7e9283 | ||
|
|
20bb1af655 | ||
|
|
370ce804e3 | ||
|
|
2d18706219 | ||
|
|
8bfef044d2 | ||
|
|
28db47c72a | ||
|
|
55d46eb6c7 | ||
|
|
5f9c3e6073 | ||
|
|
3714cee51d | ||
|
|
d0021f0415 | ||
|
|
f5b6c58f30 | ||
|
|
f0d8c86b51 | ||
|
|
76d0c018ee | ||
|
|
e60a0fc4fb | ||
|
|
9dc2821bc9 | ||
|
|
924363ca1c | ||
|
|
3515112cb2 | ||
|
|
cdc5681694 | ||
|
|
3ba7cfbff2 | ||
|
|
1c54e6b937 | ||
|
|
869e3ec92f | ||
|
|
edbb0acc7c | ||
|
|
98ac81bda2 | ||
|
|
444f3c8010 | ||
|
|
b03b2f5629 | ||
|
|
72a182c914 | ||
|
|
da24fa6d20 | ||
|
|
369a00cb71 | ||
|
|
3445d093c7 | ||
|
|
f3ced9e0ed | ||
|
|
cc2159f77b | ||
|
|
9fe69cc80a | ||
|
|
a2b0b70b55 | ||
|
|
9357bdd9db | ||
|
|
0391cfff65 | ||
|
|
da4f5d3454 | ||
|
|
e2583f5380 | ||
|
|
df7abd1bc9 | ||
|
|
164c9f8755 | ||
|
|
384fad6c40 | ||
|
|
4bd7c3bdcf | ||
|
|
4e795ea783 | ||
|
|
e3a20d5d6b |
@@ -10,3 +10,4 @@ target/
|
||||
_site/
|
||||
.idea
|
||||
*.iml
|
||||
.factorypath
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
-Xmx1024m -XX:CICompilerCount=1 -XX:TieredStopAtLevel=1 -Djava.security.egd=file:/dev/./urandom
|
||||
@@ -0,0 +1 @@
|
||||
-DaltSnapshotDeploymentRepository=repo.spring.io::default::https://repo.spring.io/libs-snapshot-local -P spring
|
||||
Vendored
BIN
Binary file not shown.
+1
@@ -0,0 +1 @@
|
||||
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip
|
||||
+5
-5
@@ -15,7 +15,7 @@
|
||||
<repository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>http://repo.spring.io/libs-snapshot-local</url>
|
||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
@@ -23,7 +23,7 @@
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>http://repo.spring.io/libs-milestone-local</url>
|
||||
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
@@ -31,7 +31,7 @@
|
||||
<repository>
|
||||
<id>spring-releases</id>
|
||||
<name>Spring Releases</name>
|
||||
<url>http://repo.spring.io/release</url>
|
||||
<url>https://repo.spring.io/release</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
@@ -41,7 +41,7 @@
|
||||
<pluginRepository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>http://repo.spring.io/libs-snapshot-local</url>
|
||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
@@ -49,7 +49,7 @@
|
||||
<pluginRepository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>http://repo.spring.io/libs-milestone-local</url>
|
||||
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
|
||||
+3
-1
@@ -15,8 +15,10 @@ before_install:
|
||||
- gem install asciidoctor
|
||||
install:
|
||||
- mvn --settings .settings.xml install -P docs -q -U -DskipTests=true -Dmaven.test.redirectTestOutputToFile=true
|
||||
- ./docs/src/main/asciidoc/ghpages.sh
|
||||
script:
|
||||
- ./docs/src/main/asciidoc/ghpages.sh
|
||||
- git status
|
||||
- ls -a
|
||||
- '[ "${TRAVIS_PULL_REQUEST}" != "false" ] || mvn --settings .settings.xml deploy -P release -nsu -Dmaven.test.redirectTestOutputToFile=true'
|
||||
- '[ "${TRAVIS_PULL_REQUEST}" = "false" ] || mvn --settings .settings.xml install -nsu -Dmaven.test.redirectTestOutputToFile=true'
|
||||
env:
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
require 'asciidoctor'
|
||||
require 'erb'
|
||||
|
||||
options = {:mkdirs => true, :safe => :unsafe, :attributes => 'linkcss'}
|
||||
options = {:mkdirs => true, :safe => :unsafe, :attributes => ['linkcss', 'allow-uri-read']}
|
||||
|
||||
guard 'shell' do
|
||||
watch(/^docs\/[A-Za-z].*\.adoc$/) {|m|
|
||||
watch(/^docs\/[A-Z-a-z][^#]*\.adoc$/) {|m|
|
||||
Asciidoctor.load_file('docs/src/main/asciidoc/README.adoc', :to_file => './README.adoc', safe: :safe, parse: false, attributes: 'allow-uri-read')
|
||||
Asciidoctor.render_file('docs/src/main/asciidoc/spring-cloud-netflix.adoc', options.merge(:to_dir => 'target/generated-docs'))
|
||||
}
|
||||
|
||||
+14
-11
@@ -18,15 +18,17 @@ Intelligent Routing (Zuul) and Client Side Load Balancing (Ribbon).
|
||||
* Circuit Breaker: embedded Hystrix dashboard with declarative Java configuration
|
||||
* Declarative REST Client: Feign creates a dynamic implementation of an interface decorated with JAX-RS or Spring MVC annotations
|
||||
* Client Side Load Balancer: Ribbon
|
||||
* External Configuration: a bridge from the Sprnig Environment to Archaius (enables native configuration of Netflix components using Spring Boot conventions)
|
||||
* External Configuration: a bridge from the Spring Environment to Archaius (enables native configuration of Netflix components using Spring Boot conventions)
|
||||
* Router and Filter: automatic regsitration of Zuul filters, and a simple convention over configuration approach to reverse proxy creation
|
||||
|
||||
== Building
|
||||
|
||||
:jdkversion: 1.8
|
||||
|
||||
=== Basic Compile and Test
|
||||
|
||||
To build the source you will need to install
|
||||
http://maven.apache.org/run-maven/index.html[Apache Maven] v3.0.6 or above and JDK 1.7.
|
||||
https://maven.apache.org/run-maven/index.html[Apache Maven] v3.0.6 or above and JDK {jdkversion}.
|
||||
|
||||
Spring Cloud uses Maven for most build-related activities, and you
|
||||
should be able to get off the ground quite quickly by cloning the
|
||||
@@ -53,7 +55,7 @@ credentials and you already have those.
|
||||
If you need mongo, rabbit or redis, see the README in the https://github.com/spring-cloud-samples/scripts[scripts
|
||||
demo repository] for
|
||||
instructions. For example consider using the "fig.yml" with
|
||||
http://www.fig.sh/[Fig] to run them in Docker containers.
|
||||
https://www.fig.sh/[Fig] to run them in Docker containers.
|
||||
|
||||
=== Documentation
|
||||
|
||||
@@ -92,7 +94,7 @@ added after the original pull request but before a merge.
|
||||
the ``Importing into eclipse'' instructions below you should get project specific
|
||||
formatting automatically. You can also import formatter settings using the
|
||||
`eclipse-code-formatter.xml` file from the `eclipse` folder. If using IntelliJ, you can
|
||||
use the http://plugins.jetbrains.com/plugin/6546[Eclipse Code Formatter Plugin]
|
||||
use the https://plugins.jetbrains.com/plugin/6546[Eclipse Code Formatter Plugin]
|
||||
to import the same file.
|
||||
* Make sure all new `.java` files to have a simple Javadoc class comment with at least an
|
||||
`@author` tag identifying you, and preferably at least a paragraph on what the class is
|
||||
@@ -108,13 +110,13 @@ added after the original pull request but before a merge.
|
||||
|
||||
=== Working with the code
|
||||
If you don't have an IDE preference we would recommend that you use
|
||||
http://www.springsource.com/developer/sts[Spring Tools Suite] or
|
||||
http://eclipse.org[Eclipse] when working with the code. We use the
|
||||
http://eclipse.org/m2e/[m2eclipe] eclipse plugin for maven support. Other IDEs and tools
|
||||
https://www.springsource.com/developer/sts[Spring Tools Suite] or
|
||||
https://eclipse.org[Eclipse] when working with the code. We use the
|
||||
https://eclipse.org/m2e/[m2eclipe] eclipse plugin for maven support. Other IDEs and tools
|
||||
should also work without issue.
|
||||
|
||||
==== Importing into eclipse with m2eclipse
|
||||
We recommend the http://eclipse.org/m2e/[m2eclipe] eclipse plugin when working with
|
||||
We recommend the https://eclipse.org/m2e/[m2eclipe] eclipse plugin when working with
|
||||
eclipse. If you don't already have m2eclipse installed it is available from the "eclipse
|
||||
marketplace".
|
||||
|
||||
@@ -133,7 +135,7 @@ from the `file` menu.
|
||||
==== Adding Project Lombok Agent
|
||||
|
||||
Spring Cloud uses [Project
|
||||
Lombok](http://projectlombok.org/features/index.html) to generate
|
||||
Lombok](https://projectlombok.org/features/index.html) to generate
|
||||
getters and setters etc. Compiling from the command line this
|
||||
shouldn't cause any problems, but in an IDE you need to add an agent
|
||||
to the JVM. Full instructions can be found in the Lombok website. The
|
||||
@@ -153,6 +155,7 @@ The method getLocation() is undefined for the type ProxyRouteLocator.ProxyRouteS
|
||||
==== Importing into other IDEs
|
||||
Maven is well supported by most Java IDEs. Refer to you vendor documentation.
|
||||
|
||||
|
||||
== Contributing
|
||||
|
||||
Spring Cloud is released under the non-restrictive Apache 2.0 license. If you would like
|
||||
@@ -176,7 +179,7 @@ added after the original pull request but before a merge.
|
||||
`eclipse-code-formatter.xml` file from the
|
||||
https://github.com/spring-cloud/build/tree/master/eclipse-coding-conventions.xml[Spring
|
||||
Cloud Build] project. If using IntelliJ, you can use the
|
||||
http://plugins.jetbrains.com/plugin/6546[Eclipse Code Formatter
|
||||
https://plugins.jetbrains.com/plugin/6546[Eclipse Code Formatter
|
||||
Plugin] to import the same file.
|
||||
* Make sure all new `.java` files to have a simple Javadoc class comment with at least an
|
||||
`@author` tag identifying you, and preferably at least a paragraph on what the class is
|
||||
@@ -189,6 +192,6 @@ added after the original pull request but before a merge.
|
||||
* A few unit tests would help a lot as well -- someone has to do it.
|
||||
* If no-one else is using your branch, please rebase it against the current master (or
|
||||
other target branch in the main project).
|
||||
* When writing a commit message please follow http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html[these conventions],
|
||||
* When writing a commit message please follow https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html[these conventions],
|
||||
if you are fixing an existing issue please add `Fixes gh-XXXX` at the end of the commit
|
||||
message (where XXXX is the issue number).
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* Asciidoctor default stylesheet | MIT License | http://asciidoctor.org */
|
||||
/* Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */
|
||||
/* Remove the comments around the @import statement below when using this as a custom stylesheet */
|
||||
/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic|Noto+Serif:400,400italic,700,700italic|Droid+Sans+Mono:400";*/
|
||||
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}
|
||||
|
||||
+2
-2
@@ -17,6 +17,6 @@ Intelligent Routing (Zuul) and Client Side Load Balancing (Ribbon).
|
||||
* Circuit Breaker: embedded Hystrix dashboard with declarative Java configuration
|
||||
* Declarative REST Client: Feign creates a dynamic implementation of an interface decorated with JAX-RS or Spring MVC annotations
|
||||
* Client Side Load Balancer: Ribbon
|
||||
* External Configuration: a bridge from the Sprnig Environment to Archaius (enabls native configuration of Netflix components using Spring Boot conventions)
|
||||
* Router and Filter: automatic regsitration of Zuul filters,a nd a simple convention over configuration approach to reverse proxy creation
|
||||
* External Configuration: a bridge from the Spring Environment to Archaius (enables native configuration of Netflix components using Spring Boot conventions )
|
||||
* Router and Filter: automatic registration of Zuul filters, and a simple convention over configuration approach to reverse proxy creation
|
||||
|
||||
|
||||
+14
-2
@@ -1,21 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-build</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.4.RELEASE</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-docs</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>Spring Cloud Netflix Docs</name>
|
||||
<version>1.0.8.BUILD-SNAPSHOT</version>
|
||||
<description>Spring Cloud Docs</description>
|
||||
<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>
|
||||
<plugin>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>docs</id>
|
||||
|
||||
@@ -10,12 +10,12 @@ include::intro.adoc[]
|
||||
* Circuit Breaker: embedded Hystrix dashboard with declarative Java configuration
|
||||
* Declarative REST Client: Feign creates a dynamic implementation of an interface decorated with JAX-RS or Spring MVC annotations
|
||||
* Client Side Load Balancer: Ribbon
|
||||
* External Configuration: a bridge from the Sprnig Environment to Archaius (enables native configuration of Netflix components using Spring Boot conventions)
|
||||
* External Configuration: a bridge from the Spring Environment to Archaius (enables native configuration of Netflix components using Spring Boot conventions)
|
||||
* Router and Filter: automatic regsitration of Zuul filters, and a simple convention over configuration approach to reverse proxy creation
|
||||
|
||||
== Building
|
||||
|
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/building.adoc[]
|
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/building-jdk8.adoc[]
|
||||
|
||||
== Contributing
|
||||
|
||||
|
||||
@@ -1,46 +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
|
||||
|
||||
# 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
|
||||
# https://stackoverflow.com/questions/1593051/how-to-programmatically-determine-the-current-checked-out-git-branch
|
||||
# If there is a branch already passed will reuse it - otherwise will try to find it
|
||||
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 master
|
||||
###################################################################
|
||||
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 .
|
||||
git add -A $file
|
||||
# Switch to gh-pages branch to sync it with current branch
|
||||
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}]"
|
||||
# https://stackoverflow.com/questions/29300806/a-bash-script-to-check-if-a-string-is-present-in-a-comma-separated-list-of-strin
|
||||
if [[ ",${WHITELISTED_BRANCHES_VALUE}," = *",${CURRENT_BRANCH},"* ]] ; then
|
||||
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 commit -a -m "Sync docs from master to gh-pages"
|
||||
|
||||
# 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!
|
||||
###################################################################
|
||||
git push origin gh-pages
|
||||
|
||||
# Finally, switch back to the master branch and exit block
|
||||
git checkout master
|
||||
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
|
||||
@@ -1,7 +1,8 @@
|
||||
:github-tag: master
|
||||
:github-tag: 1.0.x
|
||||
:github-repo: spring-cloud/spring-cloud-netflix
|
||||
:github-raw: http://raw.github.com/{github-repo}/{github-tag}
|
||||
:github-code: http://github.com/{github-repo}/tree/{github-tag}
|
||||
:github-raw: https://raw.github.com/{github-repo}/{github-tag}
|
||||
:github-code: https://github.com/{github-repo}/tree/{github-tag}
|
||||
:nofooter:
|
||||
= Spring Cloud Netflix
|
||||
|
||||
include::intro.adoc[]
|
||||
@@ -12,7 +13,7 @@ Service Discovery is one of the key tenets of a microservice based architecture.
|
||||
|
||||
=== Registering with Eureka
|
||||
|
||||
When a client registers with Eureka, it provide meta-data about itself
|
||||
When a client registers with Eureka, it provides meta-data about itself
|
||||
such as host and port, health indicator URL, home page etc. Eureka
|
||||
receives heartbeat messages from each instance belonging to a service.
|
||||
If the heartbeat fails over a configurable timetable, the instance is
|
||||
@@ -33,7 +34,7 @@ public class Application {
|
||||
public String home() {
|
||||
return "Hello world";
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(Application.class).web(true).run(args);
|
||||
}
|
||||
@@ -107,14 +108,30 @@ Cloudfoundry has a global router so that all instances of the same app have the
|
||||
----
|
||||
eureka:
|
||||
instance:
|
||||
hostname: ${vcap.application.uris[0]}
|
||||
nonSecurePort: 80
|
||||
metadataMap:
|
||||
hostname: ${vcap.application.uris[0]}
|
||||
nonSecurePort: 80
|
||||
instanceId: ${vcap.application.instance_id:${spring.application.name}:${spring.application.instance_id:${server.port}}}
|
||||
----
|
||||
|
||||
Depending on the way the security rules are set up in your Cloudfoundry instance, you might be able to register and use the IP address of the host VM for direct service-to-service calls. This feature is not (yet) available on Pivotal Web Services (https://run.pivotal.io[PWS]).
|
||||
|
||||
==== Using Eureka on AWS
|
||||
|
||||
If the application is planned to be deployed to an AWS cloud, then the Eureka instance will have to be configured to be Amazon aware and this can be done by customizing the {github-code}/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaInstanceConfigBean.java[EurekaInstanceConfigBean] the following way:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@Bean
|
||||
@Profile("!default")
|
||||
public EurekaInstanceConfigBean eurekaInstanceConfig() {
|
||||
EurekaInstanceConfigBean b = new EurekaInstanceConfigBean();
|
||||
AmazonInfo info = AmazonInfo.Builder.newBuilder().autoBuild("eureka");
|
||||
b.setDataCenterInfo(info);
|
||||
return b;
|
||||
}
|
||||
----
|
||||
|
||||
==== Making the Eureka Instance ID Unique
|
||||
|
||||
By default a eureka instance is registered with an ID that is equal to its host name (i.e. only one service per host). Using Spring Cloud you can override this by providing a unique identifier in `eureka.instance.metadataMap.instanceId`. For example:
|
||||
@@ -127,7 +144,7 @@ eureka:
|
||||
instanceId: ${spring.application.name}:${spring.application.instance_id:${random.value}}
|
||||
----
|
||||
|
||||
With this meatdata, and multiple service instances deployed on
|
||||
With this metadata, and multiple service instances deployed on
|
||||
localhost, the random value will kick in there to make the instance
|
||||
unique. In Cloudfoundry the `spring.application.instance_id` will be
|
||||
populated automatically in a Spring Boot Actuator application, so the
|
||||
@@ -137,7 +154,9 @@ random value will not be needed.
|
||||
|
||||
Once you have an app that is `@EnableEurekaClient` you can use it to
|
||||
discover service instances from the <<spring-cloud-eureka-server,
|
||||
Eureka Server>>. One way to do that is to use the native `DiscoveryClient`, e.g.
|
||||
Eureka Server>>. One way to do that is to use the native
|
||||
`com.netflix.discovery.DiscoveryClient` (as opposed to the Spring
|
||||
Cloud `DiscoveryClient`), e.g.
|
||||
|
||||
----
|
||||
@Autowired
|
||||
@@ -158,7 +177,7 @@ not be started yet). It is initialized in a `SmartLifecycle` (with
|
||||
another `SmartLifecycle` with higher phase.
|
||||
====
|
||||
|
||||
=== Alternatives to the DiscoveryClient
|
||||
=== Alternatives to the native Netflix DiscoveryClient
|
||||
|
||||
You don't have to use the raw Netflix `DiscoveryClient` and usually it
|
||||
is more convenient to use it behind a wrapper of some sort. Spring
|
||||
@@ -167,9 +186,26 @@ builder) and also <<spring-cloud-ribbon, Spring `RestTemplate`>> using
|
||||
the logical Eureka service identifiers (VIPs) instead of physical
|
||||
URLs. To configure Ribbon with a fixed list of physical servers you
|
||||
can simply set `<client>.ribbon.listOfServers` to a comma-separated
|
||||
list of physical addresses (or hostnames), where `<client>` is the ID
|
||||
list of physical addresses (or hostnames), where `<client>` is the ID
|
||||
of the client.
|
||||
|
||||
You can also use the `org.springframework.cloud.client.discovery.DiscoveryClient`
|
||||
which provides a simple API for discovery clients that is not specific
|
||||
to Netflix, e.g.
|
||||
|
||||
----
|
||||
@Autowired
|
||||
private DiscoveryClient discoveryClient;
|
||||
|
||||
public String serviceUrl() {
|
||||
List<ServiceInstance> list = client.getInstances("STORES");
|
||||
if (list != null && list.size() > 0 ) {
|
||||
return list.get(0).getUri();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
----
|
||||
|
||||
=== Why is it so Slow to Register a Service?
|
||||
|
||||
Being an instance also involves a periodic heartbeat to the registry
|
||||
@@ -186,12 +222,11 @@ assumptions about the lease renewal period.
|
||||
[[spring-cloud-eureka-server]]
|
||||
== Service Discovery: Eureka Server
|
||||
|
||||
Example eureka server:
|
||||
Example eureka server (e.g. using spring-cloud-starter-eureka-server to set up the classpath):
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootApplication
|
||||
@EnableEurekaServer
|
||||
public class Application {
|
||||
|
||||
@@ -207,69 +242,27 @@ normal Eureka functionality under `/eureka/*`.
|
||||
|
||||
Eureka background reading: see https://github.com/cfregly/fluxcapacitor/wiki/NetflixOSS-FAQ#eureka-service-discovery-load-balancer[flux capacitor] and https://groups.google.com/forum/?fromgroups#!topic/eureka_netflix/g3p2r7gHnN0[google group discussion].
|
||||
|
||||
|
||||
[TIP]
|
||||
====
|
||||
You can run the Eureka server as an executable JAR (or WAR) using the
|
||||
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#build-tool-plugins[Spring
|
||||
Boot build tools], but to avoid problems with classpath scanning in
|
||||
Jersey 1.x you have to tell the build plugins to unpack the jars that
|
||||
contain JAX-RS resources, e.g. (for Maven)
|
||||
|
||||
.pom.xml
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<requiresUnpack>
|
||||
<dependency>
|
||||
<groupId>com.netflix.eureka</groupId>
|
||||
<artifactId>eureka-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.eureka</groupId>
|
||||
<artifactId>eureka-client</artifactId>
|
||||
</dependency>
|
||||
</requiresUnpack>
|
||||
</configuration>
|
||||
</plugin>
|
||||
----
|
||||
|
||||
or with Gradle
|
||||
Due to Gradle's dependency resolution rules and the lack of a parent bom feature, simply depending on spring-cloud-starter-eureka-server can cause failures on application startup. To remedy this the Spring dependency management plugin must be added and the Spring cloud starter parent bom must be imported like so:
|
||||
|
||||
.build.gradle
|
||||
[source,java,indent=0]
|
||||
----
|
||||
apply plugin: 'spring-boot'
|
||||
springBoot {
|
||||
requiresUnpack = ['com.netflix.eureka:eureka-core','com.netflix.eureka:eureka-client']
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath "io.spring.gradle:dependency-management-plugin:0.4.0.RELEASE"
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
The Eureka server is tied to log4j and doesn't work with logback,
|
||||
so the dependency configuration
|
||||
has to be tweaked compared to a normal Spring Boot app. The
|
||||
`spring-cloud-starter-eureka-server` does this for you, but if you
|
||||
add logback transitively through another dependency you will need to
|
||||
exclude it manually, e.g. in Maven
|
||||
apply plugin: "io.spring.dependency-management"
|
||||
|
||||
.pom.xml
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom 'org.springframework.cloud:spring-cloud-starter-parent:1.0.0.RELEASE'
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
@@ -306,7 +299,7 @@ server:
|
||||
eureka:
|
||||
instance:
|
||||
hostname: localhost
|
||||
client:
|
||||
client:
|
||||
registerWithEureka: false
|
||||
fetchRegistry: false
|
||||
serviceUrl:
|
||||
@@ -332,7 +325,7 @@ spring:
|
||||
eureka:
|
||||
instance:
|
||||
hostname: peer1
|
||||
client:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://peer2/eureka/
|
||||
|
||||
@@ -342,7 +335,7 @@ spring:
|
||||
eureka:
|
||||
instance:
|
||||
hostname: peer2
|
||||
client:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://peer1/eureka/
|
||||
----
|
||||
@@ -371,7 +364,7 @@ IP Address rather than its hostname.
|
||||
|
||||
== Circuit Breaker: Hystrix Clients
|
||||
|
||||
Netflix has created a library called https://github.com/Netflix/Hystrix[Hystrix] that implements the http://martinfowler.com/bliki/CircuitBreaker.html[circuit breaker pattern]. In a microservice architecture it is common to have multiple layers of service calls.
|
||||
Netflix has created a library called https://github.com/Netflix/Hystrix[Hystrix] that implements the https://martinfowler.com/bliki/CircuitBreaker.html[circuit breaker pattern]. In a microservice architecture it is common to have multiple layers of service calls.
|
||||
|
||||
.Microservice Graph
|
||||
image::HystrixGraph.png[]
|
||||
@@ -386,9 +379,8 @@ Having an open circuit stops cascading failures and allows overwhelmed or failin
|
||||
Example boot app:
|
||||
|
||||
----
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@EnableHystrix
|
||||
@SpringBootApplication
|
||||
@EnableCircuitBreaker
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
@@ -425,8 +417,28 @@ https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-javanica#
|
||||
for more details. See the https://github.com/Netflix/Hystrix/wiki/Configuration[Hystrix wiki]
|
||||
for details on the properties available.
|
||||
|
||||
### Propagating the Security Context or using Spring Scopes
|
||||
|
||||
If you want some thread local context to propagate into a `@HystrixCommand` the default declaration will not work because it executes the command in a thread pool (in case of timeouts). You can switch Hystrix to use the same thread as the caller using some configuration, or directly in the annotation, by asking it to use a different "Isolation Strategy". For example:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@HystrixCommand(fallbackMethod = "stubMyService",
|
||||
commandProperties = {
|
||||
@HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE")
|
||||
}
|
||||
)
|
||||
...
|
||||
----
|
||||
|
||||
The same thing applies if you are using `@SessionScope` or `@RequestScope`. You will know when you need to do this because of a runtime exception that says it can't find the scoped context.
|
||||
|
||||
In particular you might be interested
|
||||
|
||||
### Health Indicator
|
||||
|
||||
The state of the connected circuit breakers are also exposed in the
|
||||
`/health` endpoint of the calling application.
|
||||
`/health` endpoint of the calling application.
|
||||
|
||||
[source,json,indent=0]
|
||||
----
|
||||
@@ -464,9 +476,9 @@ To run the Hystrix Dashboard annotate your Spring Boot main class with `@EnableH
|
||||
|
||||
=== Turbine
|
||||
|
||||
Looking at an individual instances Hystrix data is not very useful in terms of the overall health of the system. https://github.com/Netflix/Turbine[Turbine] is an application that aggregates all of the relevant `/hystrix.stream` endpoints into a combined `/turbine.stream` for use in the Hystrix Dashboard. Individual instances are located via Eureka. Running Turbine is as simple as annotating your main class with the `@EnableTurbine` annotation. All of the documented configuration properties from https://github.com/Netflix/Turbine/wiki/Configuration-(1.x)[the Turbine 1 wiki] apply. The only difference is that the `turbine.instanceUrlSuffix` does not need the port prepended as this is handled automatically unless `turbine.instanceInsertPort=false`.
|
||||
Looking at an individual instances Hystrix data is not very useful in terms of the overall health of the system. https://github.com/Netflix/Turbine[Turbine] is an application that aggregates all of the relevant `/hystrix.stream` endpoints into a combined `/turbine.stream` for use in the Hystrix Dashboard. Individual instances are located via Eureka. Running Turbine is as simple as annotating your main class with the `@EnableTurbine` annotation (e.g. using spring-cloud-starter-turbine to set up the classpath). All of the documented configuration properties from https://github.com/Netflix/Turbine/wiki/Configuration-(1.x)[the Turbine 1 wiki] apply. The only difference is that the `turbine.instanceUrlSuffix` does not need the port prepended as this is handled automatically unless `turbine.instanceInsertPort=false`.
|
||||
|
||||
Configuration key `turbine.appConfig` is a list of eureka serviceId's that turbine will use to lookup instances. The turbine stream is then used in the Hystrix dashboard using a url that looks like: `http://my.turbine.sever:8080/turbine.stream?cluster=<CLUSTERNAME>` (the cluster parameter can be omitted if the name is "default"). The `cluster` parameter must match an entry in `turbine.aggregator.clusterConfig`. Value returned from eureka are uppercase, thus we expect this example to work if there is an app registered with Eureka called "customers":
|
||||
The configuration key `turbine.appConfig` is a list of eureka serviceIds that turbine will use to lookup instances. The turbine stream is then used in the Hystrix dashboard using a url that looks like: `https://my.turbine.sever:8080/turbine.stream?cluster=<CLUSTERNAME>` (the cluster parameter can be omitted if the name is "default"). The `cluster` parameter must match an entry in `turbine.aggregator.clusterConfig`. Values returned from eureka are uppercase, thus we expect this example to work if there is an app registered with Eureka called "customers":
|
||||
|
||||
----
|
||||
turbine:
|
||||
@@ -475,7 +487,19 @@ turbine:
|
||||
appConfig: customers
|
||||
----
|
||||
|
||||
The `clusterName` can be customized by a SPEL expression in `turbine.clusterNameExpression`. For example, `turbine.clusterNameExpression=aSGName` would get the cluster name from the AWS ASG name. To use the "default" cluster for all apps you need a string literal expression (with single quotes):
|
||||
The `clusterName` can be customized by a SPEL expression in `turbine.clusterNameExpression` with root an instance of `InstanceInfo`. The default value is `appName`, which means that the Eureka serviceId ends up as the cluster key (i.e. the `InstanceInfo` for customers has an `appName` of "CUSTOMERS"). A different example would be `turbine.clusterNameExpression=aSGName`, which would get the cluster name from the AWS ASG name. Another example:
|
||||
|
||||
----
|
||||
turbine:
|
||||
aggregator:
|
||||
clusterConfig: SYSTEM,USER
|
||||
appConfig: customers,stores,ui,admin
|
||||
clusterNameExpression: metadata['cluster']
|
||||
----
|
||||
|
||||
In this case, the cluster name from 4 services is pulled from their metadata map, and is expected to have values that include "SYSTEM" and "USER".
|
||||
|
||||
To use the "default" cluster for all apps you need a string literal expression (with single quotes):
|
||||
|
||||
----
|
||||
turbine:
|
||||
@@ -519,12 +543,12 @@ so if you are using `@FeignClient` then this section also applies.
|
||||
|
||||
A central concept in Ribbon is that of the named client. Each load
|
||||
balancer is part of an ensemble of components that work together to
|
||||
contact a remote server on demend, and the ensemble has a name that
|
||||
contact a remote server on demand, and the ensemble has a name that
|
||||
you give it as an application developer (e.g. using the `@FeignClient`
|
||||
annotation). Spring Cloud creates a new ensemble as an
|
||||
`ApplicationContext` on demand for each named client using
|
||||
`RibbonClientConfiguration`. This contains (amongst other things) an
|
||||
`ILoadBalancer`, a `RestClient`, and a `ServerListFilter`.
|
||||
`ILoadBalancer`, a `RestClient`, and a `ServerListFilter`.
|
||||
|
||||
=== Customizing the Ribbon Client
|
||||
|
||||
@@ -551,6 +575,14 @@ In this case the client is composed from the components already in
|
||||
`RibbonClientConfiguration` together with any in `FooConfiguration`
|
||||
(where the latter generally will override the former).
|
||||
|
||||
WARNING: The `FooConfiguration` has to be `@Configuration` but take
|
||||
care that it is not in a `@ComponentScan` for the main application
|
||||
context, otherwise it will be shared by all the `@RibbonClients`. If
|
||||
you use `@ComponentScan` (or `@SpringBootApplication`) you need to
|
||||
take steps to avoid it being included (for instance put it in a
|
||||
separate, non-overlapping package, or specify the packages to scan
|
||||
explicitly in the `@ComponentScan`).
|
||||
|
||||
Spring Cloud Netflix provides the following beans by default for ribbon
|
||||
(`BeanType` beanName: `ClassName`):
|
||||
|
||||
@@ -615,6 +647,18 @@ stores:
|
||||
listOfServers: example.com,google.com
|
||||
----
|
||||
|
||||
=== Example: Disable Eureka use in Ribbon
|
||||
|
||||
Setting the property `ribbon.eureka.enabled = false` will explicitly
|
||||
disable the use of Eureka in Ribbon.
|
||||
|
||||
.application.yml
|
||||
----
|
||||
ribbon:
|
||||
eureka:
|
||||
enabled: false
|
||||
----
|
||||
|
||||
=== Using the Ribbon API Directly
|
||||
|
||||
You can also use the `LoadBalancerClient` directly. Example:
|
||||
@@ -633,30 +677,6 @@ public class MyClass {
|
||||
}
|
||||
----
|
||||
|
||||
=== Spring RestTemplate as a Ribbon Client
|
||||
|
||||
You can use Ribbon indirectly via an autoconfigured `RestTemplate`
|
||||
(provided Spring Cloud and Ribbon are both on the classpath):
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
public class MyClass {
|
||||
@Autowired
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
public String doOtherStuff() {
|
||||
String results = restTemplate.getForObject("http://stores/stores", String.class);
|
||||
return results;
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
The URI is inspected to see if it has a full host name, or a virtual
|
||||
one. If it is virtual the Ribbon client is used to create a full
|
||||
physical address. See
|
||||
{github-code}/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java[RibbonAutoConfiguration]
|
||||
for details of how the `RestTemplate` is set up.
|
||||
|
||||
[[spring-cloud-feign]]
|
||||
== Declarative REST Client: Feign
|
||||
|
||||
@@ -708,7 +728,7 @@ in your external configuration (see
|
||||
|
||||
== External Configuration: Archaius
|
||||
|
||||
https://github.com/Netflix/archaius[Archaius] is the Netflix client side configuration library. It is the library used by all of the Netflix OSS components for configuration. Archaius is an extension of the http://commons.apache.org/proper/commons-configuration[Apache Commons Configuration] project. It allows updates to configuration by either polling a source for changes or for a source to push changes to the client. Archaius uses Dynamic<Type>Property classes as handles to properties.
|
||||
https://github.com/Netflix/archaius[Archaius] is the Netflix client side configuration library. It is the library used by all of the Netflix OSS components for configuration. Archaius is an extension of the https://commons.apache.org/proper/commons-configuration[Apache Commons Configuration] project. It allows updates to configuration by either polling a source for changes or for a source to push changes to the client. Archaius uses Dynamic<Type>Property classes as handles to properties.
|
||||
|
||||
.Archaius Example
|
||||
[source,java]
|
||||
@@ -730,7 +750,7 @@ Archaius has its own set of configuration files and loading priorities. Spring
|
||||
|
||||
Routing in an integral part of a microservice architecture. For example, `/` may be mapped to your web application, `/api/users` is mapped to the user service and `/api/shop` is mapped to the shop service. https://github.com/Netflix/zuul[Zuul] is a JVM based router and server side load balancer by Netflix.
|
||||
|
||||
http://www.slideshare.net/MikeyCohen1/edge-architecture-ieee-international-conference-on-cloud-engineering-32240146/27[Netflix uses Zuul] for the following:
|
||||
https://www.slideshare.net/MikeyCohen1/edge-architecture-ieee-international-conference-on-cloud-engineering-32240146/27[Netflix uses Zuul] for the following:
|
||||
|
||||
* Authentication
|
||||
* Insights
|
||||
@@ -773,7 +793,7 @@ configured routes map, then it will be unignored. Example:
|
||||
[source,yaml]
|
||||
----
|
||||
zuul:
|
||||
ignoredServices: *
|
||||
ignoredServices: *
|
||||
routes:
|
||||
users: /myusers/**
|
||||
----
|
||||
@@ -810,8 +830,8 @@ and the serviceId independently:
|
||||
|
||||
This means that http calls to "/myusers" get forwarded to the
|
||||
"users_service" service. The route has to have a "path" which can be
|
||||
specified as an ant-style pattern, so "/myusers/*" only matches one
|
||||
level, but "/myusers/**" matches hierarchically.
|
||||
specified as an ant-style pattern, so "/myusers/\*" only matches one
|
||||
level, but "/myusers/**" matches hierarchically.
|
||||
|
||||
The location of the backend can be specified as either a "serviceId"
|
||||
(for a Eureka service) or a "url" (for a physical location), e.g.
|
||||
@@ -823,7 +843,30 @@ The location of the backend can be specified as either a "serviceId"
|
||||
routes:
|
||||
users:
|
||||
path: /myusers/**
|
||||
url: http://example.com/users_service
|
||||
url: https://example.com/users_service
|
||||
----
|
||||
|
||||
These simple url-routes doesn't get executed as HystrixCommand nor can you loadbalance multiple url with Ribbon.
|
||||
To achieve this specify a service-route and configure a Ribbon client for the
|
||||
serviceId (this currently requires disabling Eureka support in Ribbon:
|
||||
see <<spring-cloud-ribbon-without-eureka,above for more information>>), e.g.
|
||||
|
||||
.application.yml
|
||||
[source,yaml]
|
||||
----
|
||||
zuul:
|
||||
routes:
|
||||
users:
|
||||
path: /myusers/**
|
||||
serviceId: users
|
||||
|
||||
ribbon:
|
||||
eureka:
|
||||
enabled: false
|
||||
|
||||
users:
|
||||
ribbon:
|
||||
listOfServers: example.com,google.com
|
||||
----
|
||||
|
||||
To add a prefix to all mappings, set `zuul.prefix` to a value, such as
|
||||
@@ -860,13 +903,45 @@ An application with the `@EnableZuulProxy` could act as a standalone
|
||||
server if you set a default route ("/"), for example `zuul.route.home:
|
||||
/` would route all traffic (i.e. "/**") to the "home" service.
|
||||
|
||||
=== Uploading Files through Zuul
|
||||
|
||||
If you `@EnableZuulProxy` you can use the proxy paths to
|
||||
upload files and it should just work as long as the files
|
||||
are small. For large files there is an alternative path
|
||||
which bypasses the Spring `DispatcherServlet` (to
|
||||
avoid multipart processing) in "/zuul/*". I.e. if
|
||||
`zuul.routes.customers=/customers/**` then you can
|
||||
POST large files to "/zuul/customers/*". The servlet
|
||||
path is externalized via `zuul.servletPath`. Extremely
|
||||
large files will also require elevated timeout settings
|
||||
if the proxy route takes you through a Ribbon load
|
||||
balancer, e.g.
|
||||
|
||||
.application.yml
|
||||
[source,yaml]
|
||||
----
|
||||
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000
|
||||
ribbon:
|
||||
ConnectTimeout: 3000
|
||||
ReadTimeout: 60000
|
||||
----
|
||||
|
||||
Note that for streaming to work with large files, you need to use chunked encoding in the request (which some browsers
|
||||
do not do by default). E.g. on the command line:
|
||||
|
||||
----
|
||||
$ curl -v -H "Transfer-Encoding: chunked" \
|
||||
-F "file=@mylarge.iso" localhost:9999/zuul/simple/file
|
||||
----
|
||||
|
||||
=== Plain Embedded Zuul
|
||||
|
||||
You can also run a Zuul server without the proxying, or switch on parts of the proxying platform selectively, if you
|
||||
use `@EnableZuulServer` (instead of `@EnableZuulProxy`). Any beans that you add to the application of type `ZuulFilter`
|
||||
will be installed automatically.
|
||||
will be installed automatically, as they are with `@EnableZuulProxy`, but without any of the proxy filters being added
|
||||
automatically.
|
||||
|
||||
In this case the routes into the Zuul server are
|
||||
In this case the routes into the Zuul server are
|
||||
still specified by configuring "zuul.routes.*", but there is no service discovery and no proxying, so the
|
||||
"serviceId" and "url" settings are ignored. For example:
|
||||
|
||||
@@ -880,6 +955,16 @@ still specified by configuring "zuul.routes.*", but there is no service discover
|
||||
|
||||
maps all paths in "/api/**" to the Zuul filter chain.
|
||||
|
||||
=== Disable Zuul Filters
|
||||
|
||||
Zuul for Spring Cloud comes with a number of `ZuulFilter` beans enabled by default
|
||||
in both proxy and server mode. See https://github.com/spring-cloud/spring-cloud-netflix/tree/master/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters[the zuul filters package] for the
|
||||
possible filters that are enabled. If you want to disable one, simply set
|
||||
`zuul.<SimpleClassName>.<filterType>.disable=true`. By convention, the package after
|
||||
`filters` is the Zuul filter type. For example to disable
|
||||
`org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter` set
|
||||
`zuul.SendResponseFilter.post.disable=true`.
|
||||
|
||||
=== Polyglot support with Sidecar
|
||||
|
||||
Do you have non-jvm languages you want to take advantage of Eureka, Ribbon and
|
||||
@@ -963,7 +1048,7 @@ and the Sidecar is on port 5678, then it can be accessed at
|
||||
http://localhost:5678/configserver
|
||||
|
||||
Non-jvm app can take advantage of the Config Server's ability to return YAML
|
||||
documents. For example, a call to http://sidecar.local.spring.io:5678/configserver/default-master.yml
|
||||
documents. For example, a call to https://sidecar.local.spring.io:5678/configserver/default-master.yml
|
||||
might result in a YAML document like the following
|
||||
|
||||
[source,yaml]
|
||||
|
||||
@@ -17,7 +17,7 @@ end
|
||||
file = ARGV[0] if ARGV.length>0
|
||||
|
||||
srcDir = File.dirname(file)
|
||||
out = "// Do not edit this file (e.g. go instead to src/main/asciidoc)\n\n"
|
||||
out = "// Do not edit this file (e.g. go instead to docs/src/main/asciidoc)\n\n"
|
||||
doc = Asciidoctor.load_file file, safe: :safe, parse: false, attributes: 'allow-uri-read'
|
||||
out << doc.reader.read
|
||||
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Maven2 Start Up Batch script
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
# JAVA_HOME - location of a JDK home dir
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# M2_HOME - location of maven2's installed home dir
|
||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
# e.g. to debug Maven itself, use
|
||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||
|
||||
if [ -f /etc/mavenrc ] ; then
|
||||
. /etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.mavenrc" ] ; then
|
||||
. "$HOME/.mavenrc"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# OS specific support. $var _must_ be set to either true or false.
|
||||
cygwin=false;
|
||||
darwin=false;
|
||||
mingw=false
|
||||
case "`uname`" in
|
||||
CYGWIN*) cygwin=true ;;
|
||||
MINGW*) mingw=true;;
|
||||
Darwin*) darwin=true
|
||||
#
|
||||
# Look for the Apple JDKs first to preserve the existing behaviour, and then look
|
||||
# for the new JDKs provided by Oracle.
|
||||
#
|
||||
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
|
||||
#
|
||||
# Apple JDKs
|
||||
#
|
||||
export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
|
||||
#
|
||||
# Apple JDKs
|
||||
#
|
||||
export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
|
||||
#
|
||||
# Oracle JDKs
|
||||
#
|
||||
export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
|
||||
#
|
||||
# Apple JDKs
|
||||
#
|
||||
export JAVA_HOME=`/usr/libexec/java_home`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
if [ -r /etc/gentoo-release ] ; then
|
||||
JAVA_HOME=`java-config --jre-home`
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$M2_HOME" ] ; then
|
||||
## resolve links - $0 may be a link to maven's home
|
||||
PRG="$0"
|
||||
|
||||
# need this for relative symlinks
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG="`dirname "$PRG"`/$link"
|
||||
fi
|
||||
done
|
||||
|
||||
saveddir=`pwd`
|
||||
|
||||
M2_HOME=`dirname "$PRG"`/..
|
||||
|
||||
# make it fully qualified
|
||||
M2_HOME=`cd "$M2_HOME" && pwd`
|
||||
|
||||
cd "$saveddir"
|
||||
# echo Using m2 at $M2_HOME
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --unix "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
|
||||
fi
|
||||
|
||||
# For Migwn, ensure paths are in UNIX format before anything is touched
|
||||
if $mingw ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME="`(cd "$M2_HOME"; pwd)`"
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
|
||||
# TODO classpath?
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
javaExecutable="`which javac`"
|
||||
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
|
||||
# readlink(1) is not available as standard on Solaris 10.
|
||||
readLink=`which readlink`
|
||||
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
|
||||
if $darwin ; then
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
|
||||
else
|
||||
javaExecutable="`readlink -f \"$javaExecutable\"`"
|
||||
fi
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
|
||||
JAVA_HOME="$javaHome"
|
||||
export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$JAVACMD" ] ; then
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
else
|
||||
JAVACMD="`which java`"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||
echo " We cannot execute $JAVACMD" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
echo "Warning: JAVA_HOME environment variable is not set."
|
||||
fi
|
||||
|
||||
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --path --windows "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
|
||||
fi
|
||||
|
||||
# traverses directory structure from process work directory to filesystem root
|
||||
# first directory with .mvn subdirectory is considered project base directory
|
||||
find_maven_basedir() {
|
||||
local basedir=$(pwd)
|
||||
local wdir=$(pwd)
|
||||
while [ "$wdir" != '/' ] ; do
|
||||
if [ -d "$wdir"/.mvn ] ; then
|
||||
basedir=$wdir
|
||||
break
|
||||
fi
|
||||
wdir=$(cd "$wdir/.."; pwd)
|
||||
done
|
||||
echo "${basedir}"
|
||||
}
|
||||
|
||||
# concatenates all lines of a file
|
||||
concat_lines() {
|
||||
if [ -f "$1" ]; then
|
||||
echo "$(tr -s '\n' ' ' < "$1")"
|
||||
fi
|
||||
}
|
||||
|
||||
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
|
||||
# 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
|
||||
|
||||
echo "Running version check"
|
||||
VERSION=$( sed '\!<parent!,\!</parent!d' pom.xml | grep '<version' | head -1 | sed -e 's/.*<version>//' -e 's!</version>.*$!!' )
|
||||
echo "The found version is [${VERSION}]"
|
||||
|
||||
if echo $VERSION | egrep -q 'M|RC'; then
|
||||
echo Activating \"milestone\" profile for version=\"$VERSION\"
|
||||
echo $MAVEN_ARGS | grep -q milestone || MAVEN_ARGS="$MAVEN_ARGS -Pmilestone"
|
||||
else
|
||||
echo Deactivating \"milestone\" profile for version=\"$VERSION\"
|
||||
echo $MAVEN_ARGS | grep -q milestone && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pmilestone//')
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" \
|
||||
$MAVEN_OPTS \
|
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||
${WRAPPER_LAUNCHER} ${MAVEN_ARGS} "$@"
|
||||
@@ -0,0 +1,145 @@
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM https://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Maven2 Start Up Batch script
|
||||
@REM
|
||||
@REM Required ENV vars:
|
||||
@REM JAVA_HOME - location of a JDK home dir
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM M2_HOME - location of maven2's installed home dir
|
||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
|
||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
@REM e.g. to debug Maven itself, use
|
||||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||
@echo off
|
||||
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
|
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||
|
||||
@REM set %HOME% to equivalent of $HOME
|
||||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||
|
||||
@REM Execute a user defined script before this one
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
|
||||
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
|
||||
:skipRcPre
|
||||
|
||||
@setlocal
|
||||
|
||||
set ERROR_CODE=0
|
||||
|
||||
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||
@setlocal
|
||||
|
||||
@REM ==== START VALIDATION ====
|
||||
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME not found in your environment. >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
:OkJHome
|
||||
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
@REM ==== END VALIDATION ====
|
||||
|
||||
:init
|
||||
|
||||
set MAVEN_CMD_LINE_ARGS=%*
|
||||
|
||||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||
@REM Fallback to current working directory if not found.
|
||||
|
||||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||
|
||||
set EXEC_DIR=%CD%
|
||||
set WDIR=%EXEC_DIR%
|
||||
:findBaseDir
|
||||
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||
cd ..
|
||||
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||
set WDIR=%CD%
|
||||
goto findBaseDir
|
||||
|
||||
:baseDirFound
|
||||
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||
cd "%EXEC_DIR%"
|
||||
goto endDetectBaseDir
|
||||
|
||||
:baseDirNotFound
|
||||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||
cd "%EXEC_DIR%"
|
||||
|
||||
:endDetectBaseDir
|
||||
|
||||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||
|
||||
@setlocal EnableExtensions EnableDelayedExpansion
|
||||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||
|
||||
:endReadAdditionalConfig
|
||||
|
||||
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||
|
||||
set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar""
|
||||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
|
||||
if ERRORLEVEL 1 goto error
|
||||
goto end
|
||||
|
||||
:error
|
||||
set ERROR_CODE=1
|
||||
|
||||
:end
|
||||
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
|
||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
|
||||
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
|
||||
:skipRcPost
|
||||
|
||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||
if "%MAVEN_BATCH_PAUSE%" == "on" pause
|
||||
|
||||
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
|
||||
|
||||
exit /B %ERROR_CODE%
|
||||
@@ -1,15 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-build</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.5.BUILD-SNAPSHOT</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.8.BUILD-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Spring Cloud Netflix</name>
|
||||
<description>Spring Cloud Netflix</description>
|
||||
@@ -20,17 +20,19 @@
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
<properties>
|
||||
<bintray.package>netflix</bintray.package>
|
||||
<main.basedir>${basedir}</main.basedir>
|
||||
<archaius.version>0.6.5</archaius.version>
|
||||
<eureka.version>1.1.147</eureka.version>
|
||||
<feign.version>7.1.0</feign.version>
|
||||
<hystrix.version>1.4.0-RC6</hystrix.version>
|
||||
<ribbon.version>2.0-RC13</ribbon.version>
|
||||
<feign.version>8.1.1</feign.version>
|
||||
<hystrix.version>1.4.10</hystrix.version>
|
||||
<ribbon.version>2.0.0</ribbon.version>
|
||||
<zuul.version>1.0.28</zuul.version>
|
||||
<netflix.rxjava.version>0.20.7</netflix.rxjava.version>
|
||||
<rxjava.version>1.0.11</rxjava.version>
|
||||
<java.version>1.7</java.version>
|
||||
<spring-cloud-netflix.version>1.0.8.BUILD-SNAPSHOT</spring-cloud-netflix.version>
|
||||
<spring-integration-dsl.version>1.0.0.RELEASE</spring-integration-dsl.version>
|
||||
<cloud-connectors.version>1.1.1.BUILD-SNAPSHOT</cloud-connectors.version>
|
||||
<cloud-connectors.version>1.1.1.RELEASE</cloud-connectors.version>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
@@ -64,53 +66,10 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-config-client</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-config-server</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-core</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-eureka-server</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-hystrix-dashboard</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-hystrix-amqp</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-turbine</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-turbine-amqp</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-zuul-server</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<artifactId>spring-cloud-netflix-dependencies</artifactId>
|
||||
<version>${spring-cloud-netflix.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.archaius</groupId>
|
||||
@@ -185,12 +144,6 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- TODO: remove with feign 8.0.0 -->
|
||||
<dependency>
|
||||
<groupId>com.squareup.dagger</groupId>
|
||||
<artifactId>dagger</artifactId>
|
||||
<version>1.2.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.feign</groupId>
|
||||
<artifactId>feign-slf4j</artifactId>
|
||||
@@ -238,9 +191,14 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.rxjava</groupId>
|
||||
<artifactId>rxjava-core</artifactId>
|
||||
<version>${netflix.rxjava.version}</version>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-loadbalancer</artifactId>
|
||||
<version>${ribbon.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.reactivex</groupId>
|
||||
<artifactId>rxjava</artifactId>
|
||||
<version>${rxjava.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.zuul</groupId>
|
||||
@@ -287,6 +245,11 @@
|
||||
<artifactId>jersey-server</artifactId>
|
||||
<version>1.13</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.jersey.contribs</groupId>
|
||||
<artifactId>jersey-apache-client4</artifactId>
|
||||
<version>1.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>d3js</artifactId>
|
||||
@@ -310,6 +273,7 @@
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<modules>
|
||||
<module>spring-cloud-netflix-dependencies</module>
|
||||
<module>spring-cloud-netflix-core</module>
|
||||
<module>spring-cloud-netflix-hystrix-dashboard</module>
|
||||
<module>spring-cloud-netflix-hystrix-amqp</module>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.8.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-core</artifactId>
|
||||
@@ -16,6 +16,11 @@
|
||||
<main.basedir>${basedir}/..</main.basedir>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot</artifactId>
|
||||
@@ -29,6 +34,11 @@
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-context</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-config-client</artifactId>
|
||||
@@ -54,6 +64,11 @@
|
||||
<artifactId>eureka-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-loadbalancer</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- Eureka core deps that are now optional -->
|
||||
<dependency>
|
||||
<groupId>com.thoughtworks.xstream</groupId>
|
||||
@@ -81,11 +96,6 @@
|
||||
<artifactId>feign-ribbon</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.dagger</groupId>
|
||||
<artifactId>dagger</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-core</artifactId>
|
||||
@@ -130,11 +140,20 @@
|
||||
<artifactId>groovy-all</artifactId>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<groupId>org.mockito</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.rxjava</groupId>
|
||||
<artifactId>rxjava-core</artifactId>
|
||||
<groupId>io.reactivex</groupId>
|
||||
<artifactId>rxjava</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.jersey.contribs</groupId>
|
||||
<artifactId>jersey-apache-client4</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -149,5 +168,15 @@
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
+47
-24
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -16,12 +16,21 @@
|
||||
|
||||
package org.springframework.cloud.netflix.archaius;
|
||||
|
||||
import static com.netflix.config.ConfigurationManager.APPLICATION_PROPERTIES;
|
||||
import static com.netflix.config.ConfigurationManager.DISABLE_DEFAULT_ENV_CONFIG;
|
||||
import static com.netflix.config.ConfigurationManager.DISABLE_DEFAULT_SYS_CONFIG;
|
||||
import static com.netflix.config.ConfigurationManager.ENV_CONFIG_NAME;
|
||||
import static com.netflix.config.ConfigurationManager.SYS_CONFIG_NAME;
|
||||
import static com.netflix.config.ConfigurationManager.URL_CONFIG_NAME;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
import org.apache.commons.configuration.AbstractConfiguration;
|
||||
import org.apache.commons.configuration.ConfigurationBuilder;
|
||||
import org.apache.commons.configuration.EnvironmentConfiguration;
|
||||
@@ -40,19 +49,13 @@ import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import com.netflix.config.AggregatedConfiguration;
|
||||
import com.netflix.config.ConcurrentCompositeConfiguration;
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.config.DeploymentContext;
|
||||
import com.netflix.config.DynamicPropertyFactory;
|
||||
import com.netflix.config.DynamicURLConfiguration;
|
||||
|
||||
import static com.netflix.config.ConfigurationBasedDeploymentContext.DEPLOYMENT_APPLICATION_ID_PROPERTY;
|
||||
import static com.netflix.config.ConfigurationManager.APPLICATION_PROPERTIES;
|
||||
import static com.netflix.config.ConfigurationManager.DISABLE_DEFAULT_ENV_CONFIG;
|
||||
import static com.netflix.config.ConfigurationManager.DISABLE_DEFAULT_SYS_CONFIG;
|
||||
import static com.netflix.config.ConfigurationManager.ENV_CONFIG_NAME;
|
||||
import static com.netflix.config.ConfigurationManager.SYS_CONFIG_NAME;
|
||||
import static com.netflix.config.ConfigurationManager.URL_CONFIG_NAME;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@@ -66,6 +69,9 @@ public class ArchaiusAutoConfiguration {
|
||||
@Autowired
|
||||
private ConfigurableEnvironment env;
|
||||
|
||||
@Autowired
|
||||
private List<AbstractConfiguration> externalConfigurations;
|
||||
|
||||
@PreDestroy
|
||||
public void close() {
|
||||
setStatic(ConfigurationManager.class, "instance", null);
|
||||
@@ -85,7 +91,7 @@ public class ArchaiusAutoConfiguration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(Endpoint.class)
|
||||
protected static class ArchaiusEndpointConfuguration {
|
||||
protected static class ArchaiusEndpointConfiguration {
|
||||
@Bean
|
||||
protected ArchaiusEndpoint archaiusEndpoint() {
|
||||
return new ArchaiusEndpoint();
|
||||
@@ -95,7 +101,8 @@ public class ArchaiusAutoConfiguration {
|
||||
@Configuration
|
||||
@ConditionalOnProperty(value = "archaius.propagate.environmentChangedEvent", matchIfMissing = true)
|
||||
@ConditionalOnClass(EnvironmentChangeEvent.class)
|
||||
protected static class PropagateEventsConfiguration implements ApplicationListener<EnvironmentChangeEvent> {
|
||||
protected static class PropagateEventsConfiguration implements
|
||||
ApplicationListener<EnvironmentChangeEvent> {
|
||||
@Autowired
|
||||
private Environment env;
|
||||
|
||||
@@ -109,14 +116,13 @@ public class ArchaiusAutoConfiguration {
|
||||
int type = AbstractConfiguration.EVENT_SET_PROPERTY;
|
||||
String value = env.getProperty(key);
|
||||
boolean beforeUpdate = false;
|
||||
listener.configurationChanged(new ConfigurationEvent(source,
|
||||
type, key, value, beforeUpdate));
|
||||
listener.configurationChanged(new ConfigurationEvent(source, type,
|
||||
key, value, beforeUpdate));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void configureArchaius(ConfigurableEnvironmentConfiguration envConfig) {
|
||||
if (initialized.compareAndSet(false, true)) {
|
||||
String appName = this.env.getProperty("spring.application.name");
|
||||
@@ -124,20 +130,17 @@ public class ArchaiusAutoConfiguration {
|
||||
appName = "application";
|
||||
log.warn("No spring.application.name found, defaulting to 'application'");
|
||||
}
|
||||
// this is deprecated, but currently it seams the only way to set it initially
|
||||
System.setProperty(DEPLOYMENT_APPLICATION_ID_PROPERTY, appName);
|
||||
|
||||
// TODO: support for other DeploymentContexts
|
||||
System.setProperty(DeploymentContext.ContextKey.appId.getKey(), appName);
|
||||
|
||||
ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration();
|
||||
|
||||
// support to add other Configurations (Jdbc, DynamoDb, Zookeeper, jclouds,
|
||||
// etc...)
|
||||
/*
|
||||
* if (factories != null && !factories.isEmpty()) { for
|
||||
* (PropertiesSourceFactory factory: factories) {
|
||||
* config.addConfiguration(factory.getConfiguration(), factory.getName()); } }
|
||||
*/
|
||||
if (externalConfigurations != null) {
|
||||
for (AbstractConfiguration externalConfig : externalConfigurations) {
|
||||
config.addConfiguration(externalConfig);
|
||||
}
|
||||
}
|
||||
config.addConfiguration(envConfig,
|
||||
ConfigurableEnvironmentConfiguration.class.getSimpleName());
|
||||
|
||||
@@ -165,7 +168,7 @@ public class ArchaiusAutoConfiguration {
|
||||
config.setContainerConfigurationIndex(config
|
||||
.getIndexOfConfiguration(appOverrideConfig));
|
||||
|
||||
ConfigurationManager.install(config);
|
||||
addArchaiusConfiguration(config);
|
||||
}
|
||||
else {
|
||||
// TODO: reinstall ConfigurationManager
|
||||
@@ -173,6 +176,26 @@ public class ArchaiusAutoConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
private void addArchaiusConfiguration(ConcurrentCompositeConfiguration config) {
|
||||
if (ConfigurationManager.isConfigurationInstalled()) {
|
||||
AbstractConfiguration installedConfiguration = ConfigurationManager
|
||||
.getConfigInstance();
|
||||
if (installedConfiguration instanceof ConcurrentCompositeConfiguration) {
|
||||
ConcurrentCompositeConfiguration configInstance = (ConcurrentCompositeConfiguration) installedConfiguration;
|
||||
configInstance.addConfiguration(config);
|
||||
}
|
||||
else {
|
||||
installedConfiguration.append(config);
|
||||
if (!(installedConfiguration instanceof AggregatedConfiguration)) {
|
||||
log.warn("Appending a configuration to an existing non-aggregated installed configuration will have no effect");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
ConfigurationManager.install(config);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setStatic(Class<?> type, String name, Object value) {
|
||||
// Hack a private static field
|
||||
Field field = ReflectionUtils.findField(type, name);
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.config;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.netflix.appinfo.ApplicationInfoManager;
|
||||
import com.netflix.appinfo.EurekaInstanceConfig;
|
||||
import com.netflix.discovery.DiscoveryManager;
|
||||
import com.netflix.discovery.EurekaClientConfig;
|
||||
|
||||
/**
|
||||
* Bootstrap configuration for a config client that wants to lookup the config server via
|
||||
* discovery.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@ConditionalOnBean({ EurekaDiscoveryClientConfiguration.class })
|
||||
@ConditionalOnProperty(value = "spring.cloud.config.discovery.enabled", matchIfMissing = false)
|
||||
@Configuration
|
||||
public class DiscoveryClientConfigServiceAutoConfiguration
|
||||
implements ApplicationListener<RefreshScopeRefreshedEvent> {
|
||||
|
||||
@Autowired
|
||||
private EurekaClientConfig clientConfig;
|
||||
|
||||
@Autowired
|
||||
private EurekaInstanceConfig instanceConfig;
|
||||
|
||||
@Autowired
|
||||
private EurekaDiscoveryClientConfiguration lifecycle;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
this.lifecycle.stop();
|
||||
if (DiscoveryManager.getInstance().getDiscoveryClient() != null) {
|
||||
DiscoveryManager.getInstance().getDiscoveryClient().shutdown();
|
||||
}
|
||||
ApplicationInfoManager.getInstance().initComponent(this.instanceConfig);
|
||||
DiscoveryManager.getInstance().initComponent(this.instanceConfig,
|
||||
this.clientConfig);
|
||||
this.lifecycle.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(RefreshScopeRefreshedEvent arg0) {
|
||||
init();
|
||||
}
|
||||
|
||||
}
|
||||
+32
-18
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -16,17 +16,19 @@
|
||||
|
||||
package org.springframework.cloud.netflix.config;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
|
||||
import org.springframework.cloud.client.discovery.event.HeartbeatMonitor;
|
||||
import org.springframework.cloud.config.client.ConfigClientProperties;
|
||||
import org.springframework.cloud.config.client.ConfigServicePropertySourceLocator;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
@@ -35,36 +37,41 @@ import org.springframework.context.event.SmartApplicationListener;
|
||||
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.discovery.DiscoveryClient;
|
||||
import com.netflix.discovery.DiscoveryManager;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
/**
|
||||
* Bootstrap configuration for a config client that wants to lookup the config server via
|
||||
* discovery.
|
||||
* Bootstrap configuration for a config client that wants to lookup the config
|
||||
* server via discovery.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@ConditionalOnClass({ DiscoveryClient.class, ConfigServicePropertySourceLocator.class })
|
||||
@ConditionalOnExpression("${spring.cloud.config.discovery.enabled:false}")
|
||||
@ConditionalOnProperty(value = "spring.cloud.config.discovery.enabled", matchIfMissing = false)
|
||||
@Configuration
|
||||
@EnableDiscoveryClient
|
||||
@Import(EurekaClientAutoConfiguration.class)
|
||||
@CommonsLog
|
||||
public class DiscoveryClientConfigServiceBootstrapConfiguration implements
|
||||
SmartApplicationListener {
|
||||
public class DiscoveryClientConfigServiceBootstrapConfiguration implements SmartApplicationListener {
|
||||
|
||||
private HeartbeatMonitor monitor = new HeartbeatMonitor();
|
||||
|
||||
@Autowired
|
||||
private DiscoveryClient client;
|
||||
private ConfigClientProperties config;
|
||||
|
||||
@Autowired
|
||||
private ConfigClientProperties config;
|
||||
private org.springframework.cloud.client.discovery.DiscoveryClient client;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
if (event instanceof ContextRefreshedEvent) {
|
||||
if (event instanceof ContextRefreshedEvent
|
||||
&& ((ContextRefreshedEvent) event).getApplicationContext() == context) {
|
||||
refresh();
|
||||
}
|
||||
else if (event instanceof HeartbeatEvent) {
|
||||
} else if (event instanceof HeartbeatEvent) {
|
||||
if (this.monitor.update(((HeartbeatEvent) event).getValue())) {
|
||||
refresh();
|
||||
}
|
||||
@@ -90,9 +97,9 @@ public class DiscoveryClientConfigServiceBootstrapConfiguration implements
|
||||
private void refresh() {
|
||||
try {
|
||||
log.info("Locating configserver via discovery");
|
||||
InstanceInfo server = this.client.getNextServerFromEureka(this.config
|
||||
.getDiscovery().getServiceId(), false);
|
||||
String url = server.getHomePageUrl();
|
||||
InstanceInfo server = DiscoveryManager.getInstance().getDiscoveryClient()
|
||||
.getNextServerFromEureka(this.config.getDiscovery().getServiceId(), false);
|
||||
String url = getHomePage(server);
|
||||
if (server.getMetadata().containsKey("password")) {
|
||||
String user = server.getMetadata().get("user");
|
||||
user = user == null ? "user" : user;
|
||||
@@ -108,10 +115,17 @@ public class DiscoveryClientConfigServiceBootstrapConfiguration implements
|
||||
url = url + path;
|
||||
}
|
||||
this.config.setUri(url);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
} catch (Exception ex) {
|
||||
log.warn("Could not locate configserver via discovery", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private String getHomePage(InstanceInfo server) {
|
||||
List<ServiceInstance> instances = client.getInstances(this.config.getDiscovery().getServiceId());
|
||||
if (instances==null || instances.isEmpty()) {
|
||||
return server.getHomePageUrl();
|
||||
}
|
||||
return instances.get(0).getUri().toString() + "/";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+14
-5
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -177,8 +177,7 @@ public class DataCenterAwareMarshallingStrategy implements MarshallingStrategy {
|
||||
String instanceId = info.getMetadata().get("instanceId");
|
||||
DataCenterInfo dataCenter = info.getDataCenterInfo();
|
||||
if (instanceId != null && Name.Amazon != dataCenter.getName()) {
|
||||
String old = info.getId();
|
||||
String id = old.endsWith(instanceId) ? old : old + ":" + instanceId;
|
||||
String id = calculateId(info, instanceId);
|
||||
info = new InstanceInfo.Builder(info).setDataCenterInfo(
|
||||
new InstanceIdDataCenterInfo(id)).build();
|
||||
source = info;
|
||||
@@ -186,6 +185,17 @@ public class DataCenterAwareMarshallingStrategy implements MarshallingStrategy {
|
||||
super.marshal(source, writer, context);
|
||||
}
|
||||
|
||||
String calculateId(InstanceInfo info, String instanceId) {
|
||||
String old = info.getId();
|
||||
if (old.endsWith(instanceId)) {
|
||||
return old;
|
||||
}
|
||||
if (instanceId.startsWith(old)) {
|
||||
return instanceId;
|
||||
}
|
||||
return old + ":" + instanceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object unmarshal(HierarchicalStreamReader reader,
|
||||
UnmarshallingContext context) {
|
||||
@@ -194,8 +204,7 @@ public class DataCenterAwareMarshallingStrategy implements MarshallingStrategy {
|
||||
String instanceId = info.getMetadata().get("instanceId");
|
||||
DataCenterInfo dataCenter = info.getDataCenterInfo();
|
||||
if (instanceId != null && Name.Amazon != dataCenter.getName()) {
|
||||
String old = info.getId();
|
||||
String id = old.endsWith(instanceId) ? old : old + ":" + instanceId;
|
||||
String id = calculateId(info, instanceId);
|
||||
info = new InstanceInfo.Builder(info).setDataCenterInfo(
|
||||
new InstanceIdDataCenterInfo(id)).build();
|
||||
obj = info;
|
||||
|
||||
+5
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -18,6 +18,7 @@ package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import com.netflix.appinfo.ApplicationInfoManager;
|
||||
import com.netflix.appinfo.EurekaInstanceConfig;
|
||||
import com.netflix.discovery.DiscoveryManager;
|
||||
import com.netflix.discovery.EurekaClientConfig;
|
||||
@@ -38,6 +39,9 @@ public class DiscoveryManagerInitializer {
|
||||
DiscoveryManager.getInstance().initComponent(this.instanceConfig,
|
||||
this.clientConfig);
|
||||
}
|
||||
if (ApplicationInfoManager.getInstance().getInfo() == null) {
|
||||
ApplicationInfoManager.getInstance().initComponent(this.instanceConfig);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+7
-5
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -27,8 +27,10 @@ import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
||||
import org.springframework.boot.builder.ParentContextApplicationContextInitializer;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.client.CommonsClientAutoConfiguration;
|
||||
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
|
||||
import org.springframework.cloud.client.discovery.event.ParentHeartbeatEvent;
|
||||
import org.springframework.cloud.client.discovery.noop.NoopDiscoveryClientAutoConfiguration;
|
||||
@@ -50,7 +52,7 @@ import com.netflix.discovery.converters.XmlXStream;
|
||||
@EnableConfigurationProperties
|
||||
@ConditionalOnClass(EurekaClientConfig.class)
|
||||
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
|
||||
@AutoConfigureBefore(NoopDiscoveryClientAutoConfiguration.class)
|
||||
@AutoConfigureBefore({NoopDiscoveryClientAutoConfiguration.class, CommonsClientAutoConfiguration.class})
|
||||
public class EurekaClientAutoConfiguration implements ApplicationListener<ParentContextApplicationContextInitializer.ParentContextAvailableEvent> {
|
||||
|
||||
@Autowired
|
||||
@@ -60,7 +62,7 @@ public class EurekaClientAutoConfiguration implements ApplicationListener<Parent
|
||||
|
||||
@Value("${server.port:${SERVER_PORT:${PORT:8080}}}")
|
||||
int nonSecurePort;
|
||||
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
XmlXStream.getInstance().setMarshallingStrategy(
|
||||
@@ -70,13 +72,13 @@ public class EurekaClientAutoConfiguration implements ApplicationListener<Parent
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(EurekaClientConfig.class)
|
||||
@ConditionalOnMissingBean(value=EurekaClientConfig.class, search = SearchStrategy.CURRENT)
|
||||
public EurekaClientConfigBean eurekaClientConfigBean() {
|
||||
return new EurekaClientConfigBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(EurekaInstanceConfig.class)
|
||||
@ConditionalOnMissingBean(value=EurekaInstanceConfig.class, search = SearchStrategy.CURRENT)
|
||||
public EurekaInstanceConfigBean eurekaInstanceConfigBean() {
|
||||
EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean();
|
||||
instance.setNonSecurePort(this.nonSecurePort);
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+3
-3
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -110,8 +110,8 @@ public class EurekaDiscoveryClient implements DiscoveryClient {
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
// assume if unsecure is enabled, that is the default
|
||||
if (this.instance.isPortEnabled(UNSECURE) || !this.instance.isPortEnabled(SECURE)) {
|
||||
// assume if secure is enabled, that is the default
|
||||
if (!this.instance.isPortEnabled(SECURE)) {
|
||||
return this.instance.getPort();
|
||||
}
|
||||
return this.instance.getSecurePort();
|
||||
|
||||
+33
-14
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -17,14 +17,18 @@
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.actuate.metrics.reader.CompositeMetricReader;
|
||||
import org.springframework.boot.actuate.metrics.reader.MetricReader;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
@@ -75,6 +79,9 @@ public class EurekaDiscoveryClientConfiguration implements SmartLifecycle, Order
|
||||
@Autowired(required = false)
|
||||
private HealthCheckHandler healthCheckHandler;
|
||||
|
||||
@Autowired
|
||||
private DiscoveryManagerInitializer discoveryManagerInitializer;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
@@ -94,8 +101,8 @@ public class EurekaDiscoveryClientConfiguration implements SmartLifecycle, Order
|
||||
if (jerseyClientField != null) {
|
||||
try {
|
||||
jerseyClientField.setAccessible(true);
|
||||
if (DiscoveryManager.getInstance() != null &&
|
||||
DiscoveryManager.getInstance().getDiscoveryClient() != null) {
|
||||
if (DiscoveryManager.getInstance() != null
|
||||
&& DiscoveryManager.getInstance().getDiscoveryClient() != null) {
|
||||
Object obj = jerseyClientField.get(DiscoveryManager.getInstance()
|
||||
.getDiscoveryClient());
|
||||
if (obj != null) {
|
||||
@@ -119,7 +126,7 @@ public class EurekaDiscoveryClientConfiguration implements SmartLifecycle, Order
|
||||
// only initialize if nonSecurePort is greater than 0 and it isn't already running
|
||||
// because of containerPortInitializer below
|
||||
if (!this.running.get() && this.instanceConfig.getNonSecurePort() > 0) {
|
||||
discoveryManagerIntitializer().init();
|
||||
this.discoveryManagerInitializer.init();
|
||||
|
||||
log.info("Registering application " + this.instanceConfig.getAppname()
|
||||
+ " with eureka with status "
|
||||
@@ -130,7 +137,7 @@ public class EurekaDiscoveryClientConfiguration implements SmartLifecycle, Order
|
||||
|
||||
if (this.healthCheckHandler != null) {
|
||||
DiscoveryManager.getInstance().getDiscoveryClient()
|
||||
.registerHealthCheck(this.healthCheckHandler);
|
||||
.registerHealthCheck(this.healthCheckHandler);
|
||||
}
|
||||
this.context.publishEvent(new InstanceRegisteredEvent<>(this,
|
||||
this.instanceConfig));
|
||||
@@ -141,9 +148,11 @@ public class EurekaDiscoveryClientConfiguration implements SmartLifecycle, Order
|
||||
@Override
|
||||
public void stop() {
|
||||
log.info("Unregistering application " + this.instanceConfig.getAppname()
|
||||
+ " with eureka with status OUT_OF_SERVICE");
|
||||
ApplicationInfoManager.getInstance().setInstanceStatus(
|
||||
InstanceStatus.OUT_OF_SERVICE);
|
||||
+ " with eureka with status DOWN");
|
||||
if (ApplicationInfoManager.getInstance().getInfo() != null) {
|
||||
ApplicationInfoManager.getInstance().setInstanceStatus(
|
||||
InstanceStatus.DOWN);
|
||||
}
|
||||
this.running.set(false);
|
||||
}
|
||||
|
||||
@@ -164,6 +173,7 @@ public class EurekaDiscoveryClientConfiguration implements SmartLifecycle, Order
|
||||
|
||||
@Override
|
||||
public void stop(Runnable callback) {
|
||||
stop();
|
||||
callback.run();
|
||||
}
|
||||
|
||||
@@ -172,10 +182,15 @@ public class EurekaDiscoveryClientConfiguration implements SmartLifecycle, Order
|
||||
return this.order;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(DiscoveryManagerInitializer.class)
|
||||
public DiscoveryManagerInitializer discoveryManagerIntitializer() {
|
||||
return new DiscoveryManagerInitializer();
|
||||
@Configuration
|
||||
protected static class DiscoveryManagerInitializerConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(DiscoveryManagerInitializer.class)
|
||||
public DiscoveryManagerInitializer discoveryManagerInitializer() {
|
||||
return new DiscoveryManagerInitializer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -209,11 +224,15 @@ public class EurekaDiscoveryClientConfiguration implements SmartLifecycle, Order
|
||||
@ConditionalOnClass(Endpoint.class)
|
||||
@ConditionalOnBean(MetricReader.class)
|
||||
protected static class EurekaHealthIndicatorConfiguration {
|
||||
|
||||
@Autowired
|
||||
private List<MetricReader> metricReaders = Collections.emptyList();
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public EurekaHealthIndicator eurekaHealthIndicator(
|
||||
com.netflix.discovery.DiscoveryClient eurekaDiscoveryClient,
|
||||
MetricReader metrics, EurekaInstanceConfig config) {
|
||||
com.netflix.discovery.DiscoveryClient eurekaDiscoveryClient, EurekaInstanceConfig config) {
|
||||
CompositeMetricReader metrics = new CompositeMetricReader(this.metricReaders.toArray(new MetricReader[0]));
|
||||
return new EurekaHealthIndicator(eurekaDiscoveryClient, metrics, config);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+3
-3
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -50,9 +50,9 @@ public class EurekaServerConfigBean implements EurekaServerConfig {
|
||||
|
||||
private double renewalPercentThreshold = 0.85;
|
||||
|
||||
private int renewalThresholdUpdateIntervalMs = 10 * MINUTES;
|
||||
private int renewalThresholdUpdateIntervalMs = 15 * MINUTES;
|
||||
|
||||
private int peerEurekaNodesUpdateIntervalMs = 15 * MINUTES;
|
||||
private int peerEurekaNodesUpdateIntervalMs = 10 * MINUTES;
|
||||
|
||||
private int numberOfReplicationRetries = 5;
|
||||
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+52
-51
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -16,14 +16,17 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -37,7 +40,6 @@ import feign.Retryer;
|
||||
import feign.codec.Decoder;
|
||||
import feign.codec.Encoder;
|
||||
import feign.codec.ErrorDecoder;
|
||||
import feign.ribbon.LoadBalancingTarget;
|
||||
import feign.slf4j.Slf4jLogger;
|
||||
|
||||
/**
|
||||
@@ -45,7 +47,7 @@ import feign.slf4j.Slf4jLogger;
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean {
|
||||
class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
|
||||
|
||||
private Class<?> type;
|
||||
|
||||
@@ -53,35 +55,7 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean {
|
||||
|
||||
private String url;
|
||||
|
||||
@Autowired
|
||||
private Decoder decoder;
|
||||
|
||||
@Autowired
|
||||
private Encoder encoder;
|
||||
|
||||
@Autowired
|
||||
private Logger logger;
|
||||
|
||||
@Autowired
|
||||
private Contract contract;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Logger.Level logLevel;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Retryer retryer;
|
||||
|
||||
@Autowired(required = false)
|
||||
private ErrorDecoder errorDecoder;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Request.Options options;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Client ribbonClient;
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<RequestInterceptor> requestInterceptors;
|
||||
private ApplicationContext context;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
@@ -91,40 +65,67 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
protected Feign.Builder feign() {
|
||||
// @formatter:off
|
||||
Feign.Builder builder = Feign.builder()
|
||||
// required values
|
||||
.logger(this.logger).encoder(this.encoder).decoder(this.decoder)
|
||||
.contract(this.contract);
|
||||
.logger(get(Logger.class))
|
||||
.encoder(get(Encoder.class))
|
||||
.decoder(get(Decoder.class))
|
||||
.contract(get(Contract.class));
|
||||
// @formatter:on
|
||||
|
||||
// optional values
|
||||
if (this.logLevel != null) {
|
||||
builder.logLevel(this.logLevel);
|
||||
Logger.Level level = getOptional(Logger.Level.class);
|
||||
if (level != null) {
|
||||
builder.logLevel(level);
|
||||
}
|
||||
if (this.retryer != null) {
|
||||
builder.retryer(this.retryer);
|
||||
Retryer retryer = getOptional(Retryer.class);
|
||||
if (retryer != null) {
|
||||
builder.retryer(retryer);
|
||||
}
|
||||
if (this.errorDecoder != null) {
|
||||
builder.errorDecoder(this.errorDecoder);
|
||||
ErrorDecoder errorDecoder = getOptional(ErrorDecoder.class);
|
||||
if (errorDecoder != null) {
|
||||
builder.errorDecoder(errorDecoder);
|
||||
}
|
||||
if (this.options != null) {
|
||||
builder.options(this.options);
|
||||
Request.Options options = getOptional(Request.Options.class);
|
||||
if (options != null) {
|
||||
builder.options(options);
|
||||
}
|
||||
if (this.requestInterceptors != null) {
|
||||
builder.requestInterceptors(this.requestInterceptors);
|
||||
Map<String, RequestInterceptor> requestInterceptors = this.context.getBeansOfType(RequestInterceptor.class);
|
||||
if (requestInterceptors != null) {
|
||||
builder.requestInterceptors(requestInterceptors.values());
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
protected <T> T get(Class<T> type) {
|
||||
return this.context.getBean(type);
|
||||
}
|
||||
|
||||
protected <T> T getOptional(Class<T> type) {
|
||||
try {
|
||||
return this.context.getBean(type);
|
||||
} catch (NoSuchBeanDefinitionException e) {
|
||||
//ignore
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected <T> T loadBalance(Feign.Builder builder, Class<T> type, String schemeName) {
|
||||
builder.logger(new Slf4jLogger(type)); // TODO: how to have choice here?
|
||||
if (this.ribbonClient != null) {
|
||||
return builder.client(this.ribbonClient).target(type, schemeName);
|
||||
}
|
||||
else {
|
||||
return builder.target(LoadBalancingTarget.create(type, schemeName));
|
||||
Client client = getOptional(Client.class);
|
||||
if (client != null) {
|
||||
return builder.client(client).target(type, schemeName);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-ribbon?");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+14
-5
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
|
||||
import org.springframework.cloud.netflix.feign.support.ResponseEntityDecoder;
|
||||
import org.springframework.cloud.netflix.feign.support.SpringDecoder;
|
||||
import org.springframework.cloud.netflix.feign.support.SpringEncoder;
|
||||
import org.springframework.cloud.netflix.feign.support.SpringMvcContract;
|
||||
@@ -24,6 +28,8 @@ import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.Logger;
|
||||
import feign.codec.Decoder;
|
||||
import feign.codec.Encoder;
|
||||
import feign.slf4j.Slf4jLogger;
|
||||
|
||||
/**
|
||||
@@ -32,14 +38,17 @@ import feign.slf4j.Slf4jLogger;
|
||||
@Configuration
|
||||
public class FeignClientsConfiguration {
|
||||
|
||||
@Autowired
|
||||
private ObjectFactory<HttpMessageConverters> messageConverters;
|
||||
|
||||
@Bean
|
||||
public SpringDecoder feignDecoder() {
|
||||
return new SpringDecoder();
|
||||
public Decoder feignDecoder() {
|
||||
return new ResponseEntityDecoder(new SpringDecoder(messageConverters));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpringEncoder feignEncoder() {
|
||||
return new SpringEncoder();
|
||||
public Encoder feignEncoder() {
|
||||
return new SpringEncoder(messageConverters);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
+17
-4
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -17,6 +17,8 @@
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -117,10 +119,21 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
||||
}
|
||||
|
||||
private String getServiceId(Map<String, Object> attributes) {
|
||||
if (StringUtils.hasText((String) attributes.get("name"))) {
|
||||
return (String) attributes.get("name");
|
||||
String name = (String) attributes.get("name");
|
||||
if (!StringUtils.hasText(name)) {
|
||||
name = (String) attributes.get("value");
|
||||
}
|
||||
return (String) attributes.get("value");
|
||||
if (!StringUtils.hasText(name)) {
|
||||
return "";
|
||||
}
|
||||
String host = null;
|
||||
try {
|
||||
host = new URI("http://" + name).getHost();
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
}
|
||||
Assert.state(host != null, "Service id not legal hostname (" + name + ")");
|
||||
return name;
|
||||
}
|
||||
|
||||
private String getUrl(Map<String, Object> attributes) {
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import feign.ribbon.LBClient;
|
||||
import feign.ribbon.LBClientFactory;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* LBClientFactory that caches entries created.
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class CachingLBClientFactory implements LBClientFactory {
|
||||
|
||||
private volatile Map<String, LBClient> cache = new ConcurrentReferenceHashMap<>();
|
||||
private final LBClientFactory delegate;
|
||||
|
||||
public CachingLBClientFactory(LBClientFactory delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LBClient create(String clientName) {
|
||||
if (cache.containsKey(clientName)) {
|
||||
return cache.get(clientName);
|
||||
}
|
||||
LBClient client = delegate.create(clientName);
|
||||
cache.put(clientName, client);
|
||||
return client;
|
||||
}
|
||||
}
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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.ribbon;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import org.springframework.cloud.netflix.feign.ribbon.RibbonLoadBalancer.RibbonRequest;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import com.netflix.client.ClientException;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
|
||||
import dagger.Lazy;
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
|
||||
/**
|
||||
* @author Julien Roy
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class FeignRibbonClient implements Client {
|
||||
|
||||
private Client defaultClient = new Default(new LazySSLSocketFactory(),
|
||||
new LazyHostnameVerifier());
|
||||
|
||||
private SpringClientFactory factory;
|
||||
|
||||
public FeignRibbonClient(SpringClientFactory factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response execute(Request request, Request.Options options) throws IOException {
|
||||
try {
|
||||
URI asUri = URI.create(request.url());
|
||||
String clientName = asUri.getHost();
|
||||
URI uriWithoutSchemeAndPort = URI.create(request.url().replace(
|
||||
asUri.getScheme() + "://" + asUri.getHost(),
|
||||
asUri.getScheme() + "://"));
|
||||
RibbonLoadBalancer.RibbonRequest ribbonRequest = new RibbonRequest(request,
|
||||
uriWithoutSchemeAndPort);
|
||||
LBClient client = getClient(clientName);
|
||||
return client.getLoadBalancer()
|
||||
.executeWithLoadBalancer(ribbonRequest, client.config).toResponse();
|
||||
}
|
||||
catch (ClientException ex) {
|
||||
if (ex.getCause() instanceof IOException) {
|
||||
throw IOException.class.cast(ex.getCause());
|
||||
}
|
||||
ReflectionUtils.rethrowRuntimeException(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
private class LBClient {
|
||||
private final IClientConfig config;
|
||||
private final RibbonLoadBalancer loadBalancer;
|
||||
}
|
||||
|
||||
private LBClient getClient(String clientName) {
|
||||
IClientConfig config = this.factory.getClientConfig(clientName);
|
||||
ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
|
||||
RibbonLoadBalancer loadBalancer = new RibbonLoadBalancer(this.defaultClient, lb,
|
||||
config);
|
||||
return new LBClient(config, loadBalancer);
|
||||
}
|
||||
|
||||
public void setDefaultClient(Client defaultClient) {
|
||||
this.defaultClient = defaultClient;
|
||||
}
|
||||
|
||||
private static class LazySSLSocketFactory implements Lazy<SSLSocketFactory> {
|
||||
|
||||
@Override
|
||||
public SSLSocketFactory get() {
|
||||
return (SSLSocketFactory) SSLSocketFactory.getDefault();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class LazyHostnameVerifier implements Lazy<HostnameVerifier> {
|
||||
|
||||
@Override
|
||||
public HostnameVerifier get() {
|
||||
return HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+21
-3
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -16,8 +16,11 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import feign.ribbon.RibbonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cloud.netflix.feign.FeignAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -38,8 +41,23 @@ import feign.Feign;
|
||||
@Configuration
|
||||
@AutoConfigureBefore(FeignAutoConfiguration.class)
|
||||
public class FeignRibbonClientAutoConfiguration {
|
||||
|
||||
@Autowired
|
||||
private SpringClientFactory factory;
|
||||
|
||||
@Bean
|
||||
public Client feignRibbonClient(SpringClientFactory factory) {
|
||||
return new FeignRibbonClient(factory);
|
||||
public SpringLBClientFactory springLBClientFactory() {
|
||||
return new SpringLBClientFactory(factory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CachingLBClientFactory cachingLBClientFactory() {
|
||||
return new CachingLBClientFactory(springLBClientFactory());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public Client feignRibbonClient() {
|
||||
return RibbonClient.builder().lbClientFactory(cachingLBClientFactory()).build();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import feign.ribbon.LBClient;
|
||||
import feign.ribbon.LBClientFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class SpringLBClientFactory implements LBClientFactory {
|
||||
|
||||
private final SpringClientFactory factory;
|
||||
|
||||
public SpringLBClientFactory(SpringClientFactory factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LBClient create(String clientName) {
|
||||
IClientConfig config = factory.getClientConfig(clientName);
|
||||
ILoadBalancer lb = factory.getLoadBalancer(clientName);
|
||||
return LBClient.create(lb, config);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign.support;
|
||||
|
||||
import feign.FeignException;
|
||||
import feign.Response;
|
||||
import feign.codec.Decoder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Decoder adds compatibility for Spring MVC's ResponseEntity to any
|
||||
* other decoder via composition.
|
||||
* @author chadjaros
|
||||
*/
|
||||
@Slf4j
|
||||
public class ResponseEntityDecoder implements Decoder {
|
||||
|
||||
private Decoder decoder;
|
||||
|
||||
public ResponseEntityDecoder(Decoder decoder) {
|
||||
this.decoder = decoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object decode(final Response response, Type type) throws IOException,
|
||||
FeignException {
|
||||
|
||||
if(type instanceof ParameterizedType &&
|
||||
((ParameterizedType) type).getRawType().equals(ResponseEntity.class)) {
|
||||
|
||||
type = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
Object decodedObject = decoder.decode(response, type);
|
||||
|
||||
Class<?> clazz = null;
|
||||
if (decodedObject != null) {
|
||||
clazz = decodedObject.getClass();
|
||||
}
|
||||
return createResponse(
|
||||
clazz,
|
||||
decodedObject,
|
||||
response);
|
||||
}
|
||||
else {
|
||||
return decoder.decode(response, type);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> ResponseEntity<T> createResponse(Class<T> clazz, Object instance, Response response) {
|
||||
|
||||
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
|
||||
for(String key: response.headers().keySet()) {
|
||||
headers.put(key, new LinkedList<>(response.headers().get(key)));
|
||||
}
|
||||
|
||||
T retVal = null;
|
||||
if (clazz != null && instance != null) {
|
||||
retVal = clazz.cast(instance);
|
||||
}
|
||||
return new ResponseEntity<>(retVal, headers,
|
||||
HttpStatus.valueOf(response.status()));
|
||||
}
|
||||
}
|
||||
+8
-10
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -16,22 +16,20 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign.support;
|
||||
|
||||
import static org.springframework.cloud.netflix.feign.support.FeignUtils.getHttpHeaders;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import javax.inject.Provider;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.web.client.HttpMessageConverterExtractor;
|
||||
|
||||
import static org.springframework.cloud.netflix.feign.support.FeignUtils.getHttpHeaders;
|
||||
|
||||
import feign.FeignException;
|
||||
import feign.Response;
|
||||
import feign.codec.DecodeException;
|
||||
@@ -42,10 +40,10 @@ import feign.codec.Decoder;
|
||||
*/
|
||||
public class SpringDecoder implements Decoder {
|
||||
|
||||
@Autowired
|
||||
private Provider<HttpMessageConverters> messageConverters;
|
||||
private ObjectFactory<HttpMessageConverters> messageConverters;
|
||||
|
||||
public SpringDecoder() {
|
||||
public SpringDecoder(ObjectFactory<HttpMessageConverters> messageConverters) {
|
||||
this.messageConverters = messageConverters;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,7 +52,7 @@ public class SpringDecoder implements Decoder {
|
||||
if (type instanceof Class || type instanceof ParameterizedType) {
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
HttpMessageConverterExtractor<?> extractor = new HttpMessageConverterExtractor(
|
||||
type, this.messageConverters.get().getConverters());
|
||||
type, this.messageConverters.getObject().getConverters());
|
||||
|
||||
return extractor.extractData(new FeignResponseAdapter(response));
|
||||
}
|
||||
|
||||
+13
-11
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -16,17 +16,18 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign.support;
|
||||
|
||||
import static org.springframework.cloud.netflix.feign.support.FeignUtils.getHttpHeaders;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.inject.Provider;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
@@ -37,19 +38,20 @@ import feign.RequestTemplate;
|
||||
import feign.codec.EncodeException;
|
||||
import feign.codec.Encoder;
|
||||
|
||||
import static org.springframework.cloud.netflix.feign.support.FeignUtils.getHttpHeaders;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@CommonsLog
|
||||
public class SpringEncoder implements Encoder {
|
||||
|
||||
@Autowired
|
||||
private Provider<HttpMessageConverters> messageConverters;
|
||||
private ObjectFactory<HttpMessageConverters> messageConverters;
|
||||
|
||||
public SpringEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
|
||||
this.messageConverters = messageConverters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Object requestBody, RequestTemplate request)
|
||||
public void encode(Object requestBody, Type bodyType, RequestTemplate request)
|
||||
throws EncodeException {
|
||||
// template.body(conversionService.convert(object, String.class));
|
||||
if (requestBody != null) {
|
||||
@@ -62,8 +64,8 @@ public class SpringEncoder implements Encoder {
|
||||
requestContentType = MediaType.valueOf(type);
|
||||
}
|
||||
|
||||
for (HttpMessageConverter<?> messageConverter : this.messageConverters.get()
|
||||
.getConverters()) {
|
||||
for (HttpMessageConverter<?> messageConverter : this.messageConverters
|
||||
.getObject().getConverters()) {
|
||||
if (messageConverter.canWrite(requestType, requestContentType)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
if (requestContentType != null) {
|
||||
|
||||
+97
-51
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -16,19 +16,18 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.MethodMetadata;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.MethodMetadata;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import static feign.Util.checkState;
|
||||
import static feign.Util.emptyToNull;
|
||||
@@ -42,56 +41,72 @@ public class SpringMvcContract extends Contract.BaseContract {
|
||||
|
||||
private static final String CONTENT_TYPE = "Content-Type";
|
||||
|
||||
@Override
|
||||
public MethodMetadata parseAndValidatateMetadata(Method method) {
|
||||
MethodMetadata md = super.parseAndValidatateMetadata(method);
|
||||
|
||||
RequestMapping classAnnotation = method.getDeclaringClass().getAnnotation(RequestMapping.class);
|
||||
if (classAnnotation != null) {
|
||||
// Prepend path from class annotation if specified
|
||||
if (classAnnotation.value().length > 0) {
|
||||
String pathValue = emptyToNull(classAnnotation.value()[0]);
|
||||
checkState(pathValue != null, "RequestMapping.value() was empty on type %s",
|
||||
method.getDeclaringClass().getName());
|
||||
if (!pathValue.startsWith("/")) {
|
||||
pathValue = "/" + pathValue;
|
||||
}
|
||||
md.template().insert(0, pathValue);
|
||||
}
|
||||
|
||||
// produces - use from class annotation only if method has not specified this
|
||||
if(!md.template().headers().containsKey(ACCEPT)) {
|
||||
parseProduces(md, method, classAnnotation);
|
||||
}
|
||||
|
||||
// consumes -- use from class annotation only if method has not specified this
|
||||
if(!md.template().headers().containsKey(CONTENT_TYPE)) {
|
||||
parseConsumes(md, method, classAnnotation);
|
||||
}
|
||||
|
||||
// headers -- class annotation is inherited to methods, always write these if present
|
||||
parseHeaders(md, method, classAnnotation);
|
||||
}
|
||||
return md;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processAnnotationOnMethod(MethodMetadata data,
|
||||
Annotation methodAnnotation, Method method) {
|
||||
RequestMapping mapping = RequestMapping.class.cast(methodAnnotation);
|
||||
if (mapping != null) {
|
||||
// HTTP Method
|
||||
checkOne(method, mapping.method(), "method");
|
||||
data.template().method(mapping.method()[0].name());
|
||||
if (!(methodAnnotation instanceof RequestMapping)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// path
|
||||
checkOne(method, mapping.value(), "value");
|
||||
RequestMapping methodMapping = RequestMapping.class.cast(methodAnnotation);
|
||||
// HTTP Method
|
||||
checkOne(method, methodMapping.method(), "method");
|
||||
data.template().method(methodMapping.method()[0].name());
|
||||
|
||||
String methodAnnotationValue = mapping.value()[0];
|
||||
String pathValue = emptyToNull(methodAnnotationValue);
|
||||
checkState(pathValue != null, "value was empty on method %s",
|
||||
method.getName());
|
||||
if (!methodAnnotationValue.startsWith("/")
|
||||
&& !data.template().toString().endsWith("/")) {
|
||||
methodAnnotationValue = "/" + methodAnnotationValue;
|
||||
}
|
||||
data.template().append(methodAnnotationValue);
|
||||
|
||||
// produces
|
||||
checkAtMostOne(method, mapping.produces(), "produces");
|
||||
String[] serverProduces = mapping.produces();
|
||||
String clientAccepts = serverProduces.length == 0 ? null
|
||||
: emptyToNull(serverProduces[0]);
|
||||
if (clientAccepts != null) {
|
||||
data.template().header(ACCEPT, clientAccepts);
|
||||
}
|
||||
|
||||
// consumes
|
||||
checkAtMostOne(method, mapping.consumes(), "consumes");
|
||||
String[] serverConsumes = mapping.consumes();
|
||||
String clientProduces = serverConsumes.length == 0 ? null
|
||||
: emptyToNull(serverConsumes[0]);
|
||||
if (clientProduces != null) {
|
||||
data.template().header(CONTENT_TYPE, clientProduces);
|
||||
}
|
||||
|
||||
// headers
|
||||
// TODO: only supports one header value per key
|
||||
if (mapping.headers() != null && mapping.headers().length > 0) {
|
||||
for (String header : mapping.headers()) {
|
||||
int colon = header.indexOf(':');
|
||||
data.template().header(header.substring(0, colon),
|
||||
header.substring(colon + 2));
|
||||
// path
|
||||
checkAtMostOne(method, methodMapping.value(), "value");
|
||||
if(methodMapping.value().length > 0) {
|
||||
String pathValue = emptyToNull(methodMapping.value()[0]);
|
||||
if (pathValue != null) {
|
||||
// Append path from @RequestMapping if value is present on method
|
||||
if (!pathValue.startsWith("/") && !data.template().toString().endsWith("/")) {
|
||||
pathValue = "/" + pathValue;
|
||||
}
|
||||
data.template().append(pathValue);
|
||||
}
|
||||
}
|
||||
|
||||
// produces
|
||||
parseProduces(data, method, methodMapping);
|
||||
|
||||
// consumes
|
||||
parseConsumes(data, method, methodMapping);
|
||||
|
||||
// headers
|
||||
parseHeaders(data, method, methodMapping);
|
||||
}
|
||||
|
||||
private void checkAtMostOne(Method method, Object[] values, String fieldName) {
|
||||
@@ -176,4 +191,35 @@ public class SpringMvcContract extends Contract.BaseContract {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void parseProduces(MethodMetadata md, Method method, RequestMapping annotation) {
|
||||
checkAtMostOne(method, annotation.produces(), "produces");
|
||||
String[] serverProduces = annotation.produces();
|
||||
String clientAccepts = serverProduces.length == 0 ? null
|
||||
: emptyToNull(serverProduces[0]);
|
||||
if (clientAccepts != null) {
|
||||
md.template().header(ACCEPT, clientAccepts);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseConsumes(MethodMetadata md, Method method, RequestMapping annotation) {
|
||||
checkAtMostOne(method, annotation.consumes(), "consumes");
|
||||
String[] serverConsumes = annotation.consumes();
|
||||
String clientProduces = serverConsumes.length == 0 ? null
|
||||
: emptyToNull(serverConsumes[0]);
|
||||
if (clientProduces != null) {
|
||||
md.template().header(CONTENT_TYPE, clientProduces);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseHeaders(MethodMetadata md, Method method, RequestMapping annotation) {
|
||||
// TODO: only supports one header value per key
|
||||
if (annotation.headers() != null && annotation.headers().length > 0) {
|
||||
for (String header : annotation.headers()) {
|
||||
int colon = header.indexOf(':');
|
||||
md.template().header(header.substring(0, colon),
|
||||
header.substring(colon + 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+18
-2
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -21,6 +21,7 @@ import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@@ -33,13 +34,28 @@ import com.netflix.hystrix.Hystrix;
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ Hystrix.class, HealthIndicator.class })
|
||||
@ConditionalOnProperty(value = "health.hystrix.enabled", matchIfMissing = true)
|
||||
@AutoConfigureAfter({ HealthIndicatorAutoConfiguration.class })
|
||||
@ConditionalOnProperty(value = "health.hystrix.enabled", matchIfMissing = true)
|
||||
public class HystrixAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public HystrixHealthIndicator hystrixHealthIndicator() {
|
||||
return new HystrixHealthIndicator();
|
||||
}
|
||||
|
||||
@ConfigurationProperties("health.hystrix")
|
||||
public static class Health {
|
||||
/**
|
||||
* Flag to inidicate that the hystrix health indicator should be installed.
|
||||
*/
|
||||
boolean enabled;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+4
-4
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -30,8 +30,8 @@ import com.netflix.hystrix.HystrixCommandMetrics;
|
||||
/**
|
||||
* A {@link HealthIndicator} implementation for Hystrix circuit breakers.
|
||||
* <p>
|
||||
* This default implementation will set the system to <code>OUT_OF_SERVICE</code> and
|
||||
* include all open circuits by name.
|
||||
* This default implementation will not change the system state (e.g. <code>OK</code>) but
|
||||
* includes all open circuits by name.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
*/
|
||||
@@ -47,7 +47,7 @@ public class HystrixHealthIndicator extends AbstractHealthIndicator {
|
||||
for (HystrixCommandMetrics metrics : HystrixCommandMetrics.getInstances()) {
|
||||
HystrixCircuitBreaker circuitBreaker = HystrixCircuitBreaker.Factory
|
||||
.getInstance(metrics.getCommandKey());
|
||||
if (circuitBreaker.isOpen()) {
|
||||
if (circuitBreaker != null && circuitBreaker.isOpen()) {
|
||||
openCircuitBreakers.add(metrics.getCommandGroup().name() + "::"
|
||||
+ metrics.getCommandKey().name());
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+30
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -26,12 +26,14 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.netflix.client.IClient;
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
|
||||
/**
|
||||
* Auto configuration for Ribbon (client side load balancing).
|
||||
@@ -49,6 +51,7 @@ public class RibbonAutoConfiguration {
|
||||
@Autowired(required = false)
|
||||
private List<RibbonClientSpecification> configurations = new ArrayList<>();
|
||||
|
||||
|
||||
@Bean
|
||||
public SpringClientFactory springClientFactory() {
|
||||
SpringClientFactory factory = new SpringClientFactory();
|
||||
@@ -62,4 +65,30 @@ public class RibbonAutoConfiguration {
|
||||
return new RibbonLoadBalancerClient(springClientFactory());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(HttpRequest.class)
|
||||
protected static class RibbonClientConfig {
|
||||
|
||||
@Autowired
|
||||
private SpringClientFactory springClientFactory;
|
||||
|
||||
@Autowired
|
||||
private LoadBalancerClient loadBalancerClient;
|
||||
|
||||
@Bean
|
||||
public RestTemplateCustomizer restTemplateCustomizer() {
|
||||
return new RestTemplateCustomizer() {
|
||||
@Override
|
||||
public void customize(RestTemplate restTemplate) {
|
||||
restTemplate.setRequestFactory(ribbonClientHttpRequestFactory());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory() {
|
||||
return new RibbonClientHttpRequestFactory(springClientFactory, loadBalancerClient);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+37
-2
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -16,11 +16,14 @@
|
||||
|
||||
package org.springframework.cloud.netflix.ribbon;
|
||||
|
||||
import org.apache.http.client.params.ClientPNames;
|
||||
import org.apache.http.client.params.CookiePolicy;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
||||
import com.netflix.client.config.DefaultClientConfigImpl;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
@@ -37,6 +40,8 @@ import com.netflix.loadbalancer.ZoneAvoidanceRule;
|
||||
import com.netflix.loadbalancer.ZoneAwareLoadBalancer;
|
||||
import com.netflix.niws.client.http.RestClient;
|
||||
import com.netflix.servo.monitor.Monitors;
|
||||
import com.sun.jersey.api.client.Client;
|
||||
import com.sun.jersey.client.apache4.ApacheHttpClient4;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
@@ -83,10 +88,20 @@ public class RibbonClientConfiguration {
|
||||
return serverList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Netflix {@link RestClient} integrated with Ribbon if none already exists in the
|
||||
* application context. It is not required for Ribbon to work properly and is therefore
|
||||
* created lazily if ever another component requires it.
|
||||
*
|
||||
* @param config the configuration to use by the underlying Ribbon instance
|
||||
* @param loadBalancer the load balancer to use by the underlying Ribbon instance
|
||||
* @return a {@link RestClient} instances backed by Ribbon
|
||||
*/
|
||||
@Bean
|
||||
@Lazy
|
||||
@ConditionalOnMissingBean
|
||||
public RestClient ribbonRestClient(IClientConfig config, ILoadBalancer loadBalancer) {
|
||||
RestClient client = new RestClient(config);
|
||||
RestClient client = new OverrideRestClient(config);
|
||||
client.setLoadBalancer(loadBalancer);
|
||||
Monitors.registerObject("Client_" + this.name, client);
|
||||
return client;
|
||||
@@ -119,4 +134,24 @@ public class RibbonClientConfiguration {
|
||||
return new RibbonLoadBalancerContext(loadBalancer, config);
|
||||
}
|
||||
|
||||
static class OverrideRestClient extends RestClient {
|
||||
|
||||
protected OverrideRestClient(IClientConfig ncc) {
|
||||
super();
|
||||
initWithNiwsConfig(ncc);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Client apacheHttpClientSpecificInitialization() {
|
||||
ApacheHttpClient4 apache = (ApacheHttpClient4) super
|
||||
.apacheHttpClientSpecificInitialization();
|
||||
apache.getClientHandler()
|
||||
.getHttpClient()
|
||||
.getParams()
|
||||
.setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.IGNORE_COOKIES);
|
||||
return apache;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-8
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -38,13 +38,18 @@ public class RibbonClientConfigurationRegistrar implements ImportBeanDefinitionR
|
||||
if (attrs != null && attrs.containsKey("value")) {
|
||||
AnnotationAttributes[] clients = (AnnotationAttributes[]) attrs.get("value");
|
||||
for (AnnotationAttributes client : clients) {
|
||||
registerClientConfiguration(registry, client.get("name"),
|
||||
registerClientConfiguration(registry, getClientName(client),
|
||||
client.get("configuration"));
|
||||
}
|
||||
}
|
||||
if (attrs != null && attrs.containsKey("defaultConfiguration")) {
|
||||
registerClientConfiguration(registry,
|
||||
"default." + metadata.getEnclosingClassName(),
|
||||
String name;
|
||||
if (metadata.hasEnclosingClass()) {
|
||||
name = "default." + metadata.getEnclosingClassName();
|
||||
} else {
|
||||
name = "default." + metadata.getClassName();
|
||||
}
|
||||
registerClientConfiguration(registry, name,
|
||||
attrs.get("defaultConfiguration"));
|
||||
}
|
||||
Map<String, Object> client = metadata.getAnnotationAttributes(
|
||||
@@ -60,11 +65,10 @@ public class RibbonClientConfigurationRegistrar implements ImportBeanDefinitionR
|
||||
return null;
|
||||
}
|
||||
String value = (String) client.get("value");
|
||||
if (value != null && StringUtils.hasText(value)) {
|
||||
return value;
|
||||
if (!StringUtils.hasText(value)) {
|
||||
value = (String) client.get("name");
|
||||
}
|
||||
value = (String) client.get("name");
|
||||
if (value != null && StringUtils.hasText(value)) {
|
||||
if (StringUtils.hasText(value)) {
|
||||
return value;
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.ribbon;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.AbstractClientHttpRequest;
|
||||
import org.springframework.http.client.AbstractClientHttpResponse;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
import com.netflix.client.http.HttpResponse;
|
||||
import com.netflix.niws.client.http.RestClient;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class RibbonClientHttpRequestFactory implements ClientHttpRequestFactory {
|
||||
|
||||
private final SpringClientFactory clientFactory;
|
||||
|
||||
private LoadBalancerClient loadBalancer;
|
||||
|
||||
public RibbonClientHttpRequestFactory(SpringClientFactory clientFactory, LoadBalancerClient loadBalancer) {
|
||||
this.clientFactory = clientFactory;
|
||||
this.loadBalancer = loadBalancer;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public ClientHttpRequest createRequest(URI originalUri, HttpMethod httpMethod)
|
||||
throws IOException {
|
||||
String serviceId = originalUri.getHost();
|
||||
ServiceInstance instance = loadBalancer.choose(serviceId);
|
||||
if (instance == null) {
|
||||
throw new IllegalStateException("No instances available for "+serviceId);
|
||||
}
|
||||
URI uri = loadBalancer.reconstructURI(instance, originalUri);
|
||||
//@formatter:off
|
||||
IClientConfig clientConfig = clientFactory.getClientConfig(instance.getServiceId());
|
||||
RestClient client = clientFactory.getClient(instance.getServiceId(), RestClient.class);
|
||||
HttpRequest.Verb verb = HttpRequest.Verb.valueOf(httpMethod.name());
|
||||
//@formatter:on
|
||||
return new RibbonHttpRequest(uri, verb, client, clientConfig);
|
||||
}
|
||||
|
||||
public class RibbonHttpRequest extends AbstractClientHttpRequest {
|
||||
|
||||
private HttpRequest.Builder builder;
|
||||
private URI uri;
|
||||
private HttpRequest.Verb verb;
|
||||
private RestClient client;
|
||||
private IClientConfig config;
|
||||
private ByteArrayOutputStream outputStream = null;
|
||||
|
||||
public RibbonHttpRequest(URI uri, HttpRequest.Verb verb, RestClient client,
|
||||
IClientConfig config) {
|
||||
this.uri = uri;
|
||||
this.verb = verb;
|
||||
this.client = client;
|
||||
this.config = config;
|
||||
this.builder = HttpRequest.newBuilder().uri(uri).verb(verb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpMethod getMethod() {
|
||||
return HttpMethod.valueOf(verb.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OutputStream getBodyInternal(HttpHeaders headers) throws IOException {
|
||||
if (outputStream == null) {
|
||||
outputStream = new ByteArrayOutputStream();
|
||||
}
|
||||
return outputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
protected ClientHttpResponse executeInternal(HttpHeaders headers)
|
||||
throws IOException {
|
||||
try {
|
||||
addHeaders(headers);
|
||||
if (outputStream != null) {
|
||||
outputStream.close();
|
||||
builder.entity(outputStream.toByteArray());
|
||||
}
|
||||
HttpRequest request = builder.build();
|
||||
HttpResponse response = client.execute(request, config);
|
||||
return new RibbonHttpResponse(response);
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
//TODO: fix stats, now that execute is not called
|
||||
// use execute here so stats are collected
|
||||
/*return loadBalancer.execute(this.config.getClientName(), new LoadBalancerRequest<ClientHttpResponse>() {
|
||||
@Override
|
||||
public ClientHttpResponse apply(ServiceInstance instance) throws Exception {
|
||||
}
|
||||
});*/
|
||||
}
|
||||
|
||||
private void addHeaders(HttpHeaders headers) {
|
||||
for (String name : headers.keySet()) {
|
||||
// apache http RequestContent pukes if there is a body and
|
||||
// the dynamic headers are already present
|
||||
if (!isDynamic(name) || outputStream == null) {
|
||||
List<String> values = headers.get(name);
|
||||
for (String value : values) {
|
||||
builder.header(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDynamic(String name) {
|
||||
return name.equals("Content-Length") || name.equals("Transfer-Encoding");
|
||||
}
|
||||
}
|
||||
|
||||
public class RibbonHttpResponse extends AbstractClientHttpResponse {
|
||||
|
||||
private HttpResponse response;
|
||||
private HttpHeaders httpHeaders;
|
||||
|
||||
public RibbonHttpResponse(HttpResponse response) {
|
||||
this.response = response;
|
||||
this.httpHeaders = new HttpHeaders();
|
||||
List<Map.Entry<String, String>> headers = response.getHttpHeaders()
|
||||
.getAllHeaders();
|
||||
for (Map.Entry<String, String> header : headers) {
|
||||
this.httpHeaders.add(header.getKey(), header.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getBody() throws IOException {
|
||||
return response.getInputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return this.httpHeaders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRawStatusCode() throws IOException {
|
||||
return response.getStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusText() throws IOException {
|
||||
return HttpStatus.valueOf(response.getStatus()).name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
response.close();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+3
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -23,6 +23,7 @@ import org.springframework.cloud.client.DefaultServiceInstance;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
@@ -44,6 +45,7 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
|
||||
|
||||
@Override
|
||||
public URI reconstructURI(ServiceInstance instance, URI original) {
|
||||
Assert.notNull(instance, "instance can not be null");
|
||||
String serviceId = instance.getServiceId();
|
||||
RibbonLoadBalancerContext context = this.clientFactory
|
||||
.getLoadBalancerContext(serviceId);
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+2
-18
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -22,8 +22,6 @@ import java.util.List;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
@@ -92,7 +90,7 @@ class DomainExtractingServer extends DiscoveryEnabledServer {
|
||||
setZone(server.getInstanceInfo().getMetadata().get("zone"));
|
||||
}
|
||||
else if (approximateZoneFromHostname) {
|
||||
setZone(extractApproximateZone(server));
|
||||
setZone(ZoneUtils.extractApproximateZone(server.getHost()));
|
||||
}
|
||||
else {
|
||||
setZone(server.getZone());
|
||||
@@ -112,18 +110,4 @@ class DomainExtractingServer extends DiscoveryEnabledServer {
|
||||
}
|
||||
return super.getId();
|
||||
}
|
||||
|
||||
private String extractApproximateZone(Server server) {
|
||||
String host = server.getHost();
|
||||
if (!host.contains(".")) {
|
||||
return host;
|
||||
}
|
||||
String[] split = StringUtils.split(host, ".");
|
||||
StringBuilder builder = new StringBuilder(split[1]);
|
||||
for (int i = 2; i < split.length; i++) {
|
||||
builder.append(".").append(split[i]);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+38
-13
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -21,11 +21,15 @@ import static com.netflix.client.config.CommonClientConfigKey.EnableZoneAffinity
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
@@ -45,8 +49,10 @@ import com.netflix.niws.loadbalancer.NIWSDiscoveryPing;
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@Configuration
|
||||
@CommonsLog
|
||||
public class EurekaRibbonClientConfiguration {
|
||||
|
||||
@Value("${ribbon.eureka.approximateZoneFromHostname:false}")
|
||||
@@ -62,13 +68,19 @@ public class EurekaRibbonClientConfiguration {
|
||||
@Autowired(required = false)
|
||||
private EurekaClientConfig clientConfig;
|
||||
|
||||
@Autowired
|
||||
private EurekaInstanceConfigBean eurekaConfig;
|
||||
|
||||
public EurekaRibbonClientConfiguration() {
|
||||
}
|
||||
|
||||
public EurekaRibbonClientConfiguration(EurekaClientConfig clientConfig,
|
||||
String serviceId) {
|
||||
String serviceId, EurekaInstanceConfigBean eurekaConfig,
|
||||
boolean approximateZoneFromHostname) {
|
||||
this.clientConfig = clientConfig;
|
||||
this.serviceId = serviceId;
|
||||
this.eurekaConfig = eurekaConfig;
|
||||
this.approximateZoneFromHostname = approximateZoneFromHostname;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -82,23 +94,36 @@ public class EurekaRibbonClientConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ServerList<?> ribbonServerList(IClientConfig config) {
|
||||
DiscoveryEnabledNIWSServerList discoveryServerList = new DiscoveryEnabledNIWSServerList(config);
|
||||
DomainExtractingServerList serverList = new DomainExtractingServerList(discoveryServerList, config, this.approximateZoneFromHostname);
|
||||
DiscoveryEnabledNIWSServerList discoveryServerList = new DiscoveryEnabledNIWSServerList(
|
||||
config);
|
||||
DomainExtractingServerList serverList = new DomainExtractingServerList(
|
||||
discoveryServerList, config, this.approximateZoneFromHostname);
|
||||
return serverList;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void preprocess() {
|
||||
if (this.clientConfig != null
|
||||
&& ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone) == null) {
|
||||
String[] zones = this.clientConfig.getAvailabilityZones(this.clientConfig
|
||||
.getRegion());
|
||||
String zone = zones != null && zones.length > 0 ? zones[0] : null;
|
||||
if (zone != null) {
|
||||
// You can set this with archaius.deployment.* (maybe requires
|
||||
// custom deployment context)?
|
||||
String zone = ConfigurationManager.getDeploymentContext().getValue(
|
||||
ContextKey.zone);
|
||||
if (this.clientConfig != null && StringUtils.isEmpty(zone)) {
|
||||
if (approximateZoneFromHostname) {
|
||||
String approxZone = ZoneUtils.extractApproximateZone(eurekaConfig
|
||||
.getHostname());
|
||||
log.debug("Setting Zone To " + approxZone);
|
||||
ConfigurationManager.getDeploymentContext().setValue(ContextKey.zone,
|
||||
zone);
|
||||
approxZone);
|
||||
}
|
||||
else {
|
||||
String[] zones = this.clientConfig.getAvailabilityZones(this.clientConfig
|
||||
.getRegion());
|
||||
String availabilityZone = zones != null && zones.length > 0 ? zones[0]
|
||||
: null;
|
||||
if (availabilityZone != null) {
|
||||
// You can set this with archaius.deployment.* (maybe requires
|
||||
// custom deployment context)?
|
||||
ConfigurationManager.getDeploymentContext().setValue(ContextKey.zone,
|
||||
availabilityZone);
|
||||
}
|
||||
}
|
||||
}
|
||||
setProp(this.serviceId, DeploymentContextBasedVipAddresses.key(), this.serviceId);
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.netflix.ribbon.eureka;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Utility class for dealing with zones.
|
||||
* @author Ryan Baxter
|
||||
*
|
||||
*/
|
||||
public class ZoneUtils {
|
||||
|
||||
/**
|
||||
* Approximates Eureka zones from a host name. This method approximates the zone to be
|
||||
* everything after the first "." in the host name.
|
||||
* @param host The host name to extract the host name from
|
||||
* @return The approximate zone
|
||||
*/
|
||||
public static String extractApproximateZone(String host) {
|
||||
if (!host.contains(".")) {
|
||||
return host;
|
||||
}
|
||||
String[] split = StringUtils.split(host, ".");
|
||||
StringBuilder builder = new StringBuilder(split[1]);
|
||||
for (int i = 2; i < split.length; i++) {
|
||||
builder.append(".").append(split[i]);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.servo;
|
||||
|
||||
import com.netflix.servo.Metric;
|
||||
import com.netflix.servo.annotations.DataSourceType;
|
||||
import com.netflix.servo.monitor.MonitorConfig;
|
||||
import com.netflix.servo.tag.Tag;
|
||||
import com.netflix.servo.tag.TagList;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class DefaultServoMetricNaming implements ServoMetricNaming {
|
||||
private static final String JMX_DOMAIN_KEY = "JmxDomain";
|
||||
public static final String SERVO = "servo.";
|
||||
|
||||
@Override
|
||||
public String getName(Metric metric) {
|
||||
MonitorConfig config = metric.getConfig();
|
||||
TagList tags = config.getTags();
|
||||
|
||||
Tag domainTag = tags.getTag(JMX_DOMAIN_KEY);
|
||||
String name;
|
||||
if (domainTag != null) { // jmx metric
|
||||
name = handleJmxMetric(config, tags);
|
||||
}
|
||||
else {
|
||||
name = handleMetric(config, tags);
|
||||
}
|
||||
return name.toLowerCase();
|
||||
}
|
||||
|
||||
private String handleMetric(MonitorConfig config, TagList tags) {
|
||||
String type = cleanValue(tags.getTag(DataSourceType.KEY), false);
|
||||
String instanceName = cleanValue(tags.getTag("instance"), false);
|
||||
String name = cleanupIllegalCharacters(config.getName(), true);
|
||||
String statistic = cleanValue(tags.getTag("statistic"), false);
|
||||
|
||||
StringBuilder nameBuilder = new StringBuilder();
|
||||
if (type != null) {
|
||||
nameBuilder.append(type).append(".");
|
||||
}
|
||||
nameBuilder.append(SERVO);
|
||||
if (instanceName != null) {
|
||||
nameBuilder.append(instanceName).append(".");
|
||||
}
|
||||
if (name != null) {
|
||||
nameBuilder.append(name).append(".");
|
||||
}
|
||||
if (statistic != null) {
|
||||
nameBuilder.append(statistic).append(".");
|
||||
}
|
||||
// remove trailing "."
|
||||
nameBuilder.deleteCharAt(nameBuilder.lastIndexOf("."));
|
||||
return nameBuilder.toString();
|
||||
}
|
||||
|
||||
private String handleJmxMetric(MonitorConfig config, TagList tags) {
|
||||
String domain = cleanValue(tags.getTag(JMX_DOMAIN_KEY), true);
|
||||
String type = cleanValue(tags.getTag("Jmx.type"), false);
|
||||
String instanceName = cleanValue(tags.getTag("Jmx.instance"), false);
|
||||
String name = cleanValue(tags.getTag("Jmx.name"), true);
|
||||
String fieldName = cleanupIllegalCharacters(config.getName(), false);
|
||||
|
||||
StringBuilder nameBuilder = new StringBuilder();
|
||||
nameBuilder.append(domain).append(".");
|
||||
if (type != null) {
|
||||
nameBuilder.append(type).append(".");
|
||||
}
|
||||
nameBuilder.append(SERVO);
|
||||
if (instanceName != null) {
|
||||
nameBuilder.append(instanceName).append(".");
|
||||
}
|
||||
if (name != null) {
|
||||
nameBuilder.append(name).append(".");
|
||||
}
|
||||
if (fieldName != null) {
|
||||
nameBuilder.append(fieldName).append(".");
|
||||
}
|
||||
// remove trailing "."
|
||||
nameBuilder.deleteCharAt(nameBuilder.lastIndexOf("."));
|
||||
return nameBuilder.toString();
|
||||
}
|
||||
|
||||
private String cleanValue(Tag tag, boolean allowPeriodsInName) {
|
||||
if (tag == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return cleanupIllegalCharacters(tag.getValue(), allowPeriodsInName);
|
||||
}
|
||||
|
||||
private String cleanupIllegalCharacters(String s, boolean allowPeriodsInName) {
|
||||
if (!allowPeriodsInName) {
|
||||
s = s.replace(".", "_");
|
||||
}
|
||||
return s.replace(" ", "_");
|
||||
}
|
||||
}
|
||||
+4
-39
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -17,19 +17,15 @@
|
||||
package org.springframework.cloud.netflix.servo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.boot.actuate.metrics.Metric;
|
||||
import org.springframework.boot.actuate.metrics.reader.MetricReader;
|
||||
import org.springframework.boot.actuate.metrics.writer.MetricWriter;
|
||||
|
||||
import com.netflix.servo.monitor.MonitorConfig;
|
||||
import com.netflix.servo.publish.BaseMetricObserver;
|
||||
import com.netflix.servo.publish.BasicMetricFilter;
|
||||
import com.netflix.servo.publish.MetricObserver;
|
||||
import com.netflix.servo.publish.MonitorRegistryMetricPoller;
|
||||
@@ -46,9 +42,9 @@ import com.netflix.servo.publish.PollScheduler;
|
||||
@CommonsLog
|
||||
public class ServoMetricCollector implements DisposableBean {
|
||||
|
||||
public ServoMetricCollector(MetricWriter metrics) {
|
||||
List<MetricObserver> observers = new ArrayList<MetricObserver>();
|
||||
observers.add(new ServoMetricObserver(metrics));
|
||||
public ServoMetricCollector(MetricWriter metrics, ServoMetricNaming naming) {
|
||||
List<MetricObserver> observers = new ArrayList<>();
|
||||
observers.add(new ServoMetricObserver(metrics, naming));
|
||||
PollRunnable task = new PollRunnable(new MonitorRegistryMetricPoller(),
|
||||
BasicMetricFilter.MATCH_ALL, true, observers);
|
||||
|
||||
@@ -80,35 +76,4 @@ public class ServoMetricCollector implements DisposableBean {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link MetricObserver} to convert Servo metrics into Spring Boot {@link Metric}
|
||||
* instances.
|
||||
*/
|
||||
private static final class ServoMetricObserver extends BaseMetricObserver {
|
||||
|
||||
private final MetricWriter metrics;
|
||||
|
||||
public ServoMetricObserver(MetricWriter metrics) {
|
||||
super("spring-boot");
|
||||
this.metrics = metrics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateImpl(List<com.netflix.servo.Metric> servoMetrics) {
|
||||
for (com.netflix.servo.Metric servoMetric : servoMetrics) {
|
||||
MonitorConfig config = servoMetric.getConfig();
|
||||
String type = config.getTags().getValue("type");
|
||||
String key = new StringBuilder(type).append(".servo.")
|
||||
.append(config.getName()).toString().toLowerCase();
|
||||
|
||||
if (servoMetric.hasNumberValue()) {
|
||||
this.metrics.set(new Metric<Number>(key,
|
||||
servoMetric.getNumberValue(), new Date(servoMetric
|
||||
.getTimestamp())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.servo;
|
||||
|
||||
import com.netflix.servo.Metric;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public interface ServoMetricNaming {
|
||||
String getName(Metric metric);
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.servo;
|
||||
|
||||
import com.netflix.servo.Metric;
|
||||
import com.netflix.servo.publish.BaseMetricObserver;
|
||||
import org.springframework.boot.actuate.metrics.writer.MetricWriter;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@link com.netflix.servo.publish.MetricObserver} to convert Servo metrics into Spring Boot {@link org.springframework.boot.actuate.metrics.Metric}
|
||||
* instances.
|
||||
*/
|
||||
final class ServoMetricObserver extends BaseMetricObserver {
|
||||
|
||||
private final MetricWriter metrics;
|
||||
private final ServoMetricNaming naming;
|
||||
|
||||
public ServoMetricObserver(MetricWriter metrics, ServoMetricNaming naming) {
|
||||
super("spring-boot");
|
||||
this.metrics = metrics;
|
||||
this.naming = naming;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateImpl(List<Metric> servoMetrics) {
|
||||
for (Metric servoMetric : servoMetrics) {
|
||||
String key = naming.getName(servoMetric);
|
||||
|
||||
if (servoMetric.hasNumberValue()) {
|
||||
this.metrics.set(new org.springframework.boot.actuate.metrics.Metric<>(key,
|
||||
servoMetric.getNumberValue(), new Date(servoMetric
|
||||
.getTimestamp())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+39
-4
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -18,7 +18,10 @@ package org.springframework.cloud.netflix.servo;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.MetricRepositoryAutoConfiguration;
|
||||
import org.springframework.boot.actuate.metrics.GaugeService;
|
||||
import org.springframework.boot.actuate.metrics.Metric;
|
||||
import org.springframework.boot.actuate.metrics.reader.MetricReader;
|
||||
import org.springframework.boot.actuate.metrics.writer.Delta;
|
||||
import org.springframework.boot.actuate.metrics.writer.MetricWriter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
@@ -38,15 +41,47 @@ import com.netflix.servo.monitor.Monitors;
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ Monitors.class, MetricReader.class })
|
||||
@ConditionalOnBean(MetricReader.class)
|
||||
@ConditionalOnBean(GaugeService.class)
|
||||
@AutoConfigureBefore(EndpointAutoConfiguration.class)
|
||||
@AutoConfigureAfter({ MetricRepositoryAutoConfiguration.class })
|
||||
public class ServoMetricsAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ServoMetricCollector servoMetricCollector(MetricWriter metrics) {
|
||||
return new ServoMetricCollector(metrics);
|
||||
public ServoMetricNaming servoMetricNaming() {
|
||||
return new DefaultServoMetricNaming();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ServoMetricCollector servoMetricCollector(GaugeService gauges,
|
||||
ServoMetricNaming naming) {
|
||||
return new ServoMetricCollector(new ActuatorMetricWriter(gauges), naming);
|
||||
}
|
||||
|
||||
// TODO: refactor this when Spring Boot 1.3 is mandatory
|
||||
private static class ActuatorMetricWriter implements MetricWriter {
|
||||
|
||||
private GaugeService gauges;
|
||||
|
||||
public ActuatorMetricWriter(GaugeService gauges) {
|
||||
this.gauges = gauges;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void increment(Delta<?> delta) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Metric<?> value) {
|
||||
gauges.submit(value.getName(), value.getValue().doubleValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(String metricName) {
|
||||
gauges.submit(metricName, 0.);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+15
-2
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -20,6 +20,8 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.web.ErrorController;
|
||||
import org.springframework.boot.context.embedded.ServletRegistrationBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent;
|
||||
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
|
||||
@@ -53,6 +55,9 @@ public class ZuulConfiguration {
|
||||
@Autowired
|
||||
private ZuulProperties zuulProperties;
|
||||
|
||||
@Autowired(required = false)
|
||||
private ErrorController errorController;
|
||||
|
||||
@Bean
|
||||
public RouteLocator routeLocator() {
|
||||
return new SimpleRouteLocator(this.zuulProperties);
|
||||
@@ -65,7 +70,9 @@ public class ZuulConfiguration {
|
||||
|
||||
@Bean
|
||||
public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) {
|
||||
return new ZuulHandlerMapping(routes, zuulController());
|
||||
ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController());
|
||||
mapping.setErrorController(this.errorController);
|
||||
return mapping;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -73,6 +80,12 @@ public class ZuulConfiguration {
|
||||
return new ZuulRefreshListener();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ServletRegistrationBean zuulServlet() {
|
||||
return new ServletRegistrationBean(new ZuulServlet(),
|
||||
this.zuulProperties.getServletPattern());
|
||||
}
|
||||
|
||||
// pre filters
|
||||
|
||||
@Bean
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+36
-8
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -29,11 +29,16 @@ import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.boot.actuate.trace.TraceRepository;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.util.UriUtils;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import com.netflix.zuul.util.HTTPRequestUtils;
|
||||
@@ -41,6 +46,7 @@ import com.netflix.zuul.util.HTTPRequestUtils;
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@CommonsLog
|
||||
public class ProxyRequestHelper {
|
||||
|
||||
/**
|
||||
@@ -57,6 +63,20 @@ public class ProxyRequestHelper {
|
||||
this.traces = traces;
|
||||
}
|
||||
|
||||
public String buildZuulRequestURI(HttpServletRequest request) {
|
||||
RequestContext context = RequestContext.getCurrentContext();
|
||||
String uri = request.getRequestURI();
|
||||
String contextURI = (String) context.get("requestURI");
|
||||
if (contextURI != null) {
|
||||
try {
|
||||
uri = UriUtils.encodePath(contextURI, WebUtils.DEFAULT_CHARACTER_ENCODING);
|
||||
} catch (Exception e) {
|
||||
log.debug("unable to encode uri path from context, falling back to uri from request", e);
|
||||
}
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
public MultiValueMap<String, String> buildZuulRequestQueryParams(
|
||||
HttpServletRequest request) {
|
||||
Map<String, List<String>> map = HTTPRequestUtils.getInstance().getQueryParams();
|
||||
@@ -75,14 +95,17 @@ public class ProxyRequestHelper {
|
||||
public MultiValueMap<String, String> buildZuulRequestHeaders(
|
||||
HttpServletRequest request) {
|
||||
RequestContext context = RequestContext.getCurrentContext();
|
||||
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
|
||||
Enumeration<?> headerNames = request.getHeaderNames();
|
||||
MultiValueMap<String, String> headers = new HttpHeaders();
|
||||
Enumeration<String> headerNames = request.getHeaderNames();
|
||||
if (headerNames != null) {
|
||||
while (headerNames.hasMoreElements()) {
|
||||
String name = (String) headerNames.nextElement();
|
||||
String value = request.getHeader(name);
|
||||
String name = headerNames.nextElement();
|
||||
if (isIncludedHeader(name)) {
|
||||
headers.set(name, value);
|
||||
Enumeration<String> values = request.getHeaders(name);
|
||||
while (values.hasMoreElements()) {
|
||||
String value = values.nextElement();
|
||||
headers.add(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,7 +113,7 @@ public class ProxyRequestHelper {
|
||||
for (String header : zuulRequestHeaders.keySet()) {
|
||||
headers.set(header, zuulRequestHeaders.get(header));
|
||||
}
|
||||
headers.set("accept-encoding", "deflate, gzip");
|
||||
headers.set(HttpHeaders.ACCEPT_ENCODING, "gzip");
|
||||
return headers;
|
||||
}
|
||||
|
||||
@@ -226,9 +249,14 @@ public class ProxyRequestHelper {
|
||||
|
||||
private void debugRequestEntity(Map<String, Object> info, InputStream inputStream)
|
||||
throws IOException {
|
||||
if (RequestContext.getCurrentContext().isChunkedRequestBody()) {
|
||||
info.put("body", "<chunked>");
|
||||
return;
|
||||
}
|
||||
String entity = IOUtils.toString(inputStream);
|
||||
if (StringUtils.hasText(entity)) {
|
||||
info.put("body", entity);
|
||||
info.put("body", entity.length() <= 4096 ? entity : entity.substring(0, 4096)
|
||||
+ "<truncated>");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-2
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -56,7 +56,13 @@ public class ProxyRouteLocator implements RouteLocator {
|
||||
|
||||
public ProxyRouteLocator(String servletPath, DiscoveryClient discovery,
|
||||
ZuulProperties properties) {
|
||||
this.servletPath = servletPath;
|
||||
if (StringUtils.hasText(servletPath)) { // a servletPath is passed explicitly
|
||||
this.servletPath = servletPath;
|
||||
} else {
|
||||
//set Zuul servlet path
|
||||
this.servletPath = properties.getServletPath() != null? properties.getServletPath() : "";
|
||||
}
|
||||
|
||||
this.discovery = discovery;
|
||||
this.properties = properties;
|
||||
}
|
||||
@@ -89,17 +95,22 @@ public class ProxyRouteLocator implements RouteLocator {
|
||||
}
|
||||
|
||||
public ProxyRouteSpec getMatchingRoute(String path) {
|
||||
log.info("Finding route for path: " + path);
|
||||
|
||||
String location = null;
|
||||
String targetPath = null;
|
||||
String id = null;
|
||||
String prefix = this.properties.getPrefix();
|
||||
log.debug("servletPath=" + this.servletPath);
|
||||
if (StringUtils.hasText(this.servletPath) && !this.servletPath.equals("/")
|
||||
&& path.startsWith(this.servletPath)) {
|
||||
path = path.substring(this.servletPath.length());
|
||||
}
|
||||
log.debug("path=" + path);
|
||||
Boolean retryable = this.properties.getRetryable();
|
||||
for (Entry<String, ZuulRoute> entry : this.routes.get().entrySet()) {
|
||||
String pattern = entry.getKey();
|
||||
log.debug("Matching pattern:" + pattern);
|
||||
if (this.pathMatcher.match(pattern, path)) {
|
||||
ZuulRoute route = entry.getValue();
|
||||
id = route.getId();
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+14
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -51,6 +51,8 @@ public class ZuulProperties {
|
||||
|
||||
private List<String> ignoredServices = new ArrayList<String>();
|
||||
|
||||
private String servletPath = "/zuul";
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
for (Entry<String, ZuulRoute> entry : this.routes.entrySet()) {
|
||||
@@ -132,4 +134,15 @@ public class ZuulProperties {
|
||||
|
||||
}
|
||||
|
||||
public String getServletPattern() {
|
||||
String path = this.servletPath;
|
||||
if (!path.startsWith("/")) {
|
||||
path = "/" + path;
|
||||
}
|
||||
if (!path.contains("*")) {
|
||||
path = path.endsWith("/") ? (path + "*") : (path + "/*");
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+17
-12
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -37,6 +37,7 @@ import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.MultipartRequest;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
@@ -76,17 +77,23 @@ public class FormBodyWrapperFilter extends ZuulFilter {
|
||||
if (contentType == null) {
|
||||
return false;
|
||||
}
|
||||
// Only use this filter for form data
|
||||
// Only use this filter for form data and only for multipart data in a
|
||||
// DispatcherServlet handler
|
||||
try {
|
||||
MediaType mediaType = MediaType.valueOf(contentType);
|
||||
return MediaType.APPLICATION_FORM_URLENCODED.includes(mediaType)
|
||||
|| MediaType.MULTIPART_FORM_DATA.includes(mediaType);
|
||||
|| (isDispatcherServletRequest(request) && MediaType.MULTIPART_FORM_DATA
|
||||
.includes(mediaType));
|
||||
}
|
||||
catch (InvalidMediaTypeException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDispatcherServletRequest(HttpServletRequest request) {
|
||||
return request.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object run() {
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
@@ -108,7 +115,7 @@ public class FormBodyWrapperFilter extends ZuulFilter {
|
||||
return null;
|
||||
}
|
||||
|
||||
private class FormBodyRequestWrapper extends HttpServletRequestWrapper {
|
||||
private class FormBodyRequestWrapper extends Servlet30RequestWrapper {
|
||||
|
||||
private HttpServletRequest request;
|
||||
|
||||
@@ -135,6 +142,9 @@ public class FormBodyWrapperFilter extends ZuulFilter {
|
||||
|
||||
@Override
|
||||
public int getContentLength() {
|
||||
if (super.getContentLength() <= 0) {
|
||||
return super.getContentLength();
|
||||
}
|
||||
if (this.contentData == null) {
|
||||
buildContentData();
|
||||
}
|
||||
@@ -143,15 +153,10 @@ public class FormBodyWrapperFilter extends ZuulFilter {
|
||||
|
||||
@Override
|
||||
public ServletInputStream getInputStream() throws IOException {
|
||||
if (RequestContext.getCurrentContext().isChunkedRequestBody()) {
|
||||
return this.request.getInputStream();
|
||||
}
|
||||
else {
|
||||
if (this.contentData == null) {
|
||||
buildContentData();
|
||||
}
|
||||
return new ServletInputStreamWrapper(this.contentData);
|
||||
if (this.contentData == null) {
|
||||
buildContentData();
|
||||
}
|
||||
return new ServletInputStreamWrapper(this.contentData);
|
||||
}
|
||||
|
||||
private synchronized void buildContentData() {
|
||||
|
||||
+8
-4
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -26,6 +26,7 @@ import lombok.extern.apachecommons.CommonsLog;
|
||||
import org.springframework.cloud.netflix.zuul.filters.ProxyRouteLocator;
|
||||
import org.springframework.cloud.netflix.zuul.filters.ProxyRouteLocator.ProxyRouteSpec;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
@@ -37,6 +38,8 @@ public class PreDecorationFilter extends ZuulFilter {
|
||||
|
||||
private boolean addProxyHeaders;
|
||||
|
||||
private UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
|
||||
public PreDecorationFilter(ProxyRouteLocator routeLocator, boolean addProxyHeaders) {
|
||||
this.routeLocator = routeLocator;
|
||||
this.addProxyHeaders = addProxyHeaders;
|
||||
@@ -60,18 +63,19 @@ public class PreDecorationFilter extends ZuulFilter {
|
||||
@Override
|
||||
public Object run() {
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
final String requestURI = ctx.getRequest().getRequestURI();
|
||||
final String requestURI = this.urlPathHelper.getPathWithinApplication(ctx
|
||||
.getRequest());
|
||||
ProxyRouteSpec route = this.routeLocator.getMatchingRoute(requestURI);
|
||||
if (route != null) {
|
||||
String location = route.getLocation();
|
||||
if (location != null) {
|
||||
ctx.put("requestURI", route.getPath());
|
||||
ctx.put("proxy", route.getId());
|
||||
|
||||
|
||||
if (route.getRetryable() != null) {
|
||||
ctx.put("retryable", route.getRetryable());
|
||||
}
|
||||
|
||||
|
||||
if (location.startsWith("http:") || location.startsWith("https:")) {
|
||||
ctx.setRouteHost(getUrl(location));
|
||||
ctx.addOriginResponseHeader("X-Zuul-Service", location);
|
||||
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.zuul.filters.pre;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.servlet.AsyncContext;
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.Part;
|
||||
|
||||
import com.netflix.zuul.http.HttpServletRequestWrapper;
|
||||
|
||||
class Servlet30RequestWrapper extends HttpServletRequestWrapper {
|
||||
private HttpServletRequest request;
|
||||
|
||||
Servlet30RequestWrapper(HttpServletRequest request) {
|
||||
super(request);
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean authenticate(HttpServletResponse response) throws IOException,
|
||||
ServletException {
|
||||
return this.request.authenticate(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void login(String username, String password) throws ServletException {
|
||||
this.request.login(username, password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logout() throws ServletException {
|
||||
this.request.logout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Part> getParts() throws IOException, IllegalStateException,
|
||||
ServletException {
|
||||
return this.request.getParts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Part getPart(String name) throws IOException, IllegalStateException,
|
||||
ServletException {
|
||||
return this.request.getPart(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletContext getServletContext() {
|
||||
return this.request.getServletContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncContext startAsync() {
|
||||
return this.request.startAsync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncContext startAsync(ServletRequest servletRequest,
|
||||
ServletResponse servletResponse) {
|
||||
return this.request.startAsync(servletRequest, servletResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAsyncStarted() {
|
||||
try {
|
||||
return this.request.isAsyncStarted();
|
||||
}
|
||||
catch (Throwable e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAsyncSupported() {
|
||||
try {
|
||||
return this.request.isAsyncSupported();
|
||||
}
|
||||
catch (Throwable e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncContext getAsyncContext() {
|
||||
return this.request.getAsyncContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DispatcherType getDispatcherType() {
|
||||
return this.request.getDispatcherType();
|
||||
}
|
||||
|
||||
}
|
||||
+1
-95
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -16,19 +16,9 @@
|
||||
|
||||
package org.springframework.cloud.netflix.zuul.filters.pre;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.servlet.AsyncContext;
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.Part;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
@@ -82,88 +72,4 @@ public class Servlet30WrapperFilter extends ZuulFilter {
|
||||
return null;
|
||||
}
|
||||
|
||||
private class Servlet30RequestWrapper extends HttpServletRequestWrapper {
|
||||
private HttpServletRequest request;
|
||||
|
||||
Servlet30RequestWrapper(HttpServletRequest request) {
|
||||
super(request);
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean authenticate(HttpServletResponse response) throws IOException,
|
||||
ServletException {
|
||||
return this.request.authenticate(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void login(String username, String password) throws ServletException {
|
||||
this.request.login(username, password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logout() throws ServletException {
|
||||
this.request.logout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Part> getParts() throws IOException, IllegalStateException,
|
||||
ServletException {
|
||||
return this.request.getParts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Part getPart(String name) throws IOException, IllegalStateException,
|
||||
ServletException {
|
||||
return this.request.getPart(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletContext getServletContext() {
|
||||
return this.request.getServletContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncContext startAsync() {
|
||||
return this.request.startAsync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncContext startAsync(ServletRequest servletRequest,
|
||||
ServletResponse servletResponse) {
|
||||
return this.request.startAsync(servletRequest, servletResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAsyncStarted() {
|
||||
try {
|
||||
return this.request.isAsyncStarted();
|
||||
}
|
||||
catch (Throwable e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAsyncSupported() {
|
||||
try {
|
||||
return this.request.isAsyncSupported();
|
||||
}
|
||||
catch (Throwable e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncContext getAsyncContext() {
|
||||
return this.request.getAsyncContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DispatcherType getDispatcherType() {
|
||||
return this.request.getDispatcherType();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+20
-8
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -21,7 +21,9 @@ import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
import com.netflix.client.http.HttpRequest.Builder;
|
||||
@@ -45,6 +47,7 @@ import com.netflix.zuul.context.RequestContext;
|
||||
* https://github.com/Netflix/zuul/blob/master/zuul-netflix/src/main/java/com/
|
||||
* netflix/zuul/dependency/ribbon/hystrix/RibbonCommand.java
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class RibbonCommand extends HystrixCommand<HttpResponse> {
|
||||
|
||||
private RestClient restClient;
|
||||
@@ -55,24 +58,24 @@ public class RibbonCommand extends HystrixCommand<HttpResponse> {
|
||||
|
||||
private Boolean retryable;
|
||||
|
||||
private MultivaluedMap<String, String> headers;
|
||||
private MultiValueMap<String, String> headers;
|
||||
|
||||
private MultivaluedMap<String, String> params;
|
||||
private MultiValueMap<String, String> params;
|
||||
|
||||
private InputStream requestEntity;
|
||||
|
||||
public RibbonCommand(RestClient restClient, Verb verb, String uri,
|
||||
Boolean retryable,
|
||||
MultivaluedMap<String, String> headers,
|
||||
MultivaluedMap<String, String> params, InputStream requestEntity)
|
||||
MultiValueMap<String, String> headers,
|
||||
MultiValueMap<String, String> params, InputStream requestEntity)
|
||||
throws URISyntaxException {
|
||||
this("default", restClient, verb, uri, retryable , headers, params, requestEntity);
|
||||
}
|
||||
|
||||
public RibbonCommand(String commandKey, RestClient restClient, Verb verb, String uri,
|
||||
Boolean retryable,
|
||||
MultivaluedMap<String, String> headers,
|
||||
MultivaluedMap<String, String> params, InputStream requestEntity)
|
||||
MultiValueMap<String, String> headers,
|
||||
MultiValueMap<String, String> params, InputStream requestEntity)
|
||||
throws URISyntaxException {
|
||||
super(getSetter(commandKey));
|
||||
this.restClient = restClient;
|
||||
@@ -128,6 +131,15 @@ public class RibbonCommand extends HystrixCommand<HttpResponse> {
|
||||
HttpResponse response = this.restClient
|
||||
.executeWithLoadBalancer(httpClientRequest);
|
||||
context.set("ribbonResponse", response);
|
||||
|
||||
// Explicitly close the HttpResponse if the Hystrix command timed out to
|
||||
// release the underlying HTTP connection held by the response.
|
||||
//
|
||||
if( this.isResponseTimedOut() ) {
|
||||
if( response!= null ) {
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user