Compare commits

...
174 Commits
Author SHA1 Message Date
Spring Operator 360dad3669 URL Cleanup (#3474)
This commit updates URLs to prefer the https protocol. Redirects are not followed to avoid accidentally expanding intentionally shortened URLs (i.e. if using a URL shortener).

# HTTP URLs that Could Not Be Fixed
These URLs were unable to be fixed. Please review them to see if they can be manually resolved.

* [ ] http://conor.cc (200) with 4 occurrences could not be migrated:  
   ([https](https://conor.cc) result SSLProtocolException).
* [ ] http://richclarkdesign.com (200) with 1 occurrences could not be migrated:  
   ([https](https://richclarkdesign.com) result SSLHandshakeException).
* [ ] http://tinysort.sjeiti.com/ (200) with 1 occurrences could not be migrated:  
   ([https](https://tinysort.sjeiti.com/) result SSLException).
* [ ] http://www.sjeiti.com/ (301) with 1 occurrences could not be migrated:  
   ([https](https://www.sjeiti.com/) result SSLException).
* [ ] http://foo.com (301) with 1 occurrences could not be migrated:  
   ([https](https://foo.com) result SSLHandshakeException).

# Fixed URLs

## Fixed But Review Recommended
These URLs were fixed, but the https status was not OK. However, the https status was the same as the http request or http redirected to an https URL, so they were migrated. Your review is recommended.

* [ ] http://ejohn.org/ (301) with 1 occurrences migrated to:  
  https://johnresig.com/ ([https](https://ejohn.org/) result ClosedChannelException).
* [ ] http://ejohn.org/blog/javascript-micro-templating/ (301) with 1 occurrences migrated to:  
  https://johnresig.com/blog/javascript-micro-templating/ ([https](https://ejohn.org/blog/javascript-micro-templating/) result ClosedChannelException).
* [ ] http://foo.com:8000/ (ConnectTimeoutException) with 1 occurrences migrated to:  
  https://foo.com:8000/ ([https](https://foo.com:8000/) result ConnectTimeoutException).
* [ ] http://www.w3.org/TR/html4/loose.dtd (ReadTimeoutException) with 1 occurrences migrated to:  
  https://www.w3.org/TR/html4/loose.dtd ([https](https://www.w3.org/TR/html4/loose.dtd) result ReadTimeoutException).
* [ ] http://earlymade.com (UnknownHostException) with 4 occurrences migrated to:  
  https://earlymade.com ([https](https://earlymade.com) result UnknownHostException).
* [ ] http://hostname:port/turbine/turbine.stream (UnknownHostException) with 1 occurrences migrated to:  
  https://hostname:port/turbine/turbine.stream ([https](https://hostname:port/turbine/turbine.stream) result UnknownHostException).
* [ ] http://hystrix-app:port/hystrix.stream (UnknownHostException) with 1 occurrences migrated to:  
  https://hystrix-app:port/hystrix.stream ([https](https://hystrix-app:port/hystrix.stream) result UnknownHostException).
* [ ] http://my.turbine.sever:8080/turbine.stream?cluster= (UnknownHostException) with 1 occurrences migrated to:  
  https://my.turbine.sever:8080/turbine.stream?cluster= ([https](https://my.turbine.sever:8080/turbine.stream?cluster=) result UnknownHostException).
* [ ] http://sidecar.local.spring.io:5678/configserver/default-master.yml (UnknownHostException) with 1 occurrences migrated to:  
  https://sidecar.local.spring.io:5678/configserver/default-master.yml ([https](https://sidecar.local.spring.io:5678/configserver/default-master.yml) result UnknownHostException).
* [ ] http://turbine-hostname:port/turbine.stream (UnknownHostException) with 1 occurrences migrated to:  
  https://turbine-hostname:port/turbine.stream ([https](https://turbine-hostname:port/turbine.stream) result UnknownHostException).
* [ ] http://turbine-hostname:port/turbine.stream?cluster= (UnknownHostException) with 1 occurrences migrated to:  
  https://turbine-hostname:port/turbine.stream?cluster= ([https](https://turbine-hostname:port/turbine.stream?cluster=) result UnknownHostException).
* [ ] http://example.com/users_service (404) with 1 occurrences migrated to:  
  https://example.com/users_service ([https](https://example.com/users_service) result 404).

## Fixed Success 
These URLs were switched to an https URL with a 2xx status. While the status was successful, your review is still recommended.

* [ ] http://asciidoctor.org with 1 occurrences migrated to:  
  https://asciidoctor.org ([https](https://asciidoctor.org) result 200).
* [ ] http://example.com with 9 occurrences migrated to:  
  https://example.com ([https](https://example.com) result 200).
* [ ] http://example.com/ with 2 occurrences migrated to:  
  https://example.com/ ([https](https://example.com/) result 200).
* [ ] http://github.com/ with 1 occurrences migrated to:  
  https://github.com/ ([https](https://github.com/) result 200).
* [ ] http://jquery-howto.blogspot.com/2009/09/get-url-parameters-values-with-jquery.html with 1 occurrences migrated to:  
  https://jquery-howto.blogspot.com/2009/09/get-url-parameters-values-with-jquery.html ([https](https://jquery-howto.blogspot.com/2009/09/get-url-parameters-values-with-jquery.html) result 200).
* [ ] http://martinfowler.com/bliki/CircuitBreaker.html with 1 occurrences migrated to:  
  https://martinfowler.com/bliki/CircuitBreaker.html ([https](https://martinfowler.com/bliki/CircuitBreaker.html) result 200).
* [ ] http://maven.apache.org/run-maven/index.html with 1 occurrences migrated to:  
  https://maven.apache.org/run-maven/index.html ([https](https://maven.apache.org/run-maven/index.html) result 200).
* [ ] http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html with 1 occurrences migrated to:  
  https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html ([https](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) result 200).
* [ ] http://www.fig.sh/ with 1 occurrences migrated to:  
  https://www.fig.sh/ ([https](https://www.fig.sh/) result 200).
* [ ] http://www.gnu.org/licenses/gpl.html with 1 occurrences migrated to:  
  https://www.gnu.org/licenses/gpl.html ([https](https://www.gnu.org/licenses/gpl.html) result 200).
* [ ] http://www.slideshare.net/MikeyCohen1/edge-architecture-ieee-international-conference-on-cloud-engineering-32240146/27 with 1 occurrences migrated to:  
  https://www.slideshare.net/MikeyCohen1/edge-architecture-ieee-international-conference-on-cloud-engineering-32240146/27 ([https](https://www.slideshare.net/MikeyCohen1/edge-architecture-ieee-international-conference-on-cloud-engineering-32240146/27) result 200).
* [ ] http://commons.apache.org/proper/commons-configuration with 1 occurrences migrated to:  
  https://commons.apache.org/proper/commons-configuration ([https](https://commons.apache.org/proper/commons-configuration) result 301).
* [ ] http://creativecommons.org/licenses/MIT/ with 4 occurrences migrated to:  
  https://creativecommons.org/licenses/MIT/ ([https](https://creativecommons.org/licenses/MIT/) result 301).
* [ ] http://plugins.jetbrains.com/plugin/6546 with 2 occurrences migrated to:  
  https://plugins.jetbrains.com/plugin/6546 ([https](https://plugins.jetbrains.com/plugin/6546) result 301).
* [ ] http://projectlombok.org/features/index.html with 1 occurrences migrated to:  
  https://projectlombok.org/features/index.html ([https](https://projectlombok.org/features/index.html) result 301).
* [ ] http://raw.github.com/ with 1 occurrences migrated to:  
  https://raw.github.com/ ([https](https://raw.github.com/) result 301).
* [ ] http://raw.github.com/wiki/Netflix/Hystrix/images/hystrix-logo-tagline-tiny.png with 2 occurrences migrated to:  
  https://raw.github.com/wiki/Netflix/Hystrix/images/hystrix-logo-tagline-tiny.png ([https](https://raw.github.com/wiki/Netflix/Hystrix/images/hystrix-logo-tagline-tiny.png) result 301).
* [ ] http://www.opensource.org/licenses/mit-license.php with 1 occurrences migrated to:  
  https://www.opensource.org/licenses/mit-license.php ([https](https://www.opensource.org/licenses/mit-license.php) result 301).
* [ ] http://eclipse.org with 1 occurrences migrated to:  
  https://eclipse.org ([https](https://eclipse.org) result 302).
* [ ] http://eclipse.org/m2e/ with 2 occurrences migrated to:  
  https://eclipse.org/m2e/ ([https](https://eclipse.org/m2e/) result 302).
* [ ] http://simplegrid.info with 4 occurrences migrated to:  
  https://simplegrid.info ([https](https://simplegrid.info) result 302).
* [ ] http://simplegrid.info/ with 1 occurrences migrated to:  
  https://simplegrid.info/ ([https](https://simplegrid.info/) result 302).
* [ ] http://www.springsource.com/developer/sts with 1 occurrences migrated to:  
  https://www.springsource.com/developer/sts ([https](https://www.springsource.com/developer/sts) result 302).

# Ignored
These URLs were intentionally ignored.

* http://%s:%s with 1 occurrences
* http://%s:%s/%s with 1 occurrences
* http://foo/ with 1 occurrences
* http://foo:7001/ with 3 occurrences
* http://foo:7001/bar with 1 occurrences
* http://localhost with 64 occurrences
* http://localhost:5678/configserver with 1 occurrences
* http://localhost:5678/customers with 1 occurrences
* http://localhost:5678/hosts/ with 1 occurrences
* http://localhost:6080/ with 1 occurrences
* http://localhost:7777/local with 2 occurrences
* http://localhost:8000/health.json with 1 occurrences
* http://localhost:8000/src/test/resources/health.json with 1 occurrences
* http://localhost:8080/eureka/jsp/status.jsp with 1 occurrences
* http://localhost:8081 with 1 occurrences
* http://localhost:8761 with 1 occurrences
* http://localhost:8761/eureka/ with 5 occurrences
* http://localhost:9080/ with 1 occurrences
* http://localhost:9876 with 2 occurrences
* http://localhost:9999 with 2 occurrences
* http://myhost2:9000 with 1 occurrences
* http://myhost:8080 with 1 occurrences
* http://myhost:8989 with 1 occurrences
* http://myhost:9000 with 1 occurrences
* http://myservice with 1 occurrences
* http://peer1/eureka/ with 1 occurrences
* http://peer2/eureka/ with 1 occurrences
* http://simple/ with 1 occurrences
* http://simple/emptypost with 1 occurrences
* http://simple/header with 1 occurrences
* http://simple/path/ with 2 occurrences
* http://simple/post with 1 occurrences
* http://simple/request?param= with 1 occurrences
* http://user:password@localhost:8761/eureka/ with 1 occurrences
2019-03-27 13:24:46 -04:00
Spring Operator 2c9c59798b URL Cleanup (#3465)
This commit updates URLs to prefer the https protocol. Redirects are not followed to avoid accidentally expanding intentionally shortened URLs (i.e. if using a URL shortener).

# Fixed URLs

## Fixed Success
These URLs were switched to an https URL with a 2xx status. While the status was successful, your review is still recommended.

* [ ] http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd with 2 occurrences migrated to:
  https://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd ([https](https://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd) result 200).
* [ ] http://repo.spring.io/libs-milestone-local with 2 occurrences migrated to:
  https://repo.spring.io/libs-milestone-local ([https](https://repo.spring.io/libs-milestone-local) result 302).
* [ ] http://repo.spring.io/libs-snapshot-local with 2 occurrences migrated to:
  https://repo.spring.io/libs-snapshot-local ([https](https://repo.spring.io/libs-snapshot-local) result 302).
* [ ] http://repo.spring.io/release with 1 occurrences migrated to:
  https://repo.spring.io/release ([https](https://repo.spring.io/release) result 302).

# Ignored
These URLs were intentionally ignored.

* http://maven.apache.org/POM/4.0.0 with 20 occurrences
* http://www.isdc.ro/wro with 1 occurrences
* http://www.w3.org/2000/svg with 2 occurrences
* http://www.w3.org/2001/XMLSchema-instance with 10 occurrences
2019-03-26 11:51:53 -04:00
Spring Operator ec2cab236c URL Cleanup (#3443)
This commit updates URLs to prefer the https protocol. Redirects are not followed to avoid accidentally expanding intentionally shortened URLs (i.e. if using a URL shortener).

# Fixed URLs

## Fixed Success
These URLs were switched to an https URL with a 2xx status. While the status was successful, your review is still recommended.

* [ ] http://www.apache.org/licenses/LICENSE-2.0 with 190 occurrences migrated to:
  https://www.apache.org/licenses/LICENSE-2.0 ([https](https://www.apache.org/licenses/LICENSE-2.0) result 200).
2019-03-21 15:16:30 -04:00
Spring Operator 9e9cfdaedb URL Cleanup (#3422)
This commit updates URLs to prefer the https protocol. Redirects are not followed to avoid accidentally expanding intentionally shortened URLs (i.e. if using a URL shortener).

# Fixed URLs

## Fixed Success 
These URLs were switched to an https URL with a 2xx status. While the status was successful, your review is still recommended.

* http://maven.apache.org/xsd/maven-4.0.0.xsd with 10 occurrences migrated to:  
  https://maven.apache.org/xsd/maven-4.0.0.xsd ([https](https://maven.apache.org/xsd/maven-4.0.0.xsd) result 200).
* http://projects.spring.io/spring-cloud/ with 4 occurrences migrated to:  
  https://projects.spring.io/spring-cloud/ ([https](https://projects.spring.io/spring-cloud/) result 200).
* http://stackoverflow.com/questions/1593051/how-to-programmatically-determine-the-current-checked-out-git-branch with 1 occurrences migrated to:  
  https://stackoverflow.com/questions/1593051/how-to-programmatically-determine-the-current-checked-out-git-branch ([https](https://stackoverflow.com/questions/1593051/how-to-programmatically-determine-the-current-checked-out-git-branch) result 200).
* http://stackoverflow.com/questions/29300806/a-bash-script-to-check-if-a-string-is-present-in-a-comma-separated-list-of-strin with 1 occurrences migrated to:  
  https://stackoverflow.com/questions/29300806/a-bash-script-to-check-if-a-string-is-present-in-a-comma-separated-list-of-strin ([https](https://stackoverflow.com/questions/29300806/a-bash-script-to-check-if-a-string-is-present-in-a-comma-separated-list-of-strin) result 200).
* http://www.apache.org/licenses/LICENSE-2.0 with 2 occurrences migrated to:  
  https://www.apache.org/licenses/LICENSE-2.0 ([https](https://www.apache.org/licenses/LICENSE-2.0) result 200).

# Ignored
These URLs were intentionally ignored.

* http://maven.apache.org/POM/4.0.0 with 20 occurrences
* http://www.w3.org/2001/XMLSchema-instance with 10 occurrences
2019-03-16 11:57:18 -04:00
Marcin Grzejszczak e5e4701680 Updating mvnw for all projects 2016-09-19 19:31:16 +02:00
Marcin Grzejszczak 81a34cbcc4 Updating mvnw for all projects 2016-09-19 14:18:17 +02:00
Marcin Grzejszczak 9baf55b2a6 Updating mvnw for all projects 2016-09-19 12:46:35 +02:00
Marcin Grzejszczak 3e770881b5 Updating mvnw for all projects 2016-09-15 16:12:38 +02:00
Marcin Grzejszczak 81d2f2c3b5 Removed adoc footer 2016-09-07 18:02:52 +02:00
Marcin Grzejszczak 337ab429e6 Updating mvnw for all projects 2016-09-07 17:28:44 +02:00
Marcin Grzejszczak cc724c77fa Updating ghpages for all projects 2016-08-22 13:32:06 +02:00
Marcin Grzejszczak a54bb6b059 Updating ghpages for all projects 2016-08-19 11:46:14 +02:00
Marcin Grzejszczak 0e23a67149 Updating ghpages for all projects 2016-08-18 13:15:01 +02:00
Marcin Grzejszczak 3190b668a3 Updating ghpages for all projects 2016-08-17 12:45:01 +02:00
Marcin Grzejszczak 020f05ebfe Deploying documentation to proper folder
What we're missing ATM is different documentation versions for different application versions. What this change does is that it's:

- finding out what is the current branch (e.g. 1.0.x)
- finding out out what is the name of the main adoc file (e.g. spring-cloud-sleuth)
- pulling the changes from gh-pages after checkout
- finding out what is the list of comma separated whitelisted branches (via the `docs.whitelisted.branches` prop)
- in gh-pages creating a folder with name of the branch  (e.g. /1.0.x)
copying all the docs/target/generated-docs/ to that folder
- if the branch from which we're calling the script is NOT master then we're changing the ${main.adoc}.html to index.html so that it's easier to access the docs (e.g. http://cloud.spring.io/spring-cloud-sleuth/1.0.x/)
2016-08-16 14:57:52 +02:00
Spencer Gibb 86b4c5fb16 added maven wrapper 2016-06-30 16:46:43 -06:00
Spencer Gibb afe8f4d0ef Fix so brixton eureka clients can use angel eureka servers.
see #978
2016-05-13 15:43:45 -06:00
Dave Syer b59bc5fb05 Fix github tag 2016-02-25 11:31:36 +00:00
Dave Syer 4a049fb8fc Revert to snapshots 2016-01-27 18:11:49 +00:00
Dave Syer 8b5f6b3ccb Update to 1.0.7 2016-01-27 18:01:48 +00:00
Dave Syer e0b0f74316 Fix versions of starters in BOM
In Angel the starters are defined in a different project, not
this one, so the version can be different (as it is at the moment)
2016-01-27 17:37:47 +00:00
Dave Syer 7976719a1d Revert to snapshots 2016-01-25 16:04:23 +00:00
Dave Syer 1b2d498754 Update to 1.0.6 2016-01-25 16:01:55 +00:00
Dave Syer 5743dec1f6 Extract all dependencies to BOMs and upgrade Boot 2016-01-25 15:20:17 +00:00
Dave Syer b618c3a5f4 Add extra spring-cloud-*-dependencies BOMs 2016-01-25 14:12:47 +00:00
Spencer Gibb 3f0814fd0d Bumped to 1.0.6.BUILD-SNAPSHOT and SNAPSHOT dependencies 2015-12-10 14:43:47 -07:00
Spencer Gibb ae3da1099d Update SNAPSHOT to RELEASE 1.0.5.RELEASE 2015-12-10 14:15:08 -07:00
Spencer Gibb 5010ad1156 Force isCircuitBreakerOpen to String (if it's a Boolean for example)
see https://github.com/Netflix/Hystrix/pull/927
2015-12-09 12:38:57 -07:00
Konstantin A. Zolotukhin 4136e397de null check for command bound circuit breaker 2015-11-20 15:50:28 -07:00
Spencer Gibb bf1e0d6d44 Bumped to 1.0.5.BUILD-SNAPSHOT 2015-11-16 10:13:36 -07:00
Dave Syer bc807aae9e Use better icon image for magnifying glass
Fixes gh-628
2015-11-09 14:25:33 +00:00
Spencer Gibb 727d8be2dc Change from @Autowired to context.getBean.
Solves problems where FeignClientFactoryBean is run before auto-configuration.

fixed gh-441
2015-10-14 11:52:11 -06:00
Dave Syer f98a7d5bc5 Take into account secure port settings when looking for config server
When the config server is located via discovery
(spring.cloud.config.discovery.enabled=true) the discovery process
should attempt to establish whether the config server prefers to
be accessed securely (e.g. it registered with Eureka as
nonSecurePortEnabled=false). This change ensures that this happens.

Fixes gh-450
2015-09-25 13:09:33 +01:00
Dave Syer 5dccd03d73 Fix broken test
This test made wrong assumptions about the content of the child
context when trying to simulate autoconfiguration. It needed
an extra EurekaClientAutoConfiguration.
2015-09-25 13:04:52 +01:00
Dave Syer f904b52a1d Tweak eureka lifecycle so it re-registers after a refresh
This version of Eureka is a bit of a singleton pig, so it's hard
to control the lifecycle, but this seems to work for all the tests
and also the eureka-first and ribbon-eureka samples.

Fixes gh-551 for 1.0.x
2015-09-24 17:07:22 +01:00
Spencer Gibb ac86e7798e Fix Feign ResponseEntity<Void> NPE
fixes gh-539
2015-09-14 12:38:43 -06:00
Spencer Gibb 13a3ea071d Only manage turbine-core dep in modules
fixes gh-511
2015-09-02 10:49:32 -06:00
Dave Syer 43a5241d3c Fix autoconfig ordering so that health indicators always work 2015-09-02 13:29:41 +01:00
Dave Syer 7fc9811975 Use TRAVIS_BRANCH if available 2015-08-25 14:52:31 +01:00
Dave Syer b3ca7430b0 Move ghpages to script 2015-08-25 14:15:51 +01:00
Dave Syer 00cc64c6c5 Different special treatment of README 2015-08-25 14:04:43 +01:00
Dave Syer c905822005 Special treatnment of README 2015-08-25 12:56:15 +01:00
Dave Syer 56fae9330f Try to debug CI failures 2015-08-25 12:44:08 +01:00
Dave Syer c1cb0025c1 Use subdirectory for gh pages docs 2015-08-25 12:37:15 +01:00
Dave Syer 89ebf1fa3f Fix ghpages script to work on branches 2015-08-25 12:29:41 +01:00
Dave Syer ad86d48c6a Use HttpHeaders for headers (case insensitive)
Fixes gh-306
2015-08-25 11:13:29 +01:00
Biju Kunjummen 2c688062e7 Clarified the way to add a custom EurekaInstanceConfigBean for AWS deployment 2015-07-18 10:36:51 +01:00
Ryan J Baxter f4a69e43eb Sets the zone based on the hostname of the service if approximateZoneFromHostname is true.
The flag has always been there, it just isn't carried through to the ribbon client configuration
without this change if I understand this change correctly.

Fixes gh-315, fixes gh-423.
2015-07-10 11:47:35 +01:00
Dave Syer 3519ecf224 Prepare master for next snapshot 2015-06-26 10:57:19 +01:00
Dave Syer 72cecacea7 Fix path to docs in README raw text 2015-06-25 12:31:29 +01:00
Dave Syer e17766e916 Add dependency management for starters 2015-06-25 11:46:01 +01:00
Dave Syer 29841f9f57 Revert "add optional spring-security-rsa"
This reverts commit 06d4d1e158.
2015-06-24 20:24:50 +01:00
Spencer Gibb 06d4d1e158 add optional spring-security-rsa 2015-06-24 11:42:46 -06:00
Dave Syer a903b5d58c Fix guava imports 2015-06-24 17:31:06 +01:00
Dave Syer 25651f4835 Add sidecar to BOM 2015-06-24 17:23:39 +01:00
Spencer Gibb bab74edf89 Merge pull request #340 from kaczynskid/master
Corrected example of providing Turbine cluster name via instance metadata map
2015-06-23 15:06:45 -04:00
Dave Syer 652e4b96b3 Nail explicit dependencies for s-c-context 2015-06-23 17:58:59 +01:00
Spencer Gibb 34eb36323a Merge pull request #390 from heyjustin/master
Mark an instance as DOWN when the application context is stopped.
2015-06-23 12:00:30 -04:00
Spencer Gibb 12bee7772f Merge pull request #408 from brandon-atkinson/zuul-unescaped-paths
* zuul-unescaped-paths:
  Fix unescaped paths in SimpleHostRoutingFilter.
2015-06-23 09:57:26 -06:00
Brandon Atkinson 283c0f7600 Fix unescaped paths in SimpleHostRoutingFilter.
Move common code so RibbonRoutingFilter can use the same code.

Fixes gh-407
2015-06-23 09:56:46 -06:00
Spencer Gibb 0b925e5afa Merge pull request #409 from brandon-atkinson/issue-394-remove-deflate
* issue-394-remove-deflate:
  Removed deflate accept-encoding from outbound requests
2015-06-23 09:51:01 -06:00
Brandon Atkinson 80206e6b38 Removed deflate accept-encoding from outbound requests 2015-06-23 09:50:14 -06:00
Bertrand Renuart 58284a4508 Change Jersey init to avoid classpath scanning issues
Scan classpath for Jersey resources with Spring and initialize the
Jersey filter with a jaxrs Application instead. (Fixes gh-399,
fixes gh-400)
2015-06-23 13:00:38 +01:00
Bertrand Renuart 2ba5c4b9ec gh327: explicitly close the HttpResponse in case of timeout 2015-06-11 13:14:50 -06:00
Justin Walsh 69eaa27ccc Mark an instance as DOWN when the application context is stopped.
Fixes gh-377
2015-06-10 10:37:53 +02:00
Spencer Gibb 440779bcf0 Revert "attempt ordering of RibbonClients.defaultConfiguration's"
This reverts commit 0d468b3930.
2015-06-08 14:56:23 -06:00
Spencer Gibb 0d468b3930 attempt ordering of RibbonClients.defaultConfiguration's
ref gh-383
2015-06-08 14:55:22 -06:00
Spencer Gibb 0f2fbf8e1c fixed test broken during rebase 2015-06-08 14:02:17 -06:00
Spencer Gibb b746ad5504 Fix name of default ribbon config if config is not in an eclosing class
fixes gh-374
2015-06-08 13:44:07 -06:00
Dave Syer a63b825690 Add call to Lifecycle.stop()
See gh-377
2015-06-08 06:33:45 +01:00
Spencer Gibb ce96c83401 updated README from sources 2015-06-05 11:16:33 -06:00
Spencer Gibb aa002b56c4 fixed canceled leases template var referencec
fixes gh-375
2015-06-05 09:00:54 -06:00
Spencer Gibb fa84108ebf fix swapped default values
fixes gh-372
2015-06-02 13:21:29 -06:00
Spencer Gibb a23131f094 upgrade hystrix to 1.4.10
fixes gh-370
2015-06-02 08:33:33 -06:00
Spencer Gibb a0d72b3cbc removed test with encoded slashes 2015-06-02 08:33:32 -06:00
Dave Syer 23cde30063 Align metrics with Spring Boot 1.3
Should be no change for users of Boot 1.2 right now, but with this
patch it is possible to use 1.3 with the new conventions for metrics
(e.g. in particular exporting by declaring beans of type
MetricWriter)
2015-06-02 15:29:08 +01:00
Spencer Gibb 899a577980 add test for zuul with encoded path parameter 2015-06-01 14:44:54 -06:00
Spencer Gibb 682ceec402 Merge branch 'brenuart-gh354b' 2015-05-28 09:02:07 -06:00
Spencer Gibb 68c731d668 added test for metrics with period in name 2015-05-28 09:01:53 -06:00
Spencer Gibb 1a6602149d Merge branch 'gh354b' of https://github.com/brenuart/spring-cloud-netflix into brenuart-gh354b 2015-05-28 08:43:23 -06:00
Spencer Gibb dbef27ac6b more RibbonClient tests 2015-05-28 08:32:01 -06:00
Bertrand Renuart 28f890989d Allow for periods in metric name (see gh354) 2015-05-28 09:48:28 +02:00
Dave Syer bd808a76fa Add health.config for documentation purposes only 2015-05-27 10:27:02 +01:00
Spencer Gibb 2559b76747 fixed typo 2015-05-26 10:56:57 -06:00
Spencer Gibb 4255179354 use io.reactivex:rxjava
fixes gh-353
2015-05-20 12:35:49 -06:00
Spencer Gibb bd5d00c082 encode uri if needed
fixes gh-347
2015-05-18 21:51:58 -06:00
Spencer Gibb 8bb578ad97 update ServerMetricCollector to include instance and statistic tags if present.
fixes gh-348
2015-05-18 20:53:55 -06:00
Spencer Gibb 3bb1562b7f fix RibbonClientHttpRequestFactory to add headers and accept a body
fixes gh-350
2015-05-15 12:26:32 -06:00
Spencer Gibb 68b0c91fa7 document disabling of zuul filters
fixes gh-341
2015-05-11 10:08:06 -06:00
Darek Kaczyński 8fa9b71579 Corrected example of providing Turbine cluster name via instance metadata map. 2015-05-07 00:02:03 +02:00
Spencer Gibb 532cfd9bda Merge pull request #336 from marcingrzejszczak/patch-2
Fixed a typo in the doc
2015-05-06 15:20:16 -04:00
Marcin Grzejszczak 94081f82ce Fixed a typo 2015-05-05 23:49:49 +02:00
Spencer Gibb ed692c3836 document that build requires jdk8
fixes gh-328
2015-05-05 11:42:09 -06:00
Spencer Gibb e3b42e79b6 fix misspelling 2015-05-05 10:54:59 -06:00
Spencer Gibb 688b577d63 Merge pull request #334 from marcingrzejszczak/patch-1
Reason why Eureka instance is not found, is not visible
2015-05-05 11:43:32 -04:00
Spencer Gibb 85b7ccf618 Merge pull request #316 from brenuart/issue-312
Make creation of the RestClient LAZY as it is no always required (issue #312)
2015-05-04 16:26:17 -04:00
Marcin Grzejszczak eed9b996fb Reason why Eureka instance is not found is not visible
With the following configuration at `bootstrap.yaml`

```yaml

spring:
  application.name: spring-cloud-hystrix-dashboard
  profiles.active: prod
  cloud:
    config:
      discovery:
        enabled: true
        serviceId: spring-cloud-server-config-with-eureka
server.port: 9003

turbine:
  appConfig: some-service
  clusterNameExpression: 'default'
```

I wasn't able to see why Turbine can't find the instance. The reason was related to SPEL:

```
org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 0): Property or field 'default' cannot be found on object of type 'com.netflix.appinfo.InstanceInfo' - maybe not public?
```

Anyways there was nothing there in the logs so maybe it should not be silenced like right now?
2015-05-04 16:55:11 +02:00
Dave Syer 3809bcbc90 More 1.0.2 snapshots 2015-05-01 16:24:20 +01:00
Dave Syer 0587df4ae2 Fix parent version 2015-05-01 16:19:51 +01:00
Dave Syer 53c0e0a246 Fix parent version 2015-05-01 16:19:21 +01:00
Dave Syer bad44ec8ab Fix parent version 2015-05-01 16:15:48 +01:00
Dave Syer fde4741333 Update to 1.0.3 snapshots 2015-05-01 11:22:13 +02:00
Dave Syer c77322348c Add example for SEMAPHORE in @HystrixCommand 2015-05-01 11:20:21 +02:00
Spencer Gibb c3f5678854 only invoke RestClient.initWithNiwsConfig once
fixes gh-324
2015-04-30 12:56:38 -06:00
Spencer Gibb 5166994f7c add documentation for ribbon.eureka.enabled
fixes gh-325
2015-04-30 09:42:28 -06:00
Spencer Gibb ce6d286d70 Don't execute load balancer twice
fixes gh-323
2015-04-29 22:20:10 -06:00
Bertrand Renuart df8c637a57 add short explanation in javadoc 2015-04-28 11:26:50 +02:00
Bertrand Renuart e2fa45f960 Make creation of the RestClient LAZY as it is no always required (see
https://github.com/spring-cloud/spring-cloud-netflix/issues/312)
2015-04-24 09:32:26 +02:00
Dave Syer b28fd07cdb Update to 1.0.2 snapshots 2015-04-23 11:11:21 +01:00
Dave Syer 22c261c8ce Update connectors version 2015-04-22 11:40:25 +02:00
Spencer Gibb 3e98c78c12 add PATCH to SimpleHostRoutingFilter
fixes gh-307
2015-04-17 11:49:51 -06:00
Spencer Gibb 6dce075e99 upgrade to feign 8.1.1 which includes a fixes bad urls
fixes 305
2015-04-17 10:08:21 -06:00
Spencer Gibb 3d9e998a01 fix missing jersey apache4client 2015-04-16 14:49:12 -06:00
Spencer Gibb 55a8195a67 throw more descriptive error if no ServiceInstance is found 2015-04-16 14:48:38 -06:00
Dave Syer 936a3b334e Set explicit cookie policy in HTTP client in RibbonClientConfiguration
When the Apache HTTP client is being set up for the Ribbon client we can jump
in and explicitly set a cookie policy. Obviously, since the client will generally
be shared across sessions, the only sensible default is IGNORE_COOKIES.

I couldn't find a way round this without using deprecated APIs (but there
are other instances of that in our code because of the way Netflix uses
it, so maybe that doesn't matter).

Fixes gh-301
2015-04-16 13:28:37 +01:00
Spencer Gibb 1799809562 Merge branch 'hystrix-144' 2015-04-14 13:28:03 -06:00
Chad Jaros 38e25f390f Support for @RequestMapping on class and ResponseEntity return types 2015-04-14 09:54:43 -06:00
Spencer Gibb a765839771 upgrade hystrix to 1.4.4 and integrate dashboard changes
fixes gh-300
2015-04-14 08:58:18 -06:00
Spencer Gibb 20da63c238 Merge pull request #299 from royclarkson/dashboard-cf-fix
Ignore 'Connection: close' header from stream response
2015-04-13 16:48:16 -04:00
Roy Clarkson 408910e893 Ignore 'Connection: close' header from stream response
This commit resolves an issue where some versions of Cloud Foundry are
setting the 'Connection: close' HTTP header in the Hystrix stream
response, which causes the dashboard to not display the stream data.

See https://github.com/cloudfoundry/gorouter/issues/71
2015-04-13 15:19:16 -05:00
Spencer Gibb 45449cfb84 polish 2015-04-13 12:50:45 -06:00
Alexandru Burghelea 611c0b5c1c Added posibility to append archaius configuration to existing installed configurations 2015-04-13 12:50:45 -06:00
Spencer Gibb 9699664e09 Merge pull request #294 from joshiste/master
Docs: Example of static, loadbalanced Zuul configuration
2015-04-13 10:40:59 -04:00
Spencer Gibb 3279ae601f Merge pull request #298 from joshiste/exlude-mockito-all
Exlude mockito all
2015-04-13 10:40:53 -04:00
Roy Clarkson e1b6ddf563 Format whitespace 2015-04-13 08:40:15 -05:00
Johannes Stelzer 242419726d Exclude mockito-all from zuul-core and turbine-core
However turbine and zuul are depending on mockito-all in compile-scope. Not only I want to get rid of these (usually) test-dependencies, but also mockito-all contains some hamcrest-classes (in older versions) getting in conflict with the classes from spring-test
2015-04-11 16:59:19 +02:00
Spencer Gibb 3794213aaf add a note about the Spring Cloud DiscoveryClient.
fixes gh-276
2015-04-06 15:12:38 -06:00
Johannes Stelzer 6a0737dacf Example of static, loadbalanced Zuul configuration
It took me quite a bit, to figure out how to configure Zuul to use a loadbalanced route without Eureka. I hope this short explicit example will clarify it for other people.
2015-04-02 20:33:05 +02:00
Dave Syer 873854810a Fix eureka instance metadata in Cloud Foundry example 2015-04-02 13:45:05 +01:00
Dave Syer 555b1f1c06 Use UrlPathHelper to determine the current url path in Zuul
If the app has a context path then the route locator does not match
the whole requestURI (as provided by the raw servlet API), but Spring
has a utility for stripping it off, so we can just use that.

Fixes gh-270
2015-04-01 12:05:05 +01:00
Dave Syer 5961931caf Make /error path a special case in ZuulHandlerMapping
If there is a default route (/**) then the ZuulHandlerMapping will
mask the /error path in a normal Spring Boot application. This change
makes it a special case so that ZuulHandlerMapping will never map
the /error route (the one specified by the ErrorController).

Fixes gh-284
2015-04-01 11:50:33 +01:00
Spencer Gibb 814f7e9283 added warn logging to hystrix dashboard proxy servlet 2015-03-27 10:57:03 -06:00
Spencer Gibb 20bb1af655 added debug logging to hystrix dashboard proxy servlet 2015-03-27 10:11:53 -06:00
Roy Clarkson 370ce804e3 Update docs to use @EnableCircuitBreaker 2015-03-27 10:44:06 -05:00
Spencer Gibb 2d18706219 updated to use RestTemplateCustomizer 2015-03-26 11:45:37 -06:00
Spencer Gibb 8bfef044d2 fixed bad class name 2015-03-26 11:45:37 -06:00
Spencer Gibb 28db47c72a move RibbonClientHttpRequestFactory to configuration conditional on netflix httpclient classes 2015-03-26 11:45:37 -06:00
Spencer Gibb 55d46eb6c7 add support for ribbon http client in RestTemplate.
fixes gh-280
2015-03-26 11:45:37 -06:00
Dave Syer 5f9c3e6073 Ensure management.port=-1 switches off actuator endpoints in turbine 2015-03-26 09:37:50 +00:00
Dave Syer 3714cee51d Clarify impact of @ComponentScan on @RibbonClient
Fixes gh-281
2015-03-26 09:36:27 +00:00
Dave Syer d0021f0415 Clarify turbine config
Also fixes gh-277 (metioning starters explicitly).
2015-03-25 09:42:00 +00:00
Spencer Gibb f5b6c58f30 upgrade hystrix to 1.4.1
fixes gh-279
2015-03-24 13:44:29 -06:00
Spencer Gibb f0d8c86b51 Only call shutdown() if DiscoveryClient is not null.
fixes spring-cloud-samples/tests/issues/2
2015-03-24 13:21:46 -06:00
unknown 76d0c018ee Ensure ZuulProperties.servletPath is applied to routes
Fixes gh-271, fixes gh-272
2015-03-24 13:17:43 +00:00
Dave Syer e60a0fc4fb Re-initialize DiscoveryClient if config client is in "eureka-first"
This was kind of ugly, and caused a static usage of the eureka
DiscoveryCLient to become necessary again, just so the @Bean that is
provided for the user stays accurate (it has to be the one in the main
application context, even if the parent boot strap has different
instance metadata).

I tested with a vanilla Eureka server and config server, and used the
client in tests/eureka-first.

Fixes gh-268
2015-03-23 16:53:50 +00:00
boostrack 9dc2821bc9 Small fixes to typos in docs
Fixes gh-274
2015-03-21 12:43:07 +00:00
Spencer Gibb 924363ca1c Sidecar reports true non-jvm app status to eureka.
Added a LocalApplicationHealthCheckHandler that checks the local app and sends the appropriate status to eureka.

fixes gh-251
2015-03-20 13:25:58 -06:00
Spencer Gibb 3515112cb2 move RestTemplate docs to s-c-commons 2015-03-20 13:00:19 -06:00
Dave Syer cdc5681694 Clarify role of filter beans in @EnableZuulProxy 2015-03-20 14:48:39 +00:00
Dave Syer 3ba7cfbff2 Add link to github in rendered docs 2015-03-20 11:47:28 +00:00
Dave Syer 1c54e6b937 Add annotation processor 2015-03-20 11:47:27 +00:00
Spencer Gibb 869e3ec92f upgrade to feign 8.1.0
removes ugly dagger dependency

fixes gh-183
2015-03-19 12:13:11 -06:00
Spencer Gibb edbb0acc7c Merge pull request #266 from warmuuh/patch-1
updated documentation of HystrixHealthIndicator
2015-03-19 11:20:47 -04:00
Peter 98ac81bda2 updated documentation of HystrixHealthIndicator
documentation was not conform to specified behavior as in #24
2015-03-19 14:43:12 +01:00
Dave Syer 444f3c8010 Feign client builder should not assume all annotations are the same
Simple solution is to check for instanceof before casting.

Fixes gh-265
2015-03-19 12:13:19 +00:00
Dave Syer b03b2f5629 Add ping stream to turbine AMQP
This change adds more data to the stream with {"type":"Ping"}
so (I believe) the existing dashboard client will discard it,
but stay alive.

Fixes gh-264
2015-03-18 17:03:25 +00:00
Dave Syer 72a182c914 Make turbine AMQP into an application (optional) 2015-03-18 15:55:08 +00:00
Dave Syer da24fa6d20 Refactor for config client stuff moving to commons 2015-03-18 15:42:41 +00:00
Dave Syer 369a00cb71 Validate hostname (serviceId) in @FeignClient
Since hostnames ahve quite strict syntax (e.g. no underscores) we should
validate the feign client name to prevent nasty surprises at runtime.

Fixes gh-263
2015-03-18 10:35:57 +00:00
Dave Syer 3445d093c7 Fix RabbitAdmin so it actually finds the Declarables 2015-03-17 16:08:57 +00:00
Spencer Gibb f3ced9e0ed allow zuul to have multiple values for each named header
fixes gh-260
2015-03-16 15:19:07 -06:00
Dave Syer cc2159f77b Use separate RabbitAdmin to declare queues and exchanges
Fixes gh-259
2015-03-16 14:57:58 +00:00
Dave Syer 9fe69cc80a Merge pull request #258 from sampengilly/master
Added additional Gradle tip to Eureka Server documentation.
2015-03-16 13:31:45 +00:00
sampengilly a2b0b70b55 Added additional Gradle tip to Eureka Server documentation. Tip covers the usage of the spring dependency management plugin in order to prevent fatal crashes during application startup. 2015-03-16 21:05:52 +10:00
Dave Syer 9357bdd9db Remove log4j notes since Eureka no longer requires it 2015-03-13 17:15:08 +00:00
Dave Syer 0391cfff65 Allow streaming of multipart requests in Zuul proxy
It turns out that the suckiness of Zuul with multipart requests
comes almost entirely from the Multipart handling in Spring's
DispatcherServlet. This change makes the proxy routes available
on an alternative path /zuul/<normal_path> (where
/zuul is the default value of zuul.servletPath). I have
tested those with 800MB file uploads using the main method in
the FormZuulServletProxyApplicationTests and the main
observation is that there is no OutOfMemory error (no-one tries
to download the complete request body). It works with Ribbon
and with the simple (HttpClient) filter. With Ribbon you
will need to set some timeouts if you want to upload files
as large as that, e.g. see application.yml in the tests:

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000
ribbon:
  ConnectTimeout: 3000
  ReadTimeout: 60000

You need to set "Transfer-Encoding: chunked" in the
incoming request. Chrome does not do this by default
apparently, but I was able to test with curl, e.g.

$ curl -v -H "Transfer-Encoding: chunked" \
  -F "file=@mylarg.iso" \
  localhost:9999/zuul/direct/file

The old proxy paths through the DispatcherServlet are still
available (for backwards compatibility and for convenience of
having the paths available at the root of the context path).

Fixes gh-254
2015-03-13 16:55:30 +00:00
Dave Syer da4f5d3454 Fix issue with servlet request wrappers in Zuul
Because of the way that a FormBodyServletRequestWrapper was
implemented (extending the Zuul servlet 2.5 wrapper) it could
barf at runtime if anyone called its servlet 3.0 methods. The fix
for that was to extract our Servlet30RequestWrapper and extend that
instead.

Also tweaked the DebugFilter a bit so it doesn't try and display
the whole payload. Probably speeds up file uploads a bit but the
fact that we have to store the whole request body in memory is
going to kill us eventually.

See gh-254
2015-03-13 13:07:33 +00:00
Spencer Gibb e2583f5380 upgrade Ribbon to version 2.0.0.
From 2.0.RC13

fixes gh-216
2015-03-06 12:47:27 -07:00
Spencer Gibb df7abd1bc9 added test ReadTimeout parameter 2015-03-06 12:20:02 -07:00
Dave Syer 164c9f8755 Add bintray properties 2015-03-06 10:48:29 +00:00
Spencer Gibb 384fad6c40 wrap SpringLBClientFactory in a CachingLBClientFactory so LBClients are reused.
fixes gh-240
2015-03-05 12:39:22 -07:00
Spencer Gibb 4bd7c3bdcf upgrade feign to 7.3.0
fixes gh-243
2015-03-05 11:42:09 -07:00
Spencer Gibb 4e795ea783 upgrade hystrix to 1.4.0
fixes gh-242
2015-03-03 17:23:26 -07:00
Dave Syer e3a20d5d6b Update to next snapshot 2015-03-03 14:56:26 +00:00
245 changed files with 5525 additions and 1123 deletions
+1
View File
@@ -10,3 +10,4 @@ target/
_site/
.idea
*.iml
.factorypath
+1
View File
@@ -0,0 +1 @@
-Xmx1024m -XX:CICompilerCount=1 -XX:TieredStopAtLevel=1 -Djava.security.egd=file:/dev/./urandom
+1
View File
@@ -0,0 +1 @@
-DaltSnapshotDeploymentRepository=repo.spring.io::default::https://repo.spring.io/libs-snapshot-local -P spring
BIN
View File
Binary file not shown.
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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:
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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>
+2 -2
View File
@@ -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
+320 -36
View File
@@ -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
+199 -114
View File
@@ -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]
+1 -1
View File
@@ -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
Vendored Executable
+245
View File
@@ -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} "$@"
Vendored
+145
View File
@@ -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%
+28 -64
View File
@@ -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>
+38 -9
View File
@@ -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>
@@ -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);
@@ -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,
@@ -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,
@@ -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,
@@ -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();
}
}
@@ -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() + "/";
}
}
@@ -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,
@@ -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,
@@ -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,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);
}
}
}
@@ -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,
@@ -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);
@@ -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,
@@ -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();
@@ -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);
}
}
@@ -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,
@@ -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,
@@ -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;
@@ -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,
@@ -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,
@@ -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,
@@ -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
@@ -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
@@ -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) {
@@ -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;
}
}
@@ -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();
}
}
}
@@ -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();
}
}
@@ -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,
@@ -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);
}
}
@@ -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,
@@ -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()));
}
}
@@ -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));
}
@@ -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) {
@@ -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));
}
}
}
}
@@ -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,
@@ -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;
}
}
}
@@ -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,
@@ -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,
@@ -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());
}
@@ -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,
@@ -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);
}
}
}
@@ -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,
@@ -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;
}
}
}
@@ -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(
@@ -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();
}
}
}
@@ -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,
@@ -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,
@@ -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);
@@ -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,
@@ -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,
@@ -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,
@@ -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();
}
}
@@ -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);
@@ -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,
@@ -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();
}
}
@@ -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(" ", "_");
}
}
@@ -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())));
}
}
}
}
}
@@ -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);
}
@@ -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())));
}
}
}
}
@@ -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.);
}
}
}
@@ -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,
@@ -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,
@@ -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,
@@ -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,
@@ -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
@@ -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,
@@ -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,
@@ -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>");
}
}
@@ -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();
@@ -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,
@@ -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,
@@ -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;
}
}
@@ -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,
@@ -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,
@@ -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,
@@ -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() {
@@ -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);
@@ -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();
}
}
@@ -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();
}
}
}
@@ -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