Compare commits

...
737 Commits
Author SHA1 Message Date
buildmaster afa69e7b8b Update SNAPSHOT to 1.4.1.RELEASE 2018-01-12 13:23:44 +00:00
Ryan Baxter 8d18dfbc3d Set hystrix timeout for Zuul routes based on whether the hystrix command timeout, default timeout, or ribbon timeouts are set. Fixes #2606 (#2633) 2018-01-11 10:05:02 -05:00
Ryan Baxter d2155dc561 Only strip the prefix if it actually part of the path. Fixes #2377 (#2624) 2018-01-08 14:42:33 -05:00
saga 882219f962 spring-retry listener features (#2609) 2018-01-04 09:42:26 -05:00
Ryan Baxter 8dfcdbed76 Adding repos to sc-netflix-hystrix-contract pom 2018-01-04 07:49:06 -05:00
Ryan Baxter 8017995ba3 Moving sc-build to 1.3.8.BUILD-SNAPSHOT 2018-01-03 19:42:03 -05:00
Ryan Baxter ca399ffd0b Moving dependencies to snapshots 2018-01-03 16:28:41 -05:00
Ryan Baxter a4c995a394 Disable following redirects in SimpleHostRoutingFilter. Fixes #2578. (#2608) 2018-01-03 14:19:39 -05:00
Nastya Smirnova 9deab33dc1 fixes #2524 (#2574)
set proper status and health eureka urls if prefer ip address is set
2018-01-02 15:42:59 -05:00
周立 94358489ef Change spring-cloud-starter-hystrix-dashboard to spring-cloud-starter-netflix-hystrix-dashboard (#2561)
Change spring-cloud-starter-hystrix-dashboard to spring-cloud-starter-netflix-hystrix-dashboard
2017-12-18 09:05:30 -05:00
Craig Rueda 504720e366 InputStream should always be closed after reading contents from upstreams (#2501)
* InputStream should always be closed after reading contents from upstreams
2017-12-15 14:07:34 -05:00
Ryan Baxter 07de257371 Merge remote-tracking branch 'origin/1.3.x' into 1.4.x 2017-12-08 17:19:57 -08:00
Ryan Baxter 0401ad96e5 Fixes double encoding when creating secure URLs (#2526)
* Fixes double encoding when creating secure URLs.  Fixes #2478.
2017-12-08 10:39:21 -08:00
Ryan Baxter efb91874f8 Correcting image links. Fixes #2487 2017-12-03 15:06:48 -08:00
Daniel Lavoie ebdcca4fa8 Hystrix Dashboard Cannot display anything for Turbine Stream (#2484)
Closes #2475
2017-12-01 16:38:15 -05:00
周立 2287e0caf2 fix zuul filters package url in Edgware (#2489)
fix zuul filters package url in Edgware
2017-11-30 10:48:35 -05:00
Spencer Gibb dde7ff4c45 Update docs branches 2017-11-28 12:38:23 -05:00
saga 6c52269dc5 Associated with this PR # 2466 (#2470)
Use StringBuffer for String concatenation
2017-11-27 07:55:39 -05:00
saga 736a3e4610 Format indent adjustment (#2468)
Format indent adjustment
2017-11-26 07:32:33 -05:00
Spencer Gibb 1e87b50998 Update releaser build to build docs 2017-11-21 18:37:48 -05:00
Spencer Gibb f59a228daf Pass system properties to releaser build command 2017-11-21 17:48:01 -05:00
Ryan Baxter db3a412c8a Fixing command to run maven 2017-11-20 16:10:55 -05:00
Ryan Baxter 202f96876e Bumping versions to 1.4.1.BUILD-SNAPSHOT after release 2017-11-20 16:03:53 -05:00
Ryan Baxter 2489d5e234 Going back to snapshots 2017-11-20 16:03:52 -05:00
Ryan Baxter 64ad2ba20c Update SNAPSHOT to 1.4.0.RELEASE 2017-11-20 15:54:27 -05:00
Yongsung Yoon 60c4e1b007 Add support loadBalancerKey in RibbonRoutingFilter of Zuul for Canary test (#2439)
* Add support loadBalancerKey in RibbonRoutingFilter

* Add documentation about how to provide a loadBalancerKey to Ribbon

* Add integrationtest for RibbonRoutingFilter's loadBalancerKey support
2017-11-20 06:28:23 -05:00
Ryan Baxter 6e3b4fdd7b Adds backoff policies when retrying requests (#2436)
* Adds backoff policies when retrying requests.  Fixes #2195

* fixing final variable
2017-11-16 20:11:55 -05:00
Nastya Smirnova 62a8f72b98 Incorrect statusPageUrl and healthCheckUrl are reported to Eureka if management.context-path is not set (#2145) 2017-11-14 12:54:12 -05:00
Ryan Baxter 89441ac126 Added zuul timeout documentation. Fixes #2201 2017-11-10 15:54:05 -05:00
Ryan Baxter d9f7842aec Fix HystrixClientFallbackFactory implementation in docs. Fixes #2428 2017-11-08 11:00:51 -05:00
Ryan Baxter 65762b1dce Remove deprecated RetryableStatusCodeException. Fixes #2003 (#2427) 2017-11-07 17:26:17 -05:00
Nastya Smirnova ba24811356 Report jmx and management ports to Eureka metadata map (#1830)
Follows: https://github.com/spring-cloud/spring-cloud-netflix/pull/1317
2017-11-07 13:42:23 -05:00
agseijas 573d52119a Adds feign-* missing managed dependencies (#2417)
Probably we could import a bom from feign.
2017-11-07 10:56:10 -05:00
Ryan Baxter 3cafacc3e9 Fixes #2415 2017-11-06 15:43:20 -05:00
Spencer Gibb a805b53d86 Merge branch 'daniellavoie-no-eureka-fix' 2017-10-30 18:10:15 -04:00
Daniel Lavoie c0c59320a5 Regression fix for hystrix stream without eureka registration 2017-10-30 18:10:11 -04:00
Spencer Gibb 271bfcfb31 Fix tests 2017-10-27 18:43:31 -04:00
JagmohanSharma 798a4bb2b9 https://github.com/spring-cloud/spring-cloud-netflix/issues/2387 (#2388)
In order to fix issue with TPS in hystrix-dashboard threadpool section, removing average calculation for propertyValue_metricsRollingStatisticalWindowInMilliseconds. This was done for hystrixCommand.js previously but not for hystrixThreadpool.js.
2017-10-24 13:18:34 -04:00
Spencer Gibb df7f31098c Going back to snapshots 2017-10-23 17:44:26 -04:00
Spencer Gibb fa9524f9c9 Update SNAPSHOT to 1.4.0.RC1 2017-10-23 17:39:56 -04:00
Spencer Gibb 1368f059bb Change contract property name 2017-10-23 17:16:32 -04:00
Spencer Gibb 8701060355 Add workaround for locally failing test. 2017-10-23 17:05:30 -04:00
Spencer Gibb ee067ed3f2 Merge branch 'raiRaiyan-gh-2303' 2017-10-23 14:43:38 -04:00
Raiyan b57dedb74a - Eureka client auto configures the secure port when secure port is enabled
- The eureka instance auto registration also changes the secure port when the server port is random (0)

Fixes gh-2303
2017-10-23 14:43:20 -04:00
Spencer Gibb d7c13adac0 Merge branch 'tiny-dancer-patch-1' 2017-10-23 14:19:13 -04:00
mg bd2473a7bf Update spring-cloud-netflix.adoc
Update documentation to reflect https://github.com/spring-cloud/spring-cloud-netflix/issues/1048
2017-10-23 14:18:40 -04:00
Gregor Zurowski de16cb71f1 Add new filters actuator endpoint (#2244)
* Add new Zuul filters endpoint and refactor routes endpoint
* Refactor FiltersEndpoint
- Extend from AbstractEndpoint and simplify code
- Move tests classes into package endpoint
- Remove usage of Lombok
* Conditionally enable Zuul routes and filters endpoints
* Provide FilterRegistry to FiltersEndpoint via constructor
2017-10-23 14:08:00 -04:00
Nastya Smirnova ffc431753b Create TurbineClustersProvider
Extract separate class TurbineClustersProvider to be able to provide custom solution for retrieving configured clusters (#2219)
2017-10-20 21:46:29 -04:00
Spencer Gibb 28de82e63c Use updated Registration rather than getLocalServiceInstance()
See https://github.com/spring-cloud/spring-cloud-commons/issues/265
2017-10-18 11:34:38 -04:00
Spencer Gibb 6c65266f05 Formatting spaces->tabs 2017-10-17 13:37:28 -04:00
Spencer Gibb 6ffe2f1af2 Downgrade archaius to 0.7.4 for java 7 compatibility.
see https://github.com/Netflix/archaius/pull/522
2017-10-17 13:12:26 -04:00
Dave Syer 68ce0cf25f Merge branch 'feature/eureka-stubs' 2017-10-17 10:47:35 +01:00
Spencer Gibb 7cff8ddbd3 Add missing jackson serialization for instanceinfo.
Port information was missing from data sent to eureka.

fixes gh-2372
2017-10-16 16:23:51 -04:00
Dave Syer 9d1eed6a82 Add script to build contract utils 2017-10-16 16:54:35 +01:00
Dave Syer eeadc829b9 Parameterize contract version 2017-10-13 15:17:46 +01:00
Dave Syer efe040549c Move contract helper out of the reactor build 2017-10-13 15:17:46 +01:00
Dave Syer 5bc9e65cdd Add tests for server metadata 2017-10-13 15:17:46 +01:00
Dave Syer d6a604051f Split tests up into individual methods 2017-10-13 15:17:46 +01:00
Dave Syer 51a03b30f2 Consolidate stub tests into one place 2017-10-13 15:17:46 +01:00
Dave Syer 561f3b2fbf Add wiremock verification 2017-10-13 15:17:46 +01:00
Dave Syer 3a7af2c8fe Generate some stubs, yay! 2017-10-13 15:17:46 +01:00
Dave Syer 26c7c362d2 Add property to tests, parameterizing stubs 2017-10-13 15:17:46 +01:00
Dave Syer 0642ea52c9 Remove unused stubs file 2017-10-13 15:17:46 +01:00
Dave Syer 957e33207e Stabilise some tests 2017-10-13 15:17:46 +01:00
Dave Syer a39fe345b5 Remove deprecated annotation 2017-10-13 15:17:46 +01:00
Dave Syer 40e72ddb8c Tweak assertions a bit 2017-10-13 15:17:46 +01:00
Dave Syer 195c3fd75d Extract utility class into shared library for contract tests 2017-10-13 15:17:46 +01:00
Dave Syer c626bccad3 Add rest template to extract SSE events and assert them
The message has to be sent after the endpoint starts up, which only
happens after the first request is accepted, so we put that logic
in the response extractor.
2017-10-13 15:17:46 +01:00
Dave Syer e295188075 Add tests for body of message 2017-10-13 15:17:46 +01:00
Dave Syer 7804e1bd59 Add Spring Cloud Contract for hystrix stream 2017-10-13 15:17:46 +01:00
Spencer Gibb 6b6889b8b4 Fixes refresh scope bean type. 2017-10-04 19:32:57 -04:00
Fahim Farook f809c119bd Allow init params to be set on ProxyStreamServlet. (#2335)
* Allow init params to be set on ProxyStreamServlet.

Fixes gh-2301

* Added unit test for #gh-2301
2017-10-03 13:43:17 -04:00
Ryan Baxter a806dc920f Bumping versions to 1.3.6.BUILD-SNAPSHOT after release 2017-10-02 16:22:49 -04:00
Ryan Baxter 74fc02b2cc Going back to snapshots 2017-10-02 16:22:48 -04:00
Ryan Baxter c1fc987d96 Update SNAPSHOT to 1.3.5.RELEASE 2017-10-02 16:17:55 -04:00
Alex Sartan 7f97a61ed5 Fix minor grammar issue in Zuul doc (#2336) 2017-10-01 13:25:32 -04:00
Andre Dörnbrack 05a3db3cc0 Fix zuul post retry (#2209)
Modfied RibbonCommandContext to make request entity resettable. fixes gh-892
2017-09-25 13:59:31 -04:00
Ryan Baxter 145b7ce69a Remove @EnableDiscoveryClient and @EnableEurekaClient (#2242)
* Remove @EnableDiscoveryClient and @EnableEurekaClient as there are now optional.  Fixes #2241.
2017-09-25 13:18:04 -04:00
Ryan Baxter c6fa162728 Merge pull request #2293 from mattjbray/fix-requestmapping-headers
SpringMvcContract: Ignore RequestMapping headers with no value.
2017-09-22 21:15:53 +01:00
Matt Bray 527dc2cc48 SpringMvcContract: Ignore RequestMapping headers with no value.
According to:

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html#headers--

the `headers` field can take several formats:

1. Require the header with a concrete value: `"My-Header=myValue"`
2. Require the header with any value: `"My-Header"`
3. Require the header is absent: `"!My-Header"`
4. Require the header with a negated value : `"My-Header!=myValue"`

This fixes `parseHeaders` for cases 2-4 (which previously threw a `StringIndexOutOfBoundsException`).

Fixes gh-1778.
2017-09-22 18:01:04 +01:00
Dave Syer 7672ee3666 Fix hystrix command keys in tests to be unique 2017-09-21 12:13:19 +01:00
Dave Syer 7ca4f33dcf Remove System.err from test 2017-09-21 11:30:07 +01:00
Spencer Gibb 78e4cb4410 Fix for potential NPE 2017-09-12 17:50:57 -06:00
spccold baa50c136a simplify org.springframework.cloud.netflix.zuul.filters.ProxyRequestH… (#1965)
* simplify org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper#setResponse

* Update ProxyRequestHelper.java
2017-09-11 17:32:16 -04:00
Ryan Baxter 24d0bedf7d Merge pull request #2255 from DominikMostek/fallback_cause
Fallback cause is propagated to fallback provider
2017-09-08 14:12:52 -04:00
Ryan Baxter 38b8786ede Merge pull request #2261 from ryanjbaxter/timeout-values
Adjust Ribbon connect and read timeout values
2017-09-08 14:08:46 -04:00
Ryan Baxter 146513aeb0 Set Ribbon connect and read timeout values to equal the Hystrix timeout. Fixes #1827. 2017-09-08 10:19:01 -04:00
Dominik Mostek 4dc5088fb2 Updated documentation with new FallbackProvider 2017-09-07 12:55:33 +02:00
Dominik Mostek 4c53fd2bf6 Fallback cause is propagated to fallback provider
Cause can now be propagated to fallback provider in order to respond accordingly to a exception type.
New interface is introduced in order not to break any existing falback providers.
2017-09-07 12:23:37 +02:00
Jin Zhang 885425e2a3 format 'pom.xml' code style (#2257) 2017-09-06 22:26:20 -04:00
Ryan Baxter b74c77b74f Merge pull request #2256 from ryanjbaxter/update-rxjava-version
Update rxjava version
2017-09-06 20:21:12 -04:00
Ryan Baxter 1bc8e8fd3a Update rxjava version. Fixes #2248. 2017-09-06 16:02:04 -04:00
Gregor Zurowski 6dbdb1d8ed Remove commons-lang builders (#2243)
This commit removes usages of commons-lang builders for equals,
hashCode and toString methods.

Signed-off-by: Gregor Zurowski <gregor@zurowski.org>
2017-08-31 19:26:53 -04:00
Tom Cawley ea5585e381 Dynamic filtering on Zuul SimpleRouteLocator.getRoutes()
* Introduce getRoutesMap() so subclasses can have read/write access to configured/discovered routes.
2017-08-31 19:20:33 -04:00
Ryan Baxter ace95a3355 Merge pull request #2240 from gzurowski/remove-lombok-from-docs
Remove Lombok from documentation
2017-08-31 16:47:25 -04:00
Spencer Gibb 019615f376 remove commons.lang.builder 2017-08-31 14:45:48 -06:00
Ryan Baxter a8d40afd58 Merge pull request #2239 from gzurowski/remove-lombok-from-netflix-core
Remove Lombok from spring-cloud-netflix-core module
2017-08-31 16:22:50 -04:00
Marcin Grzejszczak ebe3800c75 Merge branch '1.3.x' 2017-08-31 15:01:12 +02:00
Marcin Grzejszczak 30b98bea85 Referrencing resources from sc-build 2017-08-31 15:01:05 +02:00
Gregor Zurowski d9738a7ee5 Remove Lombok from documentation
Signed-off-by: Gregor Zurowski <gregor@zurowski.org>
2017-08-31 12:15:35 +02:00
Gregor Zurowski d778b938e7 Remove Lombok from spring-cloud-netflix-core module
Signed-off-by: Gregor Zurowski <gregor@zurowski.org>
2017-08-31 12:01:03 +02:00
Marcin Grzejszczak 5e9fa908b4 Merge branch '1.3.x' 2017-08-29 15:59:39 +02:00
Marcin Grzejszczak 91808c5fd9 Updated whitelists 2017-08-29 15:57:17 +02:00
Marcin Grzejszczak 0784b1ee19 Migrated to new docs; fixes #2237 2017-08-29 15:54:31 +02:00
Marcin Grzejszczak b3cdd72754 Bumped libs' 2017-08-29 15:54:01 +02:00
Marcin Grzejszczak 4a8913f7ea Migrated to new docs; fixes #2237 2017-08-29 15:04:32 +02:00
Ryan Baxter 8fbd73af1e Going back to snapshots 2017-08-24 17:54:53 -04:00
Ryan Baxter 1be2b2c295 Update SNAPSHOT to 1.4.0.M1 2017-08-24 17:49:28 -04:00
Ryan Baxter 02778fd670 fixing links in docs 2017-08-24 17:07:42 -04:00
Ryan Baxter 985f014f40 bumping sc-build version 2017-08-24 16:44:46 -04:00
Nastya Smirnova 2a63d07146 Extract getApplications method in CommonsInstanceDiscovery
This allows the ability to override behavior of retrieving configured applications
2017-08-21 13:49:42 -04:00
Ryan Baxter 947d30c5c4 Bumping versions to 1.3.5.BUILD-SNAPSHOT after release 2017-08-18 13:59:32 -04:00
Ryan Baxter e71ce3395d Going back to snapshots 2017-08-18 13:59:31 -04:00
Ryan Baxter f5e7be25dd Update SNAPSHOT to 1.3.4.RELEASE 2017-08-18 13:55:59 -04:00
Ryan Baxter c8d024c25f Merge pull request #2213 from ryanjbaxter/deprecation-warnings-for-starters
Adds deprecation warning to starters
2017-08-18 13:21:25 -04:00
Ryan Baxter b519ad8d18 Adds deprecation warning to starters. Fixes #2148. 2017-08-18 12:30:45 -04:00
Ryan Baxter fcb1f5dc54 Bumping versions to 1.3.4.BUILD-SNAPSHOT after release 2017-08-18 07:55:23 -04:00
Ryan Baxter 9b7a2f93ad Going back to snapshots 2017-08-18 07:55:22 -04:00
Ryan Baxter 9ce253b644 Update SNAPSHOT to 1.3.3.RELEASE 2017-08-18 07:51:04 -04:00
Ryan Baxter 24cbbb9a11 Merging 1.3.x into master 2017-08-17 14:47:45 -04:00
Ryan Baxter ed57c8f0fa Merge pull request #2208 from ryanjbaxter/document-https-hystrix-dashboard
Document untrusted certificates when connecting to hystrix.stream endpoint
2017-08-17 14:32:52 -04:00
Ryan Baxter 07b99704ee Fixes #1523. Document untrusted certificates when connecting to hystrix.stream endpoint. 2017-08-17 14:02:14 -04:00
Biju Kunjummen 9440e4d153 Fixes #2083. Porting #1916 to the 1.3.x branch. 2017-08-17 12:29:23 -04:00
Spencer Gibb 1840647458 Move EurekaClient.close() to EurekaRegistration.close().
This moves the lifecycle of the eureka client of of EurekaServiceRegistry.

fixes gh-2099
2017-08-16 00:53:40 -06:00
Spencer Gibb c3114e65bf Merge branch '1.3.x' 2017-08-10 13:59:46 -06:00
Spencer Gibb c2aa60f292 Update test of setContentLengthLong to include param.
Otherwise useServlet31 was always returning false.

fixes gh-2196
2017-08-10 13:58:45 -06:00
Spencer Gibb e4e29e1316 Move tests out of default and base package 2017-08-04 14:37:28 -06:00
Biju Kunjummen 63f975ad43 Sync up matching of ignored paths and route paths (#2163)
Signed-off-by: João Salavessa <joao.salavessa@sky.uk>
2017-08-04 15:41:58 -04:00
Olivier Bourgain a01fc5b758 Fix check then act issue (#2160) 2017-08-04 15:40:10 -04:00
Ryan Baxter 481e31220b Merge remote-tracking branch 'origin/1.3.x' 2017-08-04 10:57:41 -04:00
Ryan Baxter 040ce4db81 Merge pull request #2177 from repos-jmp/1.3.x
Fixes gh-2125
2017-08-04 10:51:08 -04:00
Ryan Baxter 8fb89b25d9 Merge pull request #2080 from ryanjbaxter/http-client-centralization
Centralizing HTTP client creation and configuration
2017-08-03 13:02:09 -04:00
Jayanth MP 099c9024bb Fixes gh-2125
(Updated okhttp3 from v3.6.0 to v3.8.1)
2017-08-02 17:24:19 +05:30
Ryan Baxter bd7e75f4b3 Merge remote-tracking branch 'Upstream/master' into http-client-centralization 2017-08-01 16:46:07 -04:00
Ryan Baxter b3d61a6de7 Removing duplicate install goal 2017-07-31 20:22:45 -04:00
Dave Syer e5b14e4177 Fix all the start poms to be jars again
They are all deprecated but that's no reason to break them.
2017-07-26 12:13:25 +01:00
Spencer Gibb 0de86d3faf fix hashCode 2017-07-25 08:58:39 -06:00
Gregor Zurowski aa2159b7e5 Remove Lombok from spring-cloud-netflix-eureka-client module (#2131) 2017-07-24 19:07:18 -04:00
Biju Kunjummen 1c30c24639 Replace o.s.b.a.PropertyPlaceholderAuto* with o.s.b.a.context.PropertyPlaceholderAuto* (#2155) 2017-07-24 18:11:20 -04:00
Spencer Gibb cd2ed8eeee Restore turbine stream dependencies. 2017-07-24 14:37:31 -06:00
Spencer Gibb 112a3e27a6 Fix versions of new starters 2017-07-21 14:21:44 -06:00
Daniel Lavoie bbac5a730f Rename starters to include -netflix- (#1886)
Fixes gh-665
2017-07-21 15:59:32 -04:00
Daniel Lavoie 348c96bf13 Optionally remove Jersey dependencies from Eureka Client (#1850)
Fixes gh-1849
2017-07-21 15:55:57 -04:00
Biju Kunjummen fd783734ac Add docs for default ribbon client configuration (#2142)
fixes #1741
2017-07-21 11:45:01 -04:00
Biju Kunjummen fc4f375a65 Restore ignored test - works cleanly for a full build (#2146) 2017-07-21 11:41:10 -04:00
Spencer Gibb 1d6b47ceef Bumping versions to 1.3.3.BUILD-SNAPSHOT after release 2017-07-20 11:39:09 -06:00
Spencer Gibb cda1c876b2 Going back to snapshots 2017-07-20 11:39:08 -06:00
Spencer Gibb 6112ea2558 Update SNAPSHOT to 1.3.2.RELEASE 2017-07-20 11:30:44 -06:00
Spencer Gibb 37129671b6 Merge branch '1.3.x' 2017-07-20 10:06:32 -06:00
Spencer Gibb 97f1655ff7 Ensure ordering of feign ribbon client configuration.
On certain platforms and compilers the generated bytecode is not what
the configuration expected. By moving inner classes to the upper level
and using @Import this will guarantee the proper order.

fixes gh-2086
2017-07-20 10:02:05 -06:00
Spencer Gibb 1cf509c365 Merge branch '1.3.x' 2017-07-19 12:12:01 -06:00
Spencer Gibb 6b530b0269 Ensure ordering of ribbon client configuration.
On certain platforms and compilers the generated bytecode is not what
the configuration expected. By moving inner classes to the upper level
and using @Import this will guarantee the proper order.

fixes gh-2086
2017-07-19 12:11:07 -06:00
Jayanth e525ab7d64 Hystrix open threshold documentation (#2136)
fixes gh-1943
2017-07-18 17:18:01 -04:00
Spencer Gibb 31d19fd51a Remove commons.lang.builder refs 2017-07-18 11:05:05 -06:00
Spencer Gibb 74540e9f4d Merge branch '1.3.x' 2017-07-18 10:57:41 -06:00
Spencer Gibb afc8a99d7e Add @NestedConfigurationProperty to transport.
Needed in `EurekaClientConfigBean` because `CloudEurekaTransportConfig` is a separate class.

Fixes gh-2138
2017-07-18 10:56:41 -06:00
Tummala Dhanvi (c0mrad3) 859a79ad35 Document prefer hostname #2084 (#2092) 2017-07-18 12:39:43 -04:00
Ryan Baxter 6ab27328cc Merge pull request #2128 from gzurowski/remove-lombok-from-eureka-server
Remove Lombok from spring-cloud-netflix-eureka-server module
2017-07-14 13:44:12 -04:00
Gregor Zurowski 72c31ca524 Polish code
- Remove formatter markers
- Use StringBuilder in toString method

Signed-off-by: Gregor Zurowski <gregor@zurowski.org>
2017-07-14 16:26:04 +02:00
Gregor Zurowski 4d69941593 Remove Lombok from spring-cloud-netflix-eureka-server module
Signed-off-by: Gregor Zurowski <gregor@zurowski.org>
2017-07-14 11:49:33 +02:00
Ryan Baxter 0d19a55ddd Merge pull request #2120 from gzurowski/remove-lombok-log-from-eureka-client
Remove Lombok logging from spring-cloud-netflix-eureka-client module
2017-07-13 09:36:02 -04:00
Ryan Baxter ceef16f0c8 Merge pull request #2121 from gzurowski/remove-lombok-log-from-eureka-server
Remove Lombok logging from spring-cloud-netflix-eureka-server module
2017-07-13 09:35:38 -04:00
Ryan Baxter 187f34dfee Merge pull request #2122 from gzurowski/remove-lombok-log-from-core
Remove Lombok logging from spring-cloud-netflix-core module
2017-07-13 09:35:09 -04:00
Ryan Baxter 96b1e40c0a Merge pull request #2123 from gzurowski/fix-maven-warnings
Fix Maven warnings about missing plugin versions
2017-07-13 09:34:09 -04:00
Gregor Zurowski 63d77a8b81 Fix Maven warnings about missing plugin versions
Signed-off-by: Gregor Zurowski <gregor@zurowski.org>
2017-07-13 09:38:53 +02:00
Gregor Zurowski 8022342e81 Remove Lombok logging from spring-cloud-netflix-eureka-server module
Signed-off-by: Gregor Zurowski <gregor@zurowski.org>
2017-07-13 09:21:32 +02:00
Gregor Zurowski 6379e15ce1 Remove Lombok logging from spring-cloud-netflix-eureka-client module
Signed-off-by: Gregor Zurowski <gregor@zurowski.org>
2017-07-13 09:20:29 +02:00
Gregor Zurowski 5b075623ff Remove Lombok logging from spring-cloud-netflix-core module
Signed-off-by: Gregor Zurowski <gregor@zurowski.org>
2017-07-13 08:48:56 +02:00
Marco Schulte 46cbaa053c Fixed NPE if origin is unset (#2100) 2017-07-12 15:41:32 -04:00
Ryan Baxter ffc33b4c69 Merge pull request #2105 from gzurowski/routes-endpoint-details
Provide more Zuul route details in the routes actuator endpoint
2017-07-12 10:47:49 -04:00
Ryan Baxter 1d3773ab5e Merge pull request #2111 from gzurowski/remove-lombok-from-turbine-stream
Remove Lombok from spring-cloud-netflix-turbine-stream module
2017-07-12 10:35:48 -04:00
Ryan Baxter a968b4bec8 Merge pull request #2112 from gzurowski/remove-lombok-from-turbine
Remove Lombok from spring-cloud-netflix-turbine module
2017-07-12 10:35:06 -04:00
Ryan Baxter 65902d11e4 Merge pull request #2113 from gzurowski/remove-lombok-from-sidecar
Remove Lombok from spring-cloud-netflix-sidecare module
2017-07-12 10:34:12 -04:00
Ryan Baxter 050dc31c97 Merge pull request #2114 from gzurowski/remove-lombok-from-hystrix-dashboard
Remove Lombok from spring-cloud-netflix-hystrix-dashboard module
2017-07-12 10:33:29 -04:00
Gregor Zurowski aae96faa76 Remove Lombok from spring-cloud-netflix-hystrix-dashboard module
Signed-off-by: Gregor Zurowski <gregor@zurowski.org>
2017-07-11 23:25:05 +02:00
Gregor Zurowski 0dea44acf4 Remove Lombok from spring-cloud-netflix-turbine module
Signed-off-by: Gregor Zurowski <gregor@zurowski.org>
2017-07-11 23:14:40 +02:00
Gregor Zurowski b158f6f2b7 Remove Lombok from spring-cloud-netflix-turbine-stream module
Signed-off-by: Gregor Zurowski <gregor@zurowski.org>
2017-07-11 23:00:18 +02:00
Ryan Baxter 11da3d4e3c Merge remote-tracking branch 'Upstream/master' into http-client-centralization 2017-07-11 16:48:19 -04:00
Gregor Zurowski 5f4fc247a4 Remove Lombok from spring-cloud-netflix-sidecare module
Signed-off-by: Gregor Zurowski <gregor@zurowski.org>
2017-07-11 22:39:15 +02:00
Ryan Baxter 78ba3c5394 Cleanup and code review fixes. 2017-07-11 14:49:44 -04:00
Gregor Zurowski 781fa3ea0c Add documentation about the detailed output of the routes endpoint
Signed-off-by: Gregor Zurowski <gregor@zurowski.org>
2017-07-11 15:56:13 +02:00
Gregor Zurowski a08b661af6 Remove usage of Lombok
Signed-off-by: Gregor Zurowski <gregor@zurowski.org>
2017-07-11 10:38:51 +02:00
Ryan Baxter d0009ba05a Merge pull request #2087 from brenuart/gh1534
React on ContextClosedEvent only if from "our" context
2017-07-10 13:33:39 -04:00
Ryan Baxter ab9b5ba3ec Merge pull request #2104 from gzurowski/use-filter-constants
Use filter constants where appropriate
2017-07-10 11:49:06 -04:00
Ryan Baxter 70d39aae7b Merge pull request #2107 from yongsungyoon/fix-compile-error-on-ejc
Fix compilation problem on eclipse java compiler
2017-07-10 11:39:04 -04:00
Ryan Baxter 56ccc46f5f Added documentation 2017-07-09 15:26:24 -04:00
Ryan Baxter ff33905704 Updating property names 2017-07-09 14:21:10 -04:00
Ryan Baxter 69739420fa Takes advantage of new builder pattern. Also reverted elimination of properties for feign and ribbon. 2017-07-09 13:39:04 -04:00
Yongsung Yoon 2b593bc742 Fix compilation problem on eclipse java compiler 2017-07-10 00:36:07 +09:00
Gregor Zurowski eb1b7baf1c Provide more Zuul route details in the routes actuator endpoint
Add a parameter to control the level of details returned with the
routes actuator endpoint. If requested, return all available Zuul route
details including ID, prefix, sensitive headers, etc.

Signed-off-by: Gregor Zurowski <gregor@zurowski.org>
2017-07-08 22:40:39 +02:00
Gregor Zurowski 05eef0e8f5 Use filter constants
Signed-off-by: Gregor Zurowski <gregor@zurowski.org>
2017-07-07 22:16:11 +02:00
Eko Kurniawan Khannedy c2c634707e FeignClient Configuration Properties (#1942)
* add feign client properties

* configure feign builder from properties if exists

* change string config to class config and add unit test for feign client using configuration properties

* refactoring and add primary flag configuration

* add decode404 in feign client configuration properties

* change unit test properties decode404 to true

* remove lombok from FeignClientProperties and change primary attribute to default-to-properties

fixes gh-1931
2017-07-07 15:31:56 -04:00
Biju Kunjummen 0579b326be Support for re-writing Location Headers (#1863)
* Support for re-writing Location Headers

* Added additional constructor for Route, removed auto-configuration of LocationRewriteFilter

* Polished docs, more tests
2017-07-07 15:21:44 -04:00
Gregor Zurowski 70a26fec26 Use static import for constants (#2103)
Signed-off-by: Gregor Zurowski <gregor@zurowski.org>
2017-07-07 14:35:49 -04:00
Ryan Baxter 02ec9bb98e Merge remote-tracking branch 'Upstream/master' into http-client-centralization 2017-07-07 10:54:08 -04:00
Ryan Baxter b2f608b884 OK HTTP Client changes 2017-07-07 10:10:53 -04:00
Gregor Zurowski a496dc8170 Use constant utility classes instead of constant interfaces (#2081)
Signed-off-by: Gregor Zurowski <gregor@zurowski.org>
2017-07-06 12:01:26 -04:00
Bertrand Renuart 8ad15399d6 React on ContextClosedEvent only if originating from "our" context (not another like a child) 2017-07-03 15:52:01 +02:00
Yongsung Yoon 8e11514e7c Add options for RibbonCommand to use separate thread pools for hystrix (#2074)
* Add options for RibbonCommand to use separate thread pools for hystrix

* Remove lombok annotation and Add getter/setter for ZuulProperties$HystrixThreadPool

* Add doc about how to configure hystrix thread pools in Zuul Developer Guide

* Add testcases for HystrixThreadPoolKey of RibbonCommand

* Tiny change about if block in AbstractRibbonCommand
2017-06-30 20:03:52 -04:00
Spencer Gibb 89cb5573d9 bump commons to 1.3.0ˆ 2017-06-30 17:33:57 -06:00
Spencer Gibb 5856d76e65 Merge branch '1.3.x' 2017-06-30 17:11:18 -06:00
Spencer Gibb 86b1f6ede3 Make aspect method public
fixes gh-2063
2017-06-30 17:10:27 -06:00
Ryan Baxter 572305d7c8 Fixing code formatting 2017-06-28 12:00:11 -04:00
Ryan Baxter 2aa93abc0f Centralizing HTTP client creation and configuration 2017-06-28 11:43:52 -04:00
Ryan Baxter e2954d0117 Merge pull request #2059 from brandon-atkinson/gh-2042
Rename *Test.java -> *Tests.java. Fixes #2042
2017-06-20 10:49:11 -04:00
Brandon Atkinson 497b95ec64 Rename *Test.java -> *Tests.java. Fixes #2042 2017-06-19 20:41:54 -04:00
Ryan Baxter 02e7594de5 Bumping s-c-stream to Ditmars 2017-06-19 14:06:44 -04:00
Ryan Baxter 7efa68e853 Merge remote-tracking branch 'origin/1.3.x' 2017-06-19 14:05:29 -04:00
Ryan Baxter 003fefa61d Bumping release version 2017-06-19 14:04:29 -04:00
Ryan Baxter 5c035768b0 Merge remote-tracking branch 'origin/1.3.x' 2017-06-16 11:51:09 -04:00
Ryan Baxter 9a878b0fbd Merge pull request #2044 from bright/override_all_hystrix_concurrency_strategy_methods
Override getThreadPool from HystrixConcurrencyStrategy and delegate to
2017-06-16 11:27:06 -04:00
Piotr Mionskowski 779b026e6b Override getThreadPool from HystrixConcurrencyStrategy and delegate to
existing strategy if present. Fixes #2035
2017-06-15 06:06:58 +02:00
Ryan Baxter ccae4e4ce2 Bumping s-c-config version to 1.4.0 2017-06-14 16:40:48 -04:00
Dave Syer d39a22945b Merge branch '1.3.x' 2017-06-13 13:58:08 +01:00
Dave Syer aac7799e3e Clean up Zuul context in all tests 2017-06-13 13:57:16 +01:00
Dave Syer 310f558e9c Merge branch '1.3.x' 2017-06-12 16:15:44 +01:00
Dave Syer 1a0428da97 Merge branch '1.3.x' 2017-06-12 15:52:24 +01:00
Dave Syer 4783b07407 Support for multi-valued (batched) events in stream aggregator
This allows clients to send batched up events in the same format as
before (or to continue to send single events). We can switch the
default format to an array in 1.4.x.
2017-06-12 15:44:35 +01:00
André Dörnbrack 0334c5a289 Cleanup. Cherry picking #2031. 2017-06-09 12:42:12 -04:00
André Dörnbrack 12a9a2e485 code cleanup 2017-06-09 12:34:04 -04:00
Ryan Baxter f9424231a9 Merge pull request #2031 from doernbrackandre/fix_whitespaces_in_retryable_status_codes_list
fixes a problem with whitespaces in list of retryable status codes
2017-06-09 12:00:38 -04:00
Dave Syer d4ba70db5c Merge branch '1.3.x' 2017-06-09 13:31:17 +01:00
Dave Syer a59f5e1c4f Clean up some resources to keep tests running 2017-06-09 13:24:57 +01:00
André Dörnbrack 02316d4ee0 Merge branch 'fix_whitespaces_in_retryable_status_codes_list' of github.com:doernbrackandre/spring-cloud-netflix into fix_whitespaces_in_retryable_status_codes_list 2017-06-09 13:39:44 +02:00
André Dörnbrack 4f148de74d code cleanup 2017-06-09 13:37:56 +02:00
André Dörnbrack 9f10d5207f fixes a problem with whitespaces in list of retryable status codes 2017-06-09 10:12:24 +02:00
Ryan Baxter 484bde10b3 Adding 404 to list of retryable status codes 2017-06-08 16:18:11 -04:00
Ryan Baxter 8281757da8 Add path to try and fix intermittent failures with RetryableZuulApplicationTests pass 2017-06-08 15:43:15 -04:00
Ryan Baxter 4e25e2eb81 Revert "Trying a plain RestTemplate in an attempt to fix intermittent test failures."
This reverts commit f552426304.
2017-06-08 15:34:57 -04:00
Ryan Baxter f552426304 Trying a plain RestTemplate in an attempt to fix intermittent test failures. 2017-06-08 15:16:03 -04:00
Spencer Gibb 15295dd294 Merge remote-tracking branch 'origin/master' 2017-06-08 12:18:45 -06:00
Ryan Baxter 1405a1894b Closing response in Feign load balancer 2017-06-08 13:48:56 -04:00
Ryan Baxter a1c8cfe967 Close response when retry on certain status codes. Fixes #1970. 2017-06-08 13:48:56 -04:00
Ryan Baxter f0b424c84f Merge pull request #2021 from ryanjbaxter/response-code-retry-close-wait
Close response when retry on certain status codes
2017-06-08 13:47:27 -04:00
Spencer Gibb 582746587b Merge branch '1.3.x' 2017-06-08 11:35:45 -06:00
Spencer Gibb a69fe71406 Make archaius highest priority auto-configuration. 2017-06-08 11:35:05 -06:00
Spencer Gibb 13155ce64c Upgrade eureka to v1.7.0
fixes gh-2028
2017-06-07 13:14:07 -06:00
Jin Zhang c5d95ca805 SendErrorFilter: put response code into RequestContext, keep consistent with normal cases (#2004) 2017-06-07 12:06:35 -04:00
Ryan Baxter 0b2fc81be4 Closing response in Feign load balancer 2017-06-06 10:44:42 -04:00
Spencer Gibb 9e180b8ba7 Merge branch '1.3.x' 2017-06-05 16:41:57 -06:00
Spencer Gibb e08a6e8a48 Move zuul host routing timeouts to boot properties.
Previously, these were managed directly via archaius, but due to
some combination of changes in boot 1.5.x (likely a tomcat change as
zuul filters were initialzed in a servlet context listener) and Dalston,
The filter was created before archaius could be initialized.

The properties are backwards compatible and are in the
`ZuulProperties.Host` object.

fixes gh-1950
2017-06-05 16:40:12 -06:00
Ryan Baxter 7d06c140fa Close response when retry on certain status codes. Fixes #1970. 2017-06-05 14:36:49 -04:00
Ryan Baxter d914406c84 Merge pull request #2000 from rpahli/master
use only default https ports for https check
2017-06-01 20:08:10 -04:00
Biju Kunjummen 31a3ff61e8 GH-1916: Spring Boot Auto-configuration based Zuul Server and Proxy (#1951) 2017-06-01 15:12:42 -04:00
Spencer Gibb 3773047a06 Bump to version 1.4.0.BUILD-SNAPSHOT 2017-06-01 12:50:57 -06:00
pahli a1d089038d make property injection optional 2017-05-31 23:55:17 +02:00
Ryan Baxter 562e604cee Merge pull request #2002 from ryanjbaxter/deprecate-RetryableStatusCodeException
Deprecates RetryableStatusCodeException
2017-05-31 16:33:58 -04:00
pahli 71c906b00b add private accessor 2017-05-31 22:33:10 +02:00
pahli 6d22a407cc use @ConfigurationProperties 2017-05-31 22:25:51 +02:00
pahli bdfbe3a87e make secure ports for DefaultServerIntrospector configurable 2017-05-31 22:09:35 +02:00
Ryan Baxter cb3613db71 Fixing typo 2017-05-31 14:35:38 -04:00
Ryan Baxter 7dbfe15d1a Deprecates RetryableStatusCodeException in favor of the same class in s-c-commons 2017-05-31 14:22:15 -04:00
Rico Pahlisch ccf8f86c8d use only default https ports for https check
spring boot uses all high ports and there are some trouble if ports end with 443 (e.g. 24443, ...)
2017-05-31 12:04:09 +02:00
Spencer Gibb 7425f974cb Adds timeout test.
see gh-1950
2017-05-25 14:51:58 -06:00
Spencer Gibb 58e62ebd06 Bumping versions to 1.3.2.BUILD-SNAPSHOT after release 2017-05-22 12:18:04 -06:00
Spencer Gibb 0de72d2dad Going back to snapshots 2017-05-22 12:18:03 -06:00
Spencer Gibb 7898ad772b Update SNAPSHOT to 1.3.1.RELEASE 2017-05-22 12:09:31 -06:00
Spencer Gibb 25a1881127 Ignore feign.hystrix.enabled.
Let SecurityContextConcurrencyStrategy be installed as the hystrix
concurrency strategy regardless of if feign's hystrix support is installed.

fixes gh-1969
2017-05-22 09:47:49 -06:00
Spencer Gibb 34350cb7ec Merge branch 'strelok1-master' 2017-05-19 19:00:24 -06:00
Roman Rodov e130ec03ad Add management.context-path to status & health url
fixes gh-1795
2017-05-19 18:59:37 -06:00
Spencer Gibb 0cd544b4ce Merge branch 'lalib-gh-1720' 2017-05-19 18:22:26 -06:00
balp 6362be8196 Adds ability to customize simple host conn pool
The timeToLive and timeUnit parameters added to configure connection pool.

Fixes gh-1720
2017-05-19 18:22:04 -06:00
Biju Kunjummen aa4d9d62bc Test for validating that local service instance resolves for hystrix metrics (#1922) 2017-05-19 18:40:49 -04:00
Spencer Gibb cd44a52084 Update feign to 9.5.0
fixes gh-1956
2017-05-16 21:07:19 -06:00
Spencer Gibb 98fd3c7379 Upgrade Hystrix to v1.5.12
fixes gh-1955
2017-05-16 19:52:41 -06:00
Luke Tornquist 73b5609e63 Allow easier subclassing of SimpleRouteLocator
Fixes gh-1598
2017-05-15 14:48:15 -04:00
Jin Zhang fe06370aa2 correct comments in FeignClient, SendResponseFilter, RibbonRoutingFilter (#1935) 2017-05-15 13:44:14 -04:00
Spencer Gibb a1b1a7d702 Moves EurekaHealthIndicator creation to auto config.
Fixes a race condition introduced in 1.3.0

fixes gh-1933
2017-05-15 10:04:31 -06:00
Ryan Baxter ccdf0d9f95 Removing duplicate and incorrect dependency. Fixes #1941. 2017-05-15 10:48:39 -04:00
Ryan Baxter 287ce9c0ff Merge pull request #1877 from yongsungyoon/master
Add @Component for Feign Hystrix Fallback example in doc
2017-05-11 11:40:39 -04:00
윤용성(Yongsung Yoon)/개발혁신팀/SKP b8ed846247 Update docs to indicate the need of declaring feign's fallback implementation as a Spring bean 2017-05-11 11:29:59 +09:00
Ryan Baxter 2a9a62d0f6 Merge pull request #1926 from TheSentinel454/issues_1676_wildcard_type_support
Add support for Wildcard Types in Feign Client.  Fixes gh-1676
2017-05-10 16:04:38 -04:00
Dave Syer 377bccf12a Update to latest cloud dependencies 2017-05-10 11:37:53 +01:00
Dave Syer 030a8e7111 Remove Lombok from hystrix stream 2017-05-10 11:09:15 +01:00
Luke Tornquist 189d926c6a Correct import order 2017-05-09 16:10:08 -04:00
Luke Tornquist d993fe217c Format using provided Eclipse formatter. 2017-05-09 13:20:01 -04:00
Luke Tornquist 8983c9bce1 Add support for Wildcard Types in Feign Client. Fixes gh-1676 2017-05-09 13:16:12 -04:00
Dave Syer ced1f36d19 bump spring cloud build version 2017-05-05 17:16:18 +01:00
Ryan Baxter e698fa664b Cherry Picking #1910 2017-05-04 11:55:26 -04:00
Ryan Baxter ab2cf59bf6 Merge pull request #1907 from spccold/zuul-filter-bugfix
change HttpServletResponse to HttpServletRequest
2017-05-03 09:53:17 -04:00
Ryan Baxter a315ac9e54 Merge pull request #1906 from lowzj/master-removeUnusedImport
polish FeignLoadBalancer, remove unused import statement
2017-05-02 10:05:06 -04:00
wuwo c6347ec9c8 change HttpServletResponse to HttpServletRequest 2017-05-02 17:26:42 +08:00
lowzj ac8379af29 polish FeignLoadBalancer, remove unused import statement 2017-05-02 17:13:41 +08:00
Dave Syer 69622e909b Fix typo in test method name 2017-04-27 10:29:59 +01:00
Biju Kunjummen 910167de09 Ribbon based on Eureka disabled, if Eureka client is not enabled (#1892)
Ribbon client configuration based on Eureka is disabled if eureka.client.enabed is set to false.

Fixes gh-335
2017-04-26 14:45:35 -04:00
Ryan Baxter 29fb58de84 Merge pull request #1884 from ryanjbaxter/pass-config-in-retryhandler
Pass config in retry handler
2017-04-24 20:05:29 -04:00
Ryan Baxter dbf17dea18 Pass config in retry handler. Fixes #1879. 2017-04-24 14:09:04 -04:00
Ryan Baxter 7fcbc0fd5a Merge pull request #1880 from daniellavoie/mvn-3.5.0
Update mvnw to 3.5.0
2017-04-24 10:56:37 -04:00
Daniel Lavoie 32c79b7a91 Update mvnw to 3.5.0
Fixes gh-1847
2017-04-23 16:27:06 +02:00
Ryan Baxter 1647fc3017 Merge pull request #1876 from ryanjbaxter/upgrade-hystrix-1.5.11
Upgrade Hystrix to 1.5.11 and apply fix to hystrixcommand.js
2017-04-20 16:30:09 -04:00
Ryan Baxter 17f43fd138 Fixes #1855 2017-04-20 15:12:57 -04:00
Ryan Baxter a708ceefe8 Merge pull request #1870 from ryanjbaxter/force-eurekaclient-creation-on-refresh
Make sure the EurekaClient bean is recreated after refresh.
2017-04-20 14:53:03 -04:00
Ryan Baxter 1f12902f78 Make sure the EurekaClient bean is recreated after refresh. Fixes #1857. 2017-04-19 11:29:33 -04:00
Spencer Gibb cebbcf592e polish 2017-04-18 12:23:56 -06:00
Ryan Baxter 0c7994c502 We no longer use travis so removing configuration
Fixes #1846.
2017-04-11 13:45:57 -04:00
Ryan Baxter d412df5883 Merge pull request #1808 from gonzalad/1807
Add X-Forwarded-Port header in Zuul
2017-04-10 20:17:02 -04:00
Ryan Baxter cca3744396 Bumping versions to 1.3.1.BUILD-SNAPSHOT after release 2017-04-07 06:12:17 -04:00
Ryan Baxter c98c50291c Going back to snapshots 2017-04-07 06:12:16 -04:00
Ryan Baxter e9035ab3e0 Update SNAPSHOT to 1.3.0.RELEASE 2017-04-07 06:10:01 -04:00
Ryan Baxter d8eb4432b9 Updating mvnw script 2017-04-07 06:04:35 -04:00
Ryan Baxter 8dcf544079 Merge pull request #1836 from ryanjbaxter/response-code-retry
Add the ability for ribbon clients to specify response status codes they would like to retry
2017-04-05 14:01:15 -04:00
Ryan Baxter 98cbf2f47f Merge pull request #1840 from ryanjbaxter/document-hystrix-timeout
Document Hystrix timeout configuration with Ribbon clients
2017-04-05 13:46:50 -04:00
Ryan Baxter e041d87b5a Document Hystrix timeout configuration with Ribbon clients. Fixes #1828 2017-04-05 13:44:42 -04:00
Spencer Gibb 5440aca212 Merge pull request #1829 from Aloren/feature/exceptions-monitoring
* feature/exceptions-monitoring:
  Implements zuul CounterFactory
2017-04-05 11:33:40 -06:00
Anastasiia Smirnova 834980eae7 Implements zuul CounterFactory
CounterFactory is used by ZuulException and was previously initialzed
with a dummy impl.

This adds a Spring Boot CounterService based implementation.

Also adds the ability to implement a zuul TracerFactory.
2017-04-05 11:33:29 -06:00
David-V2 a61822f9c9 Added Bad Request count to Hystrix dashboard (#1839)
* brainlessly apply this : https://github.com/Netflix/Hystrix/commit/892f6a781e67f153b0bf2f7387c87ca0721955c2
2017-04-05 12:00:14 -04:00
Ryan Baxter 6290859f81 Add the ability for ribbon clients to specify response status codes they would like to retry. Fixes #1540. 2017-04-04 15:34:08 -04:00
Spencer Gibb 62408af5ea Don't set chunked encoding with GET.
fixes gh-1824
2017-04-03 14:04:07 -06:00
Ryan Baxter 711a8f0aae Merge pull request #1817 from ryanjbaxter/use-valid-hostnames-in-feign-tests
Use valid hostnames in feign tests
2017-03-28 14:31:41 -04:00
Ryan Baxter 382988a06a Fixed hostname 2017-03-28 09:48:41 -04:00
Ryan Baxter 88431bdbea Use valid hostnames in Feign retry tests 2017-03-28 09:46:16 -04:00
Ryan Baxter 62cb0ae28f Change /routes endpoint to use EndpointMvcAdapter and AbstractEndpoint (#1802)
Change /routes endpoint to use EndpointMvcAdapter and AbstractEndpoint.  

Fixes #1797
2017-03-24 16:27:41 -04:00
gonzalad 31f5a2bb73 1807: Add X-Forwarded-Port header in Zuul
Added more unit tests
2017-03-24 19:20:06 +01:00
gonzalad cb90cd9929 Add X-Forwarded-Port header in Zuul
Add X-Forwarded-Port header in Zuul PreDecorationFilter
even with X-Forwarded-Host header being absent.
2017-03-24 16:28:09 +01:00
Ryan Baxter d402587ab2 Merge pull request #1805 from ryanjbaxter/url-protocal-el-syntax
Fixes the addition of http with EL syntax
2017-03-23 16:12:04 -04:00
Ryan Baxter 02226f938e Fixes the addition of http with EL syntax. Fixes #1796 2017-03-23 15:00:55 -04:00
Spencer Gibb 37309218ec Links to sample zuul filters.
For more advanced filter examples.

fixes gh-1748
2017-03-23 12:40:41 -06:00
Michael Tecourt 03740f1369 Make EurekaDiscoveryClient.EurekaServiceInstance constructor public (#1794)
Make EurekaDiscoveryClient.EurekaServiceInstance constructor public

fixes gh-1792
2017-03-23 14:23:25 -04:00
Ryan Baxter d3314447a8 Fixed logging statement. Fixes #1798 2017-03-20 10:58:50 -04:00
Spencer Gibb a8b170f51c Upgrade eureka to v1.6.2
fixes gh-1786
2017-03-16 14:47:08 -06:00
Ryan Baxter a669a309f6 Revert "Version changes for Dalston.RC1"
This reverts commit a628ba1b8f.
2017-03-16 13:31:53 -04:00
Ryan Baxter a628ba1b8f Version changes for Dalston.RC1 2017-03-16 11:59:46 -04:00
Ryan Baxter 5bf87c4da1 Merge pull request #1749 from bijukunjummen/GH-1334
GH-1334: Support for eagerly initializing zuul Ribbon contexts
2017-03-15 11:46:34 -04:00
Spencer Gibb b7c5b24d43 comment out failing test 2017-03-14 20:06:07 -06:00
Spencer Gibb 44bb4f2cc3 Move load balancer construction test 2017-03-14 19:01:43 -06:00
Spencer Gibb 55a58eaf30 jmx=false for tests 2017-03-14 18:49:12 -06:00
Spencer Gibb 5d3ca843b6 jmx=false for tests 2017-03-14 18:25:44 -06:00
Spencer Gibb ecf12aa0e7 Makes ServerListUpdater respect configuration.
By default, `LoadBalancerBuilder` built a load-balancer with a `PollingServerListUpdater`. This change introduces a `ServerListUpdater` bean which creates a `PollingServerListUpdater(IClientConfig)` so that `ribbon.ServerListRefreshInterval` is respected.

 fixes gh-1304
2017-03-14 12:24:38 -06:00
Biju Kunjummen 427770a6d0 Added documentation, changed property name from serviceIds to clienNames, clients 2017-03-14 04:48:43 -05:00
Biju Kunjummen 2d9604918a Changed property name from zuul.ribbon.eager-load to zuul.ribbon.eager-load.enabled 2017-03-14 04:36:10 -05:00
Biju Kunjummen 567a6c4a9f Support for eagerly loading Ribbon clients through configuration properties 2017-03-14 04:36:10 -05:00
Biju Kunjummen 31b617b10f Fixed formatting issues, added more tests 2017-03-14 04:36:10 -05:00
Biju Kunjummen a6fe648a41 GH-1334: Support for eagerly initializing zuul Ribbon named client contexts 2017-03-14 04:36:10 -05:00
Ryan Baxter 66163bee2b Merge pull request #1771 from jh409/patch-1
Update docs as @Autowired fails with many beans
2017-03-13 12:07:03 -04:00
Jack Harris db7d352c97 Update docs as @Autowired fails with many beans
Fix a suspected typo. @Autowired throws a `NoUniqueBeanDefinitionException` if there are multiple beans of the same type without a primary, but the documentation said multiple beans 'cause `@Autowired` to work', rather than 'not work'.
2017-03-13 00:19:57 +00:00
Spencer Gibb cda92f4ba3 Upgrade Hystrix to v1.5.10
fixes gh-1766
2017-03-08 19:57:04 -07:00
Ryan Baxter 133613aeb1 Merging #1762 into master 2017-03-08 10:51:33 -05:00
Spencer Gibb 15e01dfe5d Don't remove content-length header in feign ribbon.
Previously, when feign used the Ribbon RestClient, an exception was thrown if Content-Length was set. Over time, feign has moved away from RestClient, yet removing the header still happened.

 Fixes gh-1705
2017-03-06 20:11:03 -07:00
Spencer Gibb 6df7190472 Update docs for REQUEST_URI_KEY in zuul 2017-03-03 15:19:10 -07:00
Spencer Gibb 5212d138f8 Upgrade okhttp3 to 3.6.0
Also add com.squareup.okhttp3:logging-interceptor dependency management.

fixes gh-1752
2017-03-03 09:01:11 -07:00
Bertrand Renuart 465582d20c Make sure MetricsRestTemplateAspectConfiguration is applied in time.
`MetricsRestTemplateConfiguration` creates a BeanPostProcessor used to configure RestTemplate instances when added to the application context. It should be made @ConditionalOnClass(RestTemplate) instead of @ConditionalOnBean(RestTemplate) since it should be present *before* any RestTemplate bean instance is created. (#1747)

fixes gh-1153
2017-03-02 10:32:04 -05:00
Bertrand Renuart fae60e9fae Remove unused registry private field. (#1746)
fixes gh-1745
2017-03-02 10:14:31 -05:00
Ryan Baxter c89645e069 Merge pull request #1743 from ryanjbaxter/disable-hystrix-feign
Disable Hystrix support when using Feign by default
2017-03-01 16:33:34 -05:00
Ryan Baxter 6465cf4f3e Added warning to the docs about the change in default behavior. 2017-03-01 13:15:11 -05:00
Ryan Baxter 54c052c5eb Disable Hystrix support when using Feign by default. Fixes #1277. 2017-03-01 11:35:08 -05:00
nick-pww 465b8273eb Clarify @FeignClient alias creation
fixes gh-1073
2017-02-28 15:02:15 -05:00
windinn 129e0ce4b5 Use prefer-ip-address in statusPageUrlPath
prefer-ip-address does not work in statusPageUrlPath when management port != server port

This change gets `prefer-ip-address` via a relaxed property resolver to set the value during the creation of the `EurekaInstanceConfigBean`.

fixes gh-1724
2017-02-28 13:07:43 -05:00
Spencer Gibb 309b280d0d Log warning if local instance fails.
If DiscoveryClient.getLocalServiceInstance() throws an exception (ie consul isn't available), log a warning and continue. This is useful for dev type environments.

fixes gh-1603
2017-02-28 10:26:15 -07:00
Biju Kunjummen 99d9b17a2d Moves EurekaServerConfiguration to auto-configuration
Eureka ServerConfiguration is now auto-configuration based, instead of being explicitly specified via configuration pulled in through @EnableEurekaServer. This way specific beans can be conditionally overridden, configuration can be re-ordered.
Supports PeerEurekaNodes bean to be overridden by the user

Fixes gh-1717
2017-02-28 12:00:29 -05:00
Ryan Baxter 208a741823 Merge pull request #1732 from utsman/master
Refactor ZoneUtils class
2017-02-27 09:40:49 -05:00
utsman c953d59b5a Refactor ZoneUtils class 2017-02-26 01:45:31 +09:00
Spencer Gibb c0fefc4d1a Update feign to version 9.4.0
fixes gh-1731
2017-02-24 15:05:42 -07:00
Ryan Baxter 0fcc979fe1 Merge pull request #1730 from utsman/rm-dupl-check
Remove duplicate null check
2017-02-24 14:05:26 -05:00
utsman cb9d9bd8a1 Remove duplicate null check 2017-02-25 03:13:24 +09:00
Ryan Baxter 1d5a68c9e7 Merge pull request #1728 from utsman/fix-typo
Fixed typo
2017-02-24 10:07:23 -05:00
utsman 8e3f45b899 Fixed typo 2017-02-24 23:30:39 +09:00
Ryan Baxter ea87b9bfc2 Merge branch 'master' of https://github.com/spring-cloud/spring-cloud-netflix 2017-02-20 15:56:50 -05:00
Ryan Baxter df91ff7735 using apache commons logger 2017-02-20 15:56:37 -05:00
Spencer Gibb 652fe97a1b fix logger 2017-02-20 13:54:54 -07:00
Ryan Baxter 06565765b8 removing unused imports 2017-02-20 14:25:50 -05:00
Ryan Baxter fc9c7dcdfc clearing context during setup 2017-02-20 13:39:06 -05:00
Ryan Baxter 72f1ce9096 added logging to test 2017-02-20 11:18:31 -05:00
Ryan Baxter 0abf69dc1d Merge pull request #1709 from ryanjbaxter/make-feign-retry-optional
Make the use of Spring Retry optional when using Feign
2017-02-17 18:25:28 -05:00
Ryan Baxter a4f30ebf1d Make the use of Spring Retry optional when using Feign. Fixes #1708 2017-02-17 11:12:12 -05:00
Tomasz Juchniewicz ed137a93ce Fix encoding issue in SpringEncoder for binary data (#1699)
* Fix encoding issue in SpringEncoder for binary data

Fixes gh-1698

* Fix encoding issue in SpringEncoder for binary data

Fixes gh-1698

* Fix encoding issue in SpringEncoder for binary data

Add unit test.

Fixes gh-1698
2017-02-16 14:52:15 -05:00
Lars e73ee5f690 Refactors zuul tests for better readability. (#1686)
* Refactors tests for better readability. Polishes imports. Removes hostname and port from TestRestTemplate-calls. Introduces @LocalServerPort
2017-02-16 12:48:48 -05:00
Ryan Baxter de886d6c3d Merge branch 'master' of https://github.com/spring-cloud/spring-cloud-netflix 2017-02-15 20:09:27 -05:00
Ryan Baxter 050d5f898f Porting changes that remove RetryTemplate from FeignLoadBalancer constructor from 1.2.x branch 2017-02-15 20:09:15 -05:00
Spencer Gibb e3341a19cd Add @FeignClient(primary)
Adds the ability to turn off Spring Cloud Netflix making the feign instances as primary.

fixes gh-1016
2017-02-15 17:46:12 -07:00
Spencer Gibb 8f5e3ad7c0 polish 2017-02-15 17:30:42 -07:00
Ryan Baxter 384c9b9725 Merge branch 'master' of https://github.com/spring-cloud/spring-cloud-netflix 2017-02-15 13:43:02 -05:00
Ryan Baxter 981fa5db9a Merging PR #1689 into master 2017-02-15 13:42:46 -05:00
Ryan Baxter c417a9a55e Merge pull request #1695 from ryanjbaxter/uri-equal-to-empty-string
Adds a check for an empty URI in updateToHttpsIfNeeded
2017-02-14 15:33:12 -05:00
Ryan Baxter 52ab4d3ecd Adds a check for an empty URI in updateToHttpsIfNeeded. Fixes #1690. 2017-02-14 14:50:55 -05:00
Ryan Baxter 62bc2758eb Merge pull request #1694 from ryanjbaxter/default-zuul-fallbackprovider
Add the ability to provide a default ZuulFallbackProvider
2017-02-14 14:42:09 -05:00
Ryan Baxter 731c8bf0fd Add the ability to provide a default ZuulFallbackProvider. Fixes #1506 2017-02-14 13:59:25 -05:00
Erik Kringen d78da7b994 Add support for Feign.SetterFactory (#1673)
Fixes gh-1414
2017-02-10 13:37:17 -05:00
Roman Terentiev c795bd16a7 #1203 Improve filter performance by using only one instance of AllEncompassingFormHttpMessageConverter (#1681) 2017-02-10 12:25:27 -05:00
Spencer Gibb 7237275f5d polish 2017-02-10 10:14:12 -07:00
Johannes Edmeier 3e7b196950 Close response on errors writing to the outputStream (#1372)
Currently all errors during writing to the outputStream are ignored. So the
response is read to the end. This is a problem when the response is
infite or very long running (`hystrix.stream` for example) and results in the
socket never being closed and eating up threads (on zuul and the proxied
backend). With this commit the errors during writing are not ignored resulting
in closing the response.
2017-02-10 12:12:26 -05:00
Ryan Baxter 919c8fa72a Merge pull request #1678 from lowzj/master-RequestParam
fix gh-1672, support @RequestParam without a value
2017-02-09 19:38:00 -05:00
Ryan Baxter bcb97bce1c Merging PR #1680 into master 2017-02-09 15:12:19 -05:00
Ryan Baxter 64d8883210 Adding missing commit from #1457 2017-02-09 14:29:40 -05:00
Ryan Baxter 0af99769ad Adding missing commit from PR #1457 2017-02-09 14:29:12 -05:00
lowzj c8fded80d5 fix gh-1672, support @RequestParam without a value 2017-02-09 13:58:30 +08:00
Spencer Gibb c6a3157fd8 Adds information on RibbonRoutingFilter HTTP Clients 2017-02-08 20:55:08 -07:00
Ben Klein 8afb02cf03 Zuul Developer Guide: minor edits 2017-02-08 21:20:16 -06:00
Spencer Gibb 675e4c1031 Adds example zuul filters. 2017-02-08 19:47:57 -07:00
Spencer Gibb d21d03b738 initial zuul developer guide 2017-02-08 18:53:21 -07:00
Spencer Gibb ff70c08fb9 More zuul constant and javadoc updates 2017-02-08 18:52:41 -07:00
Spencer Gibb 506a9a44af Move many zuul strings to FilterConstants.
Also adds minimal javadoc to classes.

See gh-1435
2017-02-08 12:09:53 -07:00
Spencer Gibb 10e8634d38 add additional conditional on class in case of webflux 2017-02-08 10:29:32 -07:00
Spencer Gibb a1577127b7 hopefully fix flaky test 2017-02-06 21:11:25 -07:00
Spencer Gibb 5a1f1d2c64 Remove unused import. 2017-02-06 20:20:22 -07:00
Ryan Baxter a915651797 Merge pull request #1670 from bijukunjummen/gh-1641
gh-1641: Fixed instructions for configuring EurekaIntanceConfig in AWS
2017-02-06 11:20:11 -05:00
Biju Kunjummen 133d054fc8 Fixed instructions for configuring EurekaIntanceConfig in AWS 2017-02-03 22:50:30 -08:00
Eloi c87f6ca983 delegate to the library httpclient parse the content type (#1664)
Fixes gh-1663
2017-02-02 11:05:01 -05:00
Spencer Gibb a2e1836e24 Merge pull request #1656 from sbley/master
* pull1656:
  Separate adding X-ForwardedProto header
2017-02-01 15:33:13 -07:00
Stefan Bley 53649f6020 Separate adding X-ForwardedProto header
Add X-Forwarded-Proto header in Zuul PreDecorationFilter even with X-Forwarded-Host header being absent
2017-02-01 15:33:00 -07:00
Spencer Gibb 601e220411 implement Registration.getServiceId() 2017-02-01 13:45:05 -07:00
Ryan Baxter ed6c4b1925 Revert "Version changes for 1.3.0.M1"
This reverts commit 46ee0ce161.
2017-01-27 14:25:34 -05:00
Ryan Baxter 46ee0ce161 Version changes for 1.3.0.M1 2017-01-27 13:02:13 -05:00
Ryan Baxter 71b3110acf Updating config and stream versions for Dalston 2017-01-27 12:01:33 -05:00
Eloi e6f2db70ef Maintain content type for data parts in multipart requests (#1614)
maintain multipart content type for data parts

Fixes gh-1581
2017-01-27 10:26:11 -05:00
Matt Sicker 65829c437c Fix docs about @Configuration with @FeignClient (#1650)
As discovered in #1643, using `@Configuration` on a custom configuration with `@FeignClient` is unnecessary and creates extra work for exclusion from a `@ComponentScan`. This updates the docs to make this explicit.
2017-01-26 11:40:58 -05:00
Spencer Gibb f27defbdf4 Make sure service-registry actuator is activated. 2017-01-25 15:15:46 -07:00
Spencer Gibb 41bf8eadfd Rename RESET_OVERRIDE to CANCEL_OVERRIDE 2017-01-25 14:23:34 -07:00
Spencer Gibb d76bd84466 Handle content-type with charset.
A new version of httpclient introduced by boot changed the behaviour that previously allowed the charset. It now has to be parsed out and passed to the apache `ContentType` object explicitly.

fixes gh-1649
2017-01-25 11:47:11 -07:00
Ryan Baxter ca244c5dbb Merge pull request #1637 from tmack8001/zuul-route-constructor-custom-sensitive-headers
ZuulProperties.ZuulRoute consturctor missing explicitly setting of customSensitiveHeaders boolean
2017-01-23 09:38:44 -05:00
Trevor Mack ff0ac568f4 addressing issues/1636 ZuulProperties.ZuulRoute consturctor missing explicitly setting of customSensitiveHeaders boolean when provided with sensitiveHeaders 2017-01-21 23:12:45 -08:00
Spencer Gibb 5d0b2fae86 Ignore misbehaving test 2017-01-20 13:42:04 -07:00
Spencer Gibb f8a8b3cf31 Fix broken test logic, rather than >= should be <= 2017-01-20 13:41:49 -07:00
Jin Zhang 88396411f7 improve efficiency of SendResponseFilter#addResponseHeaders() (#1622) 2017-01-20 15:08:38 -05:00
Ryan Baxter 42931f9300 Merge pull request #1626 from ryanjbaxter/apply-formatter-to-collections-1526
Remove check that does not apply Formatters to Collections
2017-01-17 11:44:26 -05:00
Ryan Baxter a5bd17c0c0 Remove check that does not apply Formatters to Collection in Feign Client parameters. Fixes #1526 2017-01-17 11:16:25 -05:00
Spencer Gibb a5e5f81d88 Changes equals assertion to greaterThanOrEquals.
Hopefully this fixes flaky tests.
2017-01-16 11:41:45 -07:00
Spencer Gibb 66da2d8f48 Merge pull request #1586 from Infrag/master
* pull1586:
  Adds composed @RequestMapping annotations support
2017-01-13 14:03:52 -07:00
Ondřej Božek b619ee00bf Adds composed @RequestMapping annotations support
Fixed short circuit condition to correctly detect @RequestMapping shortcuts (@GetMapping, @PostMaping etc.)

Fixes gh-1201
2017-01-13 14:03:37 -07:00
Johannes Edmeier 899d8bc24d Add CompositeRouteLocator (#1448)
Configure a CompositeRouteLocator by default so it is easier to compose
multiple RouteLocators.
2017-01-12 16:38:01 -05:00
Spencer Gibb bf586ba2cb polish 2017-01-12 14:32:57 -07:00
Spencer Gibb 22248dd9e9 Merge pull request #1591 from tranhungt/gzip_decompression
* gzip_decompression:
  Disables httpclient auto decompression of encoded data
2017-01-12 14:31:32 -07:00
Hung Tran 6dfc7a815b Disables httpclient auto decompression of encoded data
Applies only to SimpleHostRoutingFilter.

fixes gh-1480
2017-01-12 14:31:22 -07:00
Arnaud Brunet b27ba0434a More robust instantiation in SpringClientFactory
SpringClientFactory returns null if the class is not aware of IClientConfig.

This change catches the exception when trying the constructor with IClientConfig so classes that aren't aware of IClientConfig can be created.

Fixes gh-1608
2017-01-12 16:15:31 -05:00
Spencer Gibb 3b41b5e15f Remove mocks for application event listeners.
fixes gh-1616
2017-01-12 13:39:46 -07:00
Ryan Baxter 8029deb1e4 Merging PR #1457 into master. 2017-01-10 12:52:36 -05:00
Ryan Baxter 5ee9990d93 Upgrade Ribbon to 2.2.2
Fixes gh-1441
2017-01-09 23:38:06 -05:00
Spencer Gibb 8a9c5fe4a1 Update fixes for hystrix stream test 2017-01-09 21:11:31 -07:00
Abhijit Sarkar 45d769b05b Support header map and query map (#1361)
Adds support for Map types in feign for `@RequestHeader` and `@RequestParam`.

Fixes gh-1360
2017-01-09 22:45:03 -05:00
Rob Worsnop af032476a8 Modified RibbonAutoConfiguration to support AsyncLoadBalancerInterceptor (#1507) 2017-01-09 22:02:10 -05:00
Spencer Gibb c52309ad59 Update to spring-cloud-build 1.3.1.BUILD-SNAPSHOT 2017-01-09 17:22:08 -07:00
Spencer Gibb 8526371d25 Merge pull request #1451 from madgnome/#1341-single-deferred-result
* #1341-single-deferred-result:
  #1341 SingleDeferredResult should uses RESULT_NONE from DeferredResult instead of having its own empty result.
2017-01-09 16:44:04 -07:00
Julien Hoarau 7f4a6fd94c #1341 SingleDeferredResult should uses RESULT_NONE from DeferredResult instead of having its own empty result. 2017-01-09 16:43:40 -07:00
Spencer Gibb 6fc5d1aea3 Merge pull request #1485 from eacdy/master
* pull1485:
  fix spell error
2017-01-09 16:11:40 -07:00
周立 1860b26491 fix spell error
fix spell error
2017-01-09 16:11:30 -07:00
Thibaud Lepretre fccbe1aff4 Create sidecar.hostname and sidecar.ipAddress (#1561)
In addition to `eureka.instance.hostname` property you can now defined `sidecar.hostname` in order to set different hostname from application and its sidecar.
However if `eureka.instance.hostname` is present it will override `sidecar.hostname`.

Fixes #981
2017-01-09 17:13:36 -05:00
Bertrand Renuart fa45a9f3a4 Use a fixed size buffer for the copy operation (#1538)
Set the default buffer size to 8192 to match Tomcat and Jetty defaults
Use a buffer per worker thread and use it for subsequent requests
2017-01-09 14:38:45 -05:00
Biju Kunjummen 98cd592f3e Fix null instanceinfo on EurekaInstanceRenewedEvent (#1574)
Fixes gh-1546
2017-01-09 12:41:23 -05:00
Spencer Gibb 64b81ff5a4 Update hystrix to 1.5.9
fixes gh-1600
2017-01-09 10:11:39 -07:00
Ryan Baxter f3deb04521 Pass along client config in RibbonCommand.run (#1573)
Pass along client config in RibbonCommand.run.  

Fixes gh-1530
2017-01-09 11:55:05 -05:00
Ryan Baxter ab454f1e1b Make FeignClientRegistrar implement EnvironmentAware (#1569)
Make FeignClientRegistrar implement EnvironmentAware to inject the environment.  

Fixes gh-1568
2017-01-09 11:06:51 -05:00
Jacques-Etienne Beaudet bd6d732fc7 Fix potential decoding and reencoding issues in query params when using SimpleHostRoutingFilter (#1464)
Some webserver are picky with the encoding (uWSGI for example) and the current code behavior of decoding and reencoding the query string can lead to valid requests
rejected by the backend after the modifications in Zuul. This commit adds an optional parameter to force the original encoding of the query string in SimpleHostRoutingFilter.

Fixes gh-971
2017-01-09 10:53:16 -05:00
Ryan Baxter 4eac8a5a47 Merge pull request #1599 from ahmednasir91/upgrade-hystrix-157
Fixes #1440
2017-01-09 07:04:21 -05:00
Ahmed Nasir 82dd2ebd05 Fixes #1440 2017-01-09 14:20:31 +05:00
Ryan Baxter 1cd194a1e1 Merge pull request #1578 from rahmanusta/patch-1
Update spring-cloud-netflix.adoc
2016-12-27 19:50:08 -05:00
Rahman Usta f26a5554d5 Update spring-cloud-netflix.adoc
- Remove unintended white-space
 - Specify imagesdir
2016-12-26 16:03:41 +02:00
Spencer Gibb c380039b3e Add test to make sure eureka.instance.*virtualHostName can be set properly. 2016-12-16 14:00:31 -07:00
Bart Blommaerts 9125f54897 Tiny grammatical fix (#1552) 2016-12-15 11:29:14 -05:00
Spencer Gibb 08fe5a89e9 Move SendErrorFilter to type error.
This will clear up empty 200 responses.

fixes gh-1123
2016-12-12 15:01:32 -07:00
Ryan Baxter 2e46bdf9d2 Porting #1450 for Dalston release 2016-12-12 15:09:16 -05:00
Spencer Gibb 18b9c46a8b update eureka config first bootstrap for service registry 2016-12-12 10:43:05 -07:00
Dave Syer a99e23f9fc Update to Boot 1.5.0 2016-12-12 12:31:32 +00:00
Spencer Gibb 8b089222bd make external configurations optional 2016-12-08 13:47:19 -07:00
Spencer Gibb 5b25c9ad1a Only auto-register if conditions are correct.
s-c-commons creates an AutoServiceRegistrationProperties bean if enabled.

This commit only auto registers with eureka if that bean is available.
2016-12-08 12:27:10 -07:00
Spencer Gibb ec80797694 avoid potential NPE 2016-12-08 12:27:10 -07:00
Bertrand Renuart 8b7d6848b3 use com.netflix.loadbalancer.DummyPing instead of NoopPing
use `com.netflix.loadbalancer.DummyPing` instead of `NoopPing`

This eliminates the startup of another theadpool.

fixes gh-1504
2016-12-05 13:26:05 -05:00
Ryan Baxter edb14e2a6b Merge pull request #1528 from ryanjbaxter/feign-url-protocol-1522
Don’t add URL protocol when url is set using EL
2016-12-05 13:14:58 -05:00
Ryan Baxter 6a82644153 Don’t add URL protocol when url is set using EL. Fixes #1522. 2016-12-05 12:48:01 -05:00
Ryan Baxter ac3401bc4f Fixed import ordering 2016-12-01 20:28:29 -05:00
Ryan Baxter 9da71b1655 Removed unnecessary null check and fixed imports 2016-12-01 20:28:28 -05:00
Ryan Baxter ed31004aa8 Uses configuration from IConfigClient reference to always use the most up to date configuration. Fixes #1481. 2016-12-01 20:28:28 -05:00
Ryan Baxter fe50674c8e Merge pull request #1520 from ryanjbaxter/eureka-client-zone-doc-1327
Document Eureka client zone config
2016-12-01 19:15:38 -05:00
Spencer Gibb 1fe1ea58f6 Don't override values with defaults.
Defaults were set too late and were overriding values set in the environment.

Specifically, spring.application.name took precedence over eureka.instance.appname.

 fixes gh-1516
2016-11-30 11:55:57 -07:00
Spencer Gibb b3f1a30027 Move initalizing appname, etc.. to afterPropertiesSet 2016-11-30 11:55:24 -07:00
Spencer Gibb 111a95d262 Fix Eureka Ribbon Auto Config conditions.
Specifically, add `@ConditionalOnBean(EurekaClient.class)`.

Fixes an issue where eureka and ribbon are on the classpath, eureka.client.enabled=false which would cause a bean not found exception.

fixes gh-1511

(cherry picked from commit d1f8c03)
2016-11-29 14:56:55 -07:00
Ryan Baxter 35528a7199 Merge pull request #1496 from ryanjbaxter/upgrade-eureka-to-1.6.1
Upgrade Eureka to 1.6.1.  Fixes #1495.
2016-11-22 20:22:49 -05:00
Ryan Baxter 927b753262 Upgrade Eureka to 1.6.1. Fixes #1495. 2016-11-22 19:59:27 -05:00
Ryan Baxter 8359016981 Updating generated README 2016-11-22 17:14:39 -05:00
Ryan Baxter 5372fe9d6e Merge pull request #1494 from ryanjbaxter/document-feign-request-headers
Document how to use thread isolation with hystrix and feign.  Fixes #1191.
2016-11-22 17:08:05 -05:00
Ryan Baxter 8814da3b78 Document how to use thread isolation with hystrix and feign. Fixes #1191. 2016-11-22 15:39:13 -05:00
Bertrand Renuart 2d748bf63c use different bean names for Hystrix and HystrixStream features (#1470)
Fixes gh-1462
2016-11-16 09:39:48 -07:00
Ryan Baxter eff1f0d77a Fixes #1459 2016-11-14 15:30:28 -05:00
Spencer Gibb aec8ec43ef Fix broken test. 2016-11-07 11:24:52 -07:00
Spencer Gibb 93cd1e7a4a Upgrade to eureka 1.6.0
fixes gh-1345
2016-11-07 10:52:47 -07:00
Spencer Gibb ea4e5d76e0 Create EurekaServiceRegistry
Use the new ServiceRegistry interface

Creates a new EurekaRegistration object that aggregates classes needed
for a single registration.
2016-11-07 10:52:47 -07:00
Robin Stocker e6298f8107 Fix NoClassDefFoundError loading RxJavaAutoConfiguration without spring-webmvc (#1442)
RxJavaReturnValueHandlerConfig can only be loaded when both spring-web
and spring-webmvc are on the classpath, because it contains a reference
to the WebMvcConfigurerAdapter class.

But the ConditionalOnClass annotation only included the class from
spring-web. In our situation, only spring-web was on the classpath,
resulting in a NoClassDefFoundError for WebMvcConfigurerAdapter. Fix it
by adding that class to the condition as well.
2016-11-03 10:27:15 -04:00
Dave Syer 704a874e84 Add example of empty sensitive headers
Emphasises that it's a black list, and clarifies that you need to
set it to empty to get authorization and cookie headers.
2016-11-03 12:06:43 +00:00
Spencer Gibb 428ed6c88c Bump to 1.3.0.BUILD-SNAPSHOT 2016-10-27 15:37:59 -06:00
Ryan Baxter dd62879b7b Merge pull request #1430 from ryanjbaxter/zuul-hystrix-fallback-doc
Document Hystrix Fallback For Zuul
2016-10-27 15:33:52 -04:00
Ryan Baxter 14a31cd5f8 Switched media type to application/json 2016-10-27 15:09:21 -04:00
Ryan Baxter 8a7df36d88 Added route config 2016-10-27 15:02:17 -04:00
Ryan Baxter fe56d96295 Fixes #1429 2016-10-27 14:25:18 -04:00
Ryan Baxter fabf90bed4 Merge pull request #1375 from ryanjbaxter/simplify-retry-logic
Provide A Retry Policy For Ribbon Clients
2016-10-26 15:42:32 -04:00
Ryan Baxter c3a84f1b73 Merge remote-tracking branch 'Upstream/master' into simplify-retry-logic 2016-10-26 15:05:12 -04:00
Ryan Baxter 5a5a1aaa7f -Made spring-retry optional
-additional refactoring from changes in s-c-commons
2016-10-26 15:04:51 -04:00
Venil Noronha aef753a3a0 Adds Feign Logger factory interface (#1411)
Fixes gh-1363
2016-10-25 17:39:22 -04:00
Ryan Baxter 08028db982 Merge pull request #1425 from ryanjbaxter/eureka-app-name-default
Resolve spring.application.name via property resolver
2016-10-25 14:31:12 -04:00
Ryan Baxter 85b2fe37e2 Added some additional tests 2016-10-25 13:54:29 -04:00
Spencer Gibb 81959b5ef3 Use new FilterRegistrationBean package.
Breaks Spring Boot 1.3 compatibility, enables 1.5
2016-10-25 11:24:42 -06:00
Ryan Baxter 657d9bb8e8 Merge remote-tracking branch 'Upstream/master' into eureka-app-name-default 2016-10-25 13:04:35 -04:00
Ryan Baxter 2dfee97563 Resolve spring.application.name value via a property resolver instead of @Value. Fixes #1398 2016-10-25 12:56:08 -04:00
Dave Syer 7f075d11ee Allow Cors requests through to backend by default
Instead of blocking them (which is the default behaviour of a
Spring MVC controller) Cors requests will flow through the Zuul
filters by default. Users can control it by grabbing the
ZuulHandlerMapping in a @PostConstruct and injecting some
CorsConfiguration via its setCorsConfigurations() method.
2016-10-25 14:33:53 +01:00
Dave Syer a38b7b71ac Append to X-Forwarded-* headers instead of replacing them
This fixes most of the issues people encounter when there are
multiple proxies in the request. The tricky thing is that there
is another header "Forwarded" that we don't recognize, but which
backends probably do, at least some of the time (since it is
from an actual RFC). The problem is that "Forwarded" does not
contain the ports, so Spring UriComponentsBuilder cannot use it
to rewrite links to a specific port. Since we do not support it
already, this change doesn't make things any worse, but the
corner case is there still.
2016-10-25 12:56:49 +01:00
Dave Syer 139ab95438 Make configuration imports deterministic 2016-10-25 12:20:31 +01:00
Dave Syer 71ac67f9f1 Suppress some warnings 2016-10-25 12:20:21 +01:00
Dave Syer 0e1b856fc5 Extract proxy header manipulation into method 2016-10-25 07:50:38 +01:00
Ryan Baxter 0235d77932 Zuul Route Hystrix Fallback
Hystrix fallback support for Zuul routes.

Fixes gh-250.
2016-10-24 15:24:31 -04:00
Ryan Baxter 6e1ca54edc increased max retried to help with failures 2016-10-24 13:42:21 -04:00
Ryan Baxter 274213bc83 Merge pull request #1412 from ryanjbaxter/creating-feign-clients-with-builder
Document Manual Feign Client Creation
2016-10-20 16:12:15 -04:00
Ryan Baxter 5a42cb1b2b Updates based on latest version from commons 2016-10-20 15:09:52 -04:00
Ryan Baxter ee99efba36 Merge remote-tracking branch 'Upstream/master' into simplify-retry-logic 2016-10-20 14:38:37 -04:00
Ryan Baxter b16a95d0f2 Fixed typo 2016-10-20 13:40:59 -04:00
Ryan Baxter 2c8bac5666 Use client interface instead of Eureka directly 2016-10-20 13:31:41 -04:00
Spencer Gibb 8e6b643829 Use new ServletRegistrationBean package.
Breaks Spring Boot 1.3 compatibility, enables 1.5
2016-10-20 11:19:54 -06:00
Ryan Baxter cc24d00fde Fixes #1211 2016-10-19 20:06:52 -04:00
Spencer Gibb ddc1069e86 Move properties to new namespace.
Moving eureka.server.{expectedNumberOfRenewsPerMin,defaultOpenForTrafficCount} to eureka.instance.registry so relaxed binding can be used. Default to old values for backwards compatibility.
2016-10-19 12:36:15 -06:00
Ryan Baxter c9704d3c97 Updating versions for next release 2016-10-18 10:16:01 -04:00
Ryan Baxter 34775eb8db Revert "Version changes for the release"
This reverts commit 90d6490fea.
2016-10-18 10:11:03 -04:00
Ryan Baxter 90d6490fea Version changes for the release 2016-10-18 09:44:57 -04:00
Gregor Zurowski acb9e5ebdf Fix formatting (#1397)
Signed-off-by: Gregor Zurowski <gregor@zurowski.org>
2016-10-13 16:08:28 -04:00
Spencer Gibb 12fff7c97f Merge pull request #1339 from alenavorozhbieva/master
* pull1339:
  Support DELETE method body for SimpleHostRoutingFilter
2016-10-13 09:34:22 -06:00
Alena acb12ea7d4 Support DELETE method body for SimpleHostRoutingFilter
fixes gh-1329
2016-10-13 09:34:08 -06:00
Dave Syer e94c35d331 Exclude aws jars from eureka client by default
Saves about 5MB in the final executable jar, and it should be
optional for Spring Cloud users.
2016-10-13 10:03:19 +01:00
Ryan Baxter a685a72660 Merge pull request #1384 from bslota/master
#1376 Register and cancel event sending fix
2016-10-11 19:34:25 -04:00
Bartłomiej Słota 4aaa7d0555 InstanceRegistry class refactor + renew method test #1376 2016-10-11 21:46:25 +02:00
Ryan Baxter 2f09aa291b Merge pull request #1390 from ryanjbaxter/upgrade-zuul-1.3.0
Upgrade zuul 1.3.0
2016-10-11 14:53:39 -04:00
Ryan Baxter bdf2f25259 Fixes #1306 2016-10-11 14:27:47 -04:00
Ryan Baxter 5a0af27115 Merge remote-tracking branch 'Upstream/master' 2016-10-11 14:20:12 -04:00
Ryan Baxter 18c4bc060b Upgrade hystrix 1.5.6 (#1388)
Fixes gh-1374
2016-10-11 13:41:31 -04:00
Spencer Gibb d23aa67f7b Merge pull request #1370 from tiny-dancer/master
* pull1370:
  Warning for GC Overhead Limit Reached with servo.
2016-10-11 10:29:19 -06:00
mg 5a127d0700 Warning for GC Overhead Limit Reached with servo.
Fixes gh-947
2016-10-11 10:29:04 -06:00
Spencer Gibb aa160940df import order 2016-10-11 10:25:02 -06:00
Max Ishchenko c8829f2aa4 Prevent double url encoding for secure ribbon urls (#1389)
Secure ribbon urls were forced to use https scheme via UriComponentsBuilder, that was created from original uri. This transformation url encoded previously encoded url parts that were used to create builder. This was introduced in c883495.

This change fixes double url encoding using RibbonUtils.updateToHttpsIfNeeded that fixes double escaping case and corner case with '+' in url as well.

Fixes gh-1382
2016-10-11 10:19:33 -04:00
Bartłomiej Słota 0985c8f092 Merge pull request #1 from spring-cloud/master
Remove @Autowired from FeignClientFactoryBean
2016-10-11 00:09:45 +02:00
Bartłomiej Słota 279edf0665 super class method wrappers removal and test update #1376 2016-10-10 22:54:39 +02:00
Ryan Baxter 50fac81a30 Revert "Fixes #1374"
This reverts commit 99b86e7a29.
2016-10-10 15:32:20 -04:00
Ryan Baxter 99b86e7a29 Fixes #1374 2016-10-10 15:31:42 -04:00
Bartłomiej Słota 151e021ef9 formatting fixed + tests, fixes #1376 2016-10-08 00:24:35 +02:00
Bartłomiej Słota dd97b6a4b7 #1376 Register and cancel event sending fix 2016-10-07 00:11:24 +02:00
Spencer Gibb f30e8b7b46 Remove @Autowired from FeignClientFactoryBean
@Autowired causes NullPointerExceptions because of some unknown lifecycle issues.

fixes gh-1366
2016-10-06 10:19:46 -06:00
Spencer Gibb ba3923d706 Fix zuul error when there are colons in request param name.
Previously, parameter names with colons (ie foo:bar), caused an IllegalArgumentException because 41c364002f introduced UriTemplate to properly encode.

fixes gh-1193
2016-10-05 12:51:28 -06:00
Spencer Gibb 622cc83204 polish 2016-10-04 18:48:28 -06:00
bpicode 8e18c3d3b6 Add support for fallback factories in feign client annotation. (#1373)
Adds `@FeignClient.fallbackFactory` to define a `feign.hystrix.FallbackFactory`.

fixes gh-1117
2016-10-04 20:46:33 -04:00
Spencer Gibb 0adbd5662f polish 2016-10-04 18:39:07 -06:00
Ryan Baxter f00d1159c8 Merge remote-tracking branch 'Upstream/master' into simplify-retry-logic 2016-10-04 10:49:02 -04:00
Ryan Baxter 4c90ce437e Implementation of factory pattern for creating retry policies 2016-10-04 10:48:40 -04:00
Mathias Düsterhöft e109093b83 add config option to not ignore security headers (#1354)
* add config option to not ignore security headers
* fix field documentation of ignoreSecurityHeaders
* add documenation for  zuul.ignoreSecurityHeaders

fixes gh-1096
2016-10-03 23:30:17 -04:00
Ryan Baxter 4ba3411292 Clarified zuul.stripPrefix usage
Helps with the confusion seen in #1365
2016-10-03 13:44:02 -07:00
Jacques-Etienne Beaudet 8e1be54759 Support '+' in encoded query string when rebuilding the URI to https
RibbonUtils now replace the encoded space character '+' in the query string in favor of the equivalent '%20' when rewriting the URI in https. This is due to UriComponentsBuilder verifying the allowed characters in the encoded URI when building to the URI java.net class. The '+' is considered illegal but is widely used in the field. (#1367)
2016-10-03 14:35:30 -04:00
Spencer Gibb be6f2634b5 Allow OkHttpClient to be customized.
Moved common functionality to AbstractLoadBalancingClient
2016-09-23 14:09:41 -06:00
Ryan Baxter 7191e820a2 Implementing retry policy 2016-09-23 15:01:09 -04:00
Spencer Gibb 9eed249028 Allow customization of HttpClient.
Uses ribbon settings to set MaxConnTotal and MaxConnPerRoute. Also allows user to create a custom HttpClient as a constructor argument.

fixes gh-1149
2016-09-22 19:42:20 -06:00
Spencer Gibb bbff2b24d0 Put Apache Http Client and OkHttpClient in ribbon context.
This eliminates them being created for every zuul request.

Also makes properties consistent accross modules: ribbon.httpclient.*, ribbon.restclient.* and ribbon.okhttp.*

fixes gh-1150
2016-09-22 15:46:53 -06:00
Ryan Baxter b91fa734d2 Bumping versions for next release 2016-09-22 14:55:08 -04:00
Ryan Baxter 302eae6aeb Revert "version changes for Camden release"
This reverts commit 088afa6d71.
2016-09-22 14:21:39 -04:00
Ryan Baxter 088afa6d71 version changes for Camden release 2016-09-22 13:15:22 -04:00
Dave Syer 5f96c415e3 Upgrade xstream to 1.4.9
Fixes XXE vulnerability in Eureka Server (external entity processing
on by default in older versions of the library).
2016-09-22 16:23:27 +01:00
Ryan Baxter 6c9fa68d5a Translate ribbon.ReadTimeout to apache http client socket timeout. Fixes #1357. (#1358) 2016-09-21 14:03:14 -04:00
Dave Syer bc4f5a3788 Ensure version is scraped from parent pom 2016-09-20 13:34:30 +01:00
Dave Syer f094ecfd88 Allow user to set system properties to control http client in zuul
The SimpleHostRoutingFilter now accepts normal -Dhttps.proxy= and
-Dhttps.port= settings (and other things that generally work with
java.net).

Fixes gh-510
2016-09-20 12:30:49 +01:00
Marcin Grzejszczak 13268f7c63 Updating mvnw for all projects 2016-09-19 19:31:33 +02:00
Marcin Grzejszczak aa820ce347 Updating mvnw for all projects 2016-09-19 14:18:37 +02:00
Dave Syer 6310059531 Move unnecessary dependencies up a level 2016-09-19 12:02:34 +01:00
Marcin Grzejszczak 6f92859608 Updating mvnw for all projects 2016-09-19 12:46:58 +02:00
Spencer Gibb 758ff920fc Merge pull request #1351 from VanRoy/improve-formbodywrapperfilter-test
* improve-formbodywrapperfilter-test:
  Add test on FormBodyWrapperFilter to avoid regressions (#1171)
2016-09-16 14:42:48 -06:00
Julien Roy 7ba8063940 Add test on FormBodyWrapperFilter to avoid regressions (#1171) 2016-09-16 14:42:31 -06:00
Niklas Herder 61de118130 Use management.port for locating hystrix.stream (#1317)
Allow override of /hystrix.stream location via management.port metadata.
2016-09-16 15:15:46 -04:00
Matthew Jenkins 4bf64f7005 Use statusPage/healthCheckUrlPath w/ custom management.port (#1349)
Use statusPage/healthCheckUrlPath w/ custom management.port

Added RelaxedPropertyResolver to handle multiple valid formats for
eureka properties.  Added tests.

Fixes gh-1348
2016-09-15 21:20:35 -04:00
Spencer Gibb 85eab87dce formatting 2016-09-15 08:34:59 -06:00
rockinrimmer 5ba0cc5860 Check if object is already a ResponseEntity, if so return instead of … (#1343)
* Check if object is already a ResponseEntity, if so return instead of wrapping in another ResponseEnitiy. Which caused headers and status code from original ResponseEnitiy to be lost and for body to be output with whole ResponseEntity instead of just the body

* added test
2016-09-15 10:15:42 -04:00
Marcin Grzejszczak 08aae5be3b Updating mvnw for all projects 2016-09-15 16:13:07 +02:00
Ryan Baxter 071bad218a Document Eureka client zone config. Fixes #31327 2016-09-14 10:41:38 -04:00
Ryan Baxter 027e7fc5c6 Merging changes from #1338 2016-09-13 08:57:01 -04:00
Spencer Gibb 498e8a1486 Use default maxSemaphore property.
Rather than hard code the default maxSemaphores for zuul use the already defined `zuul.semaphore.maxSemaphores` property.

fixes gh-1332
2016-09-12 09:36:22 -06:00
Ryan Baxter 09c6bc1d12 Merge pull request #1325 from herder/fix_registry_failures_report_time_mismatch
Fix lastFetch status calculation.
2016-09-08 09:38:09 -04:00
Niklas Herder 62521f38e8 Fix lastFetch status calculation.
Was previously milliseconds/seconds when calculating whether to display the message about reporting failures.
2016-09-08 11:24:11 +02:00
Spencer Gibb a6785afa5a Exclude commons-logging from archaius starter
fixes gh-126
2016-09-07 11:16:51 -06:00
Marcin Grzejszczak 7ebb46a092 Removed adoc footer 2016-09-07 18:02:13 +02:00
Marcin Grzejszczak a9fcc0a002 Updating mvnw for all projects 2016-09-07 17:29:18 +02:00
Ryan Baxter 08ed3c7fc4 Revert "Updated versions for Camden.RC1 release"
This reverts commit 3c1a23e7b1.
2016-09-06 14:25:21 -04:00
Ryan Baxter 3c1a23e7b1 Updated versions for Camden.RC1 release 2016-09-06 14:16:23 -04:00
Spencer Gibb c7403361fd polish 2016-09-06 08:40:49 -06:00
Sébastien Nussbaumer d9efb0a84d Allow Content-Length header with values greater than 2 GB (#1320)
fixes gh-1319
2016-09-06 10:38:16 -04:00
Ryan Baxter 4f81255970 Merge pull request #1318 from jkschneider/no-commons-lang
Remove dep on commons-lang in DimensionalServoMetricNaming (#1268)
2016-09-06 07:45:24 -04:00
Jon Schneider e556ef84a4 Eliminate dependency on commons-lang in DimensionalServoMetricNaming (#1268) 2016-09-02 22:06:18 -07:00
Jon Schneider 9587003d44 Sanitize Servo metric tag key/value pairs before publishing to Atlas (#1315) 2016-09-01 17:32:30 -04:00
Jon Schneider 8f7ea41fe8 Log partial failure reasons for metrics batches published to Atlas (#1314) 2016-09-01 14:57:52 -04:00
Jon Schneider b4284d18d5 Escape regex path variables in the uri tag for Spectator (#1311) 2016-09-01 11:27:30 -04:00
Spencer Gibb 048d401309 Merge pull request #1310 from garciafl/master
* garciafl-master:
  Removes RibbonCommand suffix.
2016-09-01 09:24:39 -06:00
Garcia Florian 85a1f3ec20 Removes RibbonCommand suffix.
This is a regression that was originally fixed in 1.1.x

Fixes gh-1308
2016-09-01 09:23:24 -06:00
Spencer Gibb 959b007269 removed duplicate feign ApacheHttpClient configuration 2016-08-31 12:02:25 -06:00
Spencer Gibb 9b550f7293 Support delete with body in zuul where possible.
The apache http client and ok http client support delete with body, ribbon rest client does not.

fixes gh-949
2016-08-31 11:43:05 -06:00
Spencer Gibb dc26b1efa8 Merge pull request #1102 from lowzj/request-param-processor
* lowzj-request-param-processor:
  update RequestParamParameterProcessor to support map param
2016-08-31 11:05:05 -06:00
lowzj d356e57134 update RequestParamParameterProcessor to support map param
fixes gh-1097
2016-08-31 11:04:20 -06:00
Spencer Gibb c5b0ffe310 Document customizing the Ribbon Client using properties
fixes gh-1188
2016-08-30 10:17:04 -06:00
Spencer Gibb 1ffa7825ed Add {spring-cloud-version} 2016-08-30 09:58:21 -06:00
Spencer Gibb d95f3cd1c5 Mention starters to include for features.
fixes gh-1134
2016-08-30 09:44:18 -06:00
Spencer Gibb 6ba64d97c8 Upgrade feign to 9.3.1
fixes gh-1303
2016-08-30 09:22:40 -06:00
Spencer Gibb fa508a2c7e Add warning for eureka healthcheck.
fixes gh-1276
2016-08-30 09:14:32 -06:00
Spencer Gibb d04df564f0 Try and fix old static wonkiness in test. 2016-08-29 12:47:35 -06:00
Spencer Gibb 5f192dd6ce Fix NPE when instance has no status page url.
fixes gh-1285
2016-08-29 12:29:57 -06:00
Spencer Gibb 6819922d26 Fix MetricsTagProvider for non web apps.
Guard creating and creation of classes that use it with @ConditionalOnClass(name = "javax.servlet.http.HttpServletRequest")

fixes gh-1302
2016-08-29 10:43:17 -06:00
Ryan Baxter ec726e863c Merge pull request #1296 from ryanjbaxter/upgrade-eureka-1.4.11-1288
Upgrade Eureka to 1.4.11
2016-08-25 14:15:11 -04:00
Ryan Baxter 610754981b Upgrade Eureka to 1.4.11. Fixes #1288 2016-08-25 13:53:24 -04:00
Ryan Baxter 485034dec0 Merge pull request #1294 from ryanjbaxter/upgrade-hystrix-1.5.5-1289
Upgrade Hystrix to 1.5.5
2016-08-24 19:29:38 -04:00
Ryan Baxter 9316124733 Upgrade Hystrix to 1.5.5. Fixes #1289 2016-08-24 16:10:50 -04:00
Ryan Baxter 1b9d91dc1e Merge pull request #1293 from ryanjbaxter/upgrade-stream
Updated SC Stream to Brooklyn SNAPSHOT
2016-08-24 09:55:07 -04:00
Ryan Baxter 15550e1855 Updated SC Stream to Brooklyn SNAPSHOT. Fixes #1292. 2016-08-24 09:32:30 -04:00
Spencer Gibb 43adc84dfd Make moving to https urls consistent.
This fixes a case where the ribbon property is set `ribbon.IsSecure=false`, but because the eureka secure port was enabled, the uri created by ribbon was prefixed with https when it shouldn't have.

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

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

fixes gh-1257
2016-08-22 15:05:59 -06:00
Ryan Baxter 1c8c0cfbf0 Moved parent element in pom so milestone profile activation occurs correctly 2016-08-22 15:32:31 -04:00
Ryan Baxter 047e25f7f7 Revert "Camden.M1 release"
This reverts commit 258e53a402.
2016-08-22 15:31:32 -04:00
Ryan Baxter 258e53a402 Camden.M1 release 2016-08-22 15:01:16 -04:00
Marcin Grzejszczak c8c4dfba6e Updating ghpages for all projects 2016-08-22 13:32:23 +02:00
Ryan Baxter 0bed544188 Merge pull request #1221 from stephlag/zuul_hystrix_threadpool
Make Hystrix Thread Pool available while using Zuul
2016-08-19 12:31:03 -04:00
Ryan Baxter 3cc07d36a2 Merge pull request #1282 from ryanjbaxter/upgrade-feign-to-9.3.0
Upgrade Feign to 9.3.0
2016-08-19 12:07:28 -04:00
Ryan Baxter 78b53a5215 Upgrade Feign to 9.3.0. Fixes #1281. 2016-08-19 11:14:11 -04:00
Marcin Grzejszczak 886382f568 Updating ghpages for all projects 2016-08-19 11:46:36 +02:00
Ryan Baxter a9dd1b4568 Merge pull request #1278 from ryanjbaxter/upgrade-feign-to-9.2.0
Upgrade Feign to 9.2.0
2016-08-18 14:43:29 -04:00
Ryan Baxter 323243f1ec Upgrade Feign to 9.2.0. Fixes #1273. 2016-08-18 14:24:53 -04:00
Spencer Gibb 25366c2a37 Add not about zuul route ordering.
fixes gh-1266
2016-08-18 11:33:29 -06:00
Stéphane Lagraulet b98c9ebc1d Add tests to verify old constructors 2016-08-18 14:27:11 +02:00
Dave Syer cb44cd2e33 Add additional test to look for null hosts in Zuul
Conflicts:
	spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfigurationTests.java
2016-08-18 12:59:05 +01:00
Marcin Grzejszczak 1fefd410be Updating ghpages for all projects 2016-08-18 13:15:30 +02:00
Stéphane Lagraulet f5bb5ff9c1 Keep old constructors to maintain backwards compatibility with spring cloud sleuth 2016-08-18 11:24:15 +02:00
Ryan Baxter e2e7c490db Merge pull request #1247 from Coramo/master
fix 'RibbonRequest' cannot to encode issues
2016-08-17 13:48:36 -04:00
Dave Syer 68cf346025 Change repo URLs to https 2016-08-17 17:33:02 +01:00
Dave Syer 7927372b75 Polish 2016-08-17 17:33:02 +01:00
Stéphane Lagraulet ca2c9018c6 Merge branch 'master' of https://github.com/spring-cloud/spring-cloud-netflix into zuul_hystrix_threadpool 2016-08-17 18:11:52 +02:00
Spencer Gibb 9ed5621a78 Revert "Adds zuul property to customize Hystrix ExecutionIsolationStrategy"
This reverts commit 21df6e43a3.
2016-08-17 09:57:35 -06:00
Dave Syer 58cf528463 Remove extra (redundant) condition
A problem in Spring Boot means that the extra conditions are combined
with OR not AND, so it's a bug to have more than one until that
gets fixed (in an AnyNestedCondition).

See https://github.com/spring-projects/spring-boot/pull/6672
2016-08-17 14:04:45 +01:00
Marcin Grzejszczak 4602fc1105 Updating ghpages for all projects 2016-08-17 12:45:28 +02:00
Dave Syer b9e2a1b21e Fix test so it actually works if there is a server on localhost:80 2016-08-17 09:19:33 +01:00
Dave Syer f76f294f7f Rename test to conform to usual pattern 2016-08-17 09:04:37 +01:00
Spencer Gibb 64fff7edd2 Merge branch 'context-path-prefix-support' of https://github.com/kakawait/spring-cloud-netflix into kakawait-context-path-prefix-support 2016-08-16 12:59:10 -06:00
Spencer Gibb beb84dbe07 Merge branch 'molindo-1108-proxy-headers' 2016-08-16 12:57:10 -06:00
Stefan Fussenegger d9768b5378 Improved zuul proxy header support.
- add zuul.add-host-header property to add Host header
- add port to X-Forwarded-Host as defined in RFC 7239
- extract PreDecorationFilter.filterOrder() into a public constant

fixes gh-1108
2016-08-16 12:56:54 -06:00
Spencer Gibb 34befdf1d2 Merge branch 'stephlag-zuul_hystrix_threadpool' 2016-08-16 10:44:32 -06:00
Stéphane Lagraulet 21df6e43a3 Adds zuul property to customize Hystrix ExecutionIsolationStrategy
Add a check to current context as contentLength is already in the context
Get content length from Headers when RibbonCommandContext is built + add tests
Use request content length RibbonRoutingFilter
Add documentation for new zuul.ribbonIsolationStrategy property

fixes gh-339
2016-08-16 10:43:24 -06:00
Jin Zhang bc7b8367a9 add *.swp *.swo to .gitignore (#1181) 2016-08-16 12:01:04 -04:00
Marcin Grzejszczak c7a6720593 Updated e2e tests with new brewery parameters 2016-08-16 15:27:29 +02:00
Marcin Grzejszczak cf0c3bda92 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:22 +02:00
Coramo 2db2a033ab add 'RibbonRequest' test 2016-08-12 12:02:58 +08:00
Coramo cf72184883 Merge branch 'master' of https://github.com/spring-cloud/spring-cloud-netflix 2016-08-12 11:00:43 +08:00
Stéphane Lagraulet 62344dccda Merge branch 'master' into zuul_hystrix_threadpool 2016-08-11 09:42:03 +02:00
Stéphane Lagraulet 03caa1b8ce Remove deprecated and unused constructors for RibbonCommandContext and RestClientRibbonCommand 2016-08-11 09:41:15 +02:00
Coramo 97c8f4c026 fix 'RibbonRequest' cannot to encode issues 2016-08-11 11:05:14 +08:00
Spencer Gibb 674e45f473 Add Client to list of feign objects that can be customized. 2016-08-10 14:13:28 -06:00
Ryan Baxter 36fec07a17 Merge pull request #1093 from daniellavoie/hystrix-security
Expose security context to any Hystrix command.
2016-08-10 15:20:19 -04:00
Spencer Gibb 0c694a018c Merge pull request #1242 from mikexliu/master
* pull1242:
  Add @Qualifier customization for @FeignClient
2016-08-10 12:22:45 -06:00
Mike Liu f37b513f21 Add @Qualifier customization for @FeignClient
Changed test to use @Qualifier.
Add documentation to reference the new attribute.
2016-08-10 12:22:33 -06:00
Spencer Gibb 56c7466e64 Merge branch 'Egencia-master' 2016-08-10 12:01:27 -06:00
libetl 519be4db06 MetricsInterceptorConfiguration modifies potenially unmodifiable list.
fixes gh-1240
2016-08-10 12:00:49 -06:00
Spencer Gibb 4255c5578b Merge pull request #1244 from cah-oster/master
* pull1244:
  Fixed double-encoding issue in RibbonClientConfiguration
2016-08-10 11:25:10 -06:00
Scott Oster 5e698278a8 Fixed double-encoding issue in RibbonClientConfiguration
fixes gh-1241
2016-08-10 11:24:59 -06:00
Ryan Baxter b09b0bcdbd Added Codacy badge to README 2016-08-10 10:36:34 -04:00
Spencer Gibb 909a51000f Remove @Ignore
fixes gh-1157
2016-08-09 16:50:57 -06:00
Spencer Gibb 306b43624b update readme 2016-08-09 14:51:19 -06:00
Ryan Baxter ec22c23bc5 Added CircleCI and Codecov badges 2016-08-09 16:45:26 -04:00
Ryan Baxter a4ba11eabb Updated sonar profile configuration 2016-08-09 14:31:13 -04:00
Stéphane Lagraulet 58559aff16 Add documentation for new zuul.ribbonIsolationStrategy property 2016-08-09 18:32:36 +02:00
Ryan Baxter 3e3511ac44 merge 2016-08-09 11:46:03 -04:00
Ryan Baxter 36ae029c18 circleci updates 2016-08-09 11:44:01 -04:00
Stéphane Lagraulet 4dc016f3e5 Merge branch 'master' into zuul_hystrix_threadpool
Conflicts:
	spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/support/AbstractRibbonCommand.java
2016-08-09 17:40:30 +02:00
Ryan Baxter d90e99216b Merge pull request #1225 from coveord/fix-content-length
Content-length not properly set when using the FormBodyWrapperFilter
2016-08-09 10:30:20 -04:00
Stéphane Lagraulet 67838807e8 Add zuulProperties as a method parameter 2016-08-09 09:36:30 +02:00
Jacques-Etienne Beaudet dc2cdd55c0 Merge branch 'master' into fix-content-length 2016-08-08 22:45:20 -04:00
Daniel Lavoie 5b0f6013e8 Expose security context to any Hystrix command.
Fix issue gh-1054
2016-08-08 23:05:10 +02:00
Ryan Baxter 52c956831e circleci updates 2016-08-08 15:03:00 -04:00
Ryan Baxter 4f93897b71 more circle ci configuration 2016-08-08 13:57:23 -04:00
Ryan Baxter 5676c8f753 added artifacts directory to circle config 2016-08-08 13:20:12 -04:00
Ryan Baxter eed72600c5 initial commit for circleci build 2016-08-08 11:47:29 -04:00
Stéphane Lagraulet 35ca9691d2 Merge branch 'master' into zuul_hystrix_threadpool
Conflicts:
	spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/okhttp/OkHttpRibbonCommandIntegrationTests.java
2016-08-08 17:29:10 +02:00
Stéphane Lagraulet 072f7760a4 Use request content length RibbonRoutingFilter 2016-08-08 17:07:35 +02:00
Ryan Baxter 3c1599a1f4 Merge pull request #1213 from mhartle/master
FormBodyWrapperFilter may add multiples of every multipart part to a request
2016-08-08 10:58:59 -04:00
Ryan Baxter 90808124f5 Merge pull request #1206 from daniellavoie/spring-boot-test-1.4
Migrate tests to Spring Boot Test 1.4
2016-08-08 10:53:17 -04:00
Stéphane Lagraulet 3c96fcc888 Get content length from Headers when RibbonCommandContext is built + add tests 2016-08-05 18:57:20 +02:00
Jacques-Etienne Beaudet d21a7ef8e2 Fix an issue where the FormBodyWrapperFilter would encode the form parameters differently than on the original request.
The FormBodyWrapperFilter handles the application/x-www-form-urlencoded. In the case of requests received by curl or javascript, the FormHttpMessageConverter will reencode the parameters differently (for example, '(' will be encoded while it's not with the javascript encodeURIComponent method).

While this doesn't create any problem, the content length was not properly set in AbstractRibbonCommand. This causes the form params being stripped or the backend server would wait a long time for additional bytes depending on if the content length header was bigger/smaller than the actual data.
2016-08-03 14:27:46 -04:00
Stéphane Lagraulet b1ec12af43 Add a check to current context as contentLength is already in the context 2016-08-03 15:32:08 +02:00
Stéphane Lagraulet 7929dfec0f Add a zuul property to customize Hystrix ExecutionIsolationStrategy 2016-08-02 13:39:04 +02:00
Michael Hartle 8bb223f6e3 Added a minimal test for FormBodyWrapperFilter handling multiple part names with multiple parts 2016-08-02 12:15:15 +02:00
Ryan Baxter 8b911e6074 Merge pull request #1217 from ryanjbaxter/move-to-open-feign
Changed version and group id to use latest OpenFeign release
2016-07-29 17:07:18 -04:00
Ryan Baxter 10a3d1df69 Changed version and group id to use latest OpenFeign release. Fixes #1189. 2016-07-29 14:21:21 -04:00
Dave Syer 8cf581d496 Correct description of cloud foundry instance id
Fixes gh-1214
2016-07-27 11:37:47 +01:00
Michael Hartle 0095ba8984 Fixed an issue where the pre FormBodyWrapperFilter adds multiples of every multipart part to a request 2016-07-26 13:37:46 +02:00
Daniel Lavoie 6f32aa95a7 Migrate tests to Spring Boot Test 1.4
Fixes gh-1205
2016-07-22 00:54:48 +02:00
Dave Syer afb43c5883 Actually fix the feign client validation issue
We need to still assert that the FeignClient is valid (no name and
value specified together for instance). Added missing tests.
2016-07-21 10:36:18 +01:00
Dave Syer 85ef54a9a1 Fix Feign validation for Spring 4.2 and 4.3
There is a utility method in Spring that we can use to merge all
the aliases. That way 4.2 and 4.3 behave the same way.

Fixes gh-1192
2016-07-21 08:28:33 +01:00
Dave Syer 2f581a1181 Use JUnit assertions 2016-07-21 08:28:31 +01:00
Dave Syer 110f872779 Fix generic type signature (otherwise breaks with Spring 4.3) 2016-07-21 08:28:27 +01:00
Spencer Gibb 54931afa8b Remove assertion that fails in spring 4.3.x
Need to make this class 4.2.x compatible.

see gh-1192
2016-07-16 10:36:27 -06:00
Ryan Baxter 2bdd2dab78 Merge pull request #1173 from denyska/master
Disable cookie management for RibbonLoadBalancingHttpClient Fixes gh-301
2016-07-14 12:55:32 -04:00
Spencer Gibb 2908323702 Add debug logging that code is setting spring.aop.proxyTargetClass=true
See gh-1105
2016-07-13 18:08:12 -06:00
Ryan Baxter 5d37bfe2fa Merge pull request #1183 from ryanjbaxter/master
Upgrade Eureka to 1.4.10.  Fixes #1180.
2016-07-13 10:26:41 -04:00
Ryan Baxter 0b0bdb17aa Upgrade Eureka to 1.4.10. Fixes #1180. 2016-07-13 09:19:38 -04:00
Spencer Gibb e5b2ae967b Remove phantom spring-cloud-netflix-zuul-server from dependencies pom
fixes gh-1175
2016-07-11 21:49:43 -06:00
Dave Syer c03ae0ab47 Update README with new CLA link 2016-07-11 14:35:10 +01:00
Denys Kurylenko 5d664cd00d Disable cookie management for RibbonLoadBalancingHttpClient Fixes gh-301 2016-07-08 18:48:47 -07:00
Ryan Baxter 00ed67f1b7 Upgrade Eureka to 1.4.9. Fixes #1167. 2016-07-08 10:31:34 -04:00
Spencer Gibb 277ded519d Merge pull request #1156 from brenuart/gh1155
remove Guava ImmutableMap in favour of Collections.unmodifiab…

fixes gh-1155
2016-07-01 16:41:37 -06:00
Spencer Gibb 7835c4047b temporarily comment out FormZuulProxyApplicationTests.postWithMultipartForm 2016-07-01 15:34:46 -06:00
Spencer Gibb ab0af4554c Update to s-c-build 1.2.0 2016-07-01 15:14:19 -06:00
Bertrand Renuart 926414a014 GH1155: remove Guava ImmutableMap in favour of Collections.unmodifiableMap()
Make sanitizeUrlTemplate() protected for easy sub-classing
2016-07-01 14:14:58 +02:00
Spencer Gibb 3991c8c5f7 Allow custom feign clients when not using ribbon.
fixes gh-1143
2016-06-29 12:39:58 -06:00
Spencer Gibb cb4d60ca8d fix typo 2016-06-29 11:39:04 -06:00
Thibaud Leprêtre 7ebe1977d8 Handle context-path on X-Forwarded-Prefix support
Since revision 7b270c4b46, Zuul add support of `X-Forwarded-Prefix` for route with `stripPrefix=true`.
However it supposes that Zuul context-path is equals to `/`. By using a custom context-path on Zuul we can expect that `zuul.add-proxy-headers=true` will compute a prefix regarding that context-path.

Indeed with following architecture:

- Zuul with context-path equals to `/foo`
- A random service (*bar-service*) without context-path

Zuul configurations:

```
zuul:
  add-proxy-headers: true
  routes:
    bar-service: /bar/**
```

We expect that *bar-service* when targeting `http://blabla.com/foo/bar/1`  will produce endpoint url equals to `http://blabla.com/foo/bar` and not only `http://blabla.com/bar` (that will not be resolvable).

Furthermore context-path influence not only `stripPrefix=true` routes because with following configuration

```
zuul:
  add-proxy-headers: true
  routes:
    bar-service:
      path: /bar/**
      strip-prefix: false
```

We expect that *bar-service* when targeting `http://blabla.com/foo/bar/1` will also prodcues endpoint url equals to `http://blabla.com/foo/bar`...

---

Moreover since *Spring 4.3* and new [`ForwardedHeaderFilter`](http://docs.spring.io/spring-framework/docs/4.3.0.BUILD-SNAPSHOT/javadoc-api/org/springframework/web/filter/ForwardedHeaderFilter.html) to handle `X-Forwarded-*` headers, when filter catches `X-Forwarded-Prefix` header it will override request context-path by `X-Forwarded-Prefix` header value and then **removes headers from request**. Thus the fact to support context-path will allow to use that filter with Zuul!

---

Attention there is only one edge case, when `X-Forwarded-Prefix` header is present as same as custom `context-path`! In that case context-path will be ignored in favor to `X-Forwarded-Prefix` as same does `ForwardedHeaderFilter`
2016-06-29 10:37:52 +02:00
Spencer Gibb 5e9c71e0e3 Merge pull request #1138 from orchit/master
* orchit-master:
  Adds ability to disable hostname validation.
2016-06-28 10:33:50 -06:00
Patrick Cornelißen 723287b305 Adds ability to disable hostname validation.
Adds property zuul.sslHostnameValidationEnabled to be able to disable the hostname validation in the simpleHostRoutingFilter

fixes gh-1057
2016-06-28 10:32:49 -06:00
Spencer Gibb 46b2e45fd5 Adds a RibbonRequestCustomizer.
This allows developers to customize the request builders for zuul requests to inject headers or modify the request in general.  Each zuul request type (RestClient, Apache HttpClient and OkHttp) is supported.

fixes gh-1141
2016-06-27 13:15:54 -06:00
Spencer Gibb 390999e042 Add default zuul.ribbon.httpclient.enabled=true 2016-06-24 16:17:33 -06:00
Spencer Gibb 8a7e165214 Split ProxyRequestHelper to function without Actuator.
Previously ProxyRequestHelper contained a TraceRepository field. This caused zuul apps to fail with a class not found exception if they excluded actuator. This splits TraceRepository functionality into a new TraceProxyRequestHelper that extends ProxyRequestHelper. Auto configuration creates the appropriate ProxyRequestHelper based on the existence or not of actuator classes.

fixes gh-1135
2016-06-24 16:08:23 -06:00
Spencer Gibb 5742f5e811 add .shelf to .gitignore 2016-06-24 09:58:30 -06:00
Spencer Gibb 52b6578fcf polish 2016-06-24 09:32:18 -06:00
Spencer Gibb 2f205f20c5 Skip tests that only fail in travis for now. 2016-06-24 09:20:19 -06:00
Spencer Gibb e1df7c3b4d Add back RestClientRibbonCommand api's. 2016-06-24 09:13:18 -06:00
Spencer Gibb 7c2f806745 Add hystrix.stream.queue.size to customize queue size.
Also added descriptions to properties.

fixes gh-1131
2016-06-24 08:41:45 -06:00
Spencer Gibb 6877f0898f Move RestClientRibbonCommand back to original place.
Made it public and undid other refactorings.
2016-06-24 08:15:12 -06:00
Marcin Grzejszczak 74ac6eae8f Made RestClientRibbonCommand public 2016-06-24 09:10:54 +02:00
Spencer Gibb 75610e92a7 added todo 2016-06-23 22:34:34 -06:00
Spencer Gibb 7dc8b86001 Load ribbon classes if defined via properties.
Allows ribbon class name properties to be used to define beans.

This seems like boilerplate, but works, though not easily extensible.

fixes gh-927
2016-06-23 20:20:34 -06:00
Will Tran dfbfcfecc7 Without explicit client config of IsSecure, lookup from registration
instead of defaulting to false.

Fixes gh-1126
2016-06-23 15:12:35 -06:00
Spencer Gibb 7ac81f8fd5 Adds zuul okhttp support & zuul refactor.
Adds a OkHttpRibbonCommand and accomanying classes. They should be
considered beta.

Refactors zuul ribbon command classes and tests.

fixes gh-1125
2016-06-23 12:10:59 -06:00
Spencer Gibb fd36a0cc72 Default to HttpClientRibbonCommandFactory 2016-06-22 21:08:34 -06:00
Spencer Gibb 398c5aa009 upgrade feign to v8.17.0
fixes gh-1081
2016-06-22 15:53:18 -06:00
Spencer Gibb 7a90c0cd88 Set entity contentLength to 0 for GET requests.
Zuul sets an empty entity for GET requests. The apache http client either sets transfer encoding to chunked or a content length header. This change sets the apache entity contentLength to 0 (not the header), to that in RequestContent the "content-length" header is set to 0 rathern than transfer encoding.

See gh-1042
2016-06-22 13:01:51 -06:00
Spencer Gibb cfcca5f881 Set entity contentLength for HttpClientRibbonCommand.
If the content length isn't set on the entity, when http client determines which header to set, it sets transfer-encoding to chunked rather than content-length.

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

See gh-1042
2016-06-22 13:01:34 -06:00
Spencer Gibb ca67265c79 Merge pull request #1120 from jmirc/master
* pull1120:
  Fix hystrix dashboard TPS.
2016-06-21 15:23:05 -06:00
Jérôme Mirc b422fe297c Fix hystrix dashboard TPS.
fixes gh-1113

See https://github.com/Netflix/Hystrix/issues/1251
2016-06-21 15:21:10 -06:00
Spencer Gibb 856c47138a Don't use deprecated constructor.
Eliminates a legacy use of a static singleton. This will help allow multiple registrations.
2016-06-20 18:27:12 -06:00
Spencer Gibb 83cdf647c9 Angel eureka clients can use brixton eureka servers.
Updates so CloudJacksonJson extension is properly recognized by Eureka
Server as LegacyJacksonJson.

see gh-978
fixes gh-1111
2016-06-17 16:53:44 -06:00
Dave Syer ff46a7dc50 Merge branch '1.1.x'
Conflicts:
	spring-cloud-netflix-dependencies/pom.xml
2016-06-17 15:10:51 +01:00
Spencer Gibb 532627fad2 Merge pull request #1106 from spring-cloud/zuul-122
Upgrade to zuul 1.2.2
2016-06-16 13:58:10 -06:00
Spencer Gibb aa23660d0c Upgrade to zuul 1.2.2
fixes gh-1091
closes gh-958
2016-06-16 13:50:33 -06:00
Spencer Gibb 02948657a4 Don't send 4xx errors to /error
fixes gh-1110
2016-06-16 11:45:52 -06:00
Spencer Gibb cfb13ac59f quote var 2016-06-15 17:17:52 -06:00
Spencer Gibb 00aee4be00 skip source generation 2016-06-15 17:16:12 -06:00
Spencer Gibb a669975fb0 Only install and skip javadoc generation 2016-06-15 17:09:14 -06:00
Spencer Gibb 161d719dba add *.log to .gitignore 2016-06-15 15:38:13 -06:00
Spencer Gibb dd220458f8 polish 2016-06-13 09:46:32 -06:00
Spencer Gibb d4163dde8e Polish 2016-06-13 09:46:32 -06:00
Spencer Gibb 3bd563e495 remove h2 test dep 2016-06-13 09:46:32 -06:00
Dave Syer 525c5852fb Add JAVA_OPTS to travis 2016-06-13 08:40:36 +01:00
Dave Syer 69501bd110 Merge 1.1.x 2016-06-13 08:05:07 +01:00
Dave Syer f6cd2d279d Update README 2016-06-11 10:50:32 +01:00
Dave Syer 9cab69a968 Update readme script to handle local includes 2016-06-11 09:54:41 +01:00
Spencer Gibb 243c13b7ee Make turbine.combineHostPort=true the default.
fixes gh-1087
2016-06-09 16:28:48 -06:00
Spencer Gibb 09cc1f2db0 polish 2016-06-09 13:09:09 -06:00
Spencer Gibb 7d295bdc8b Update to SNAPSHOT 2016-06-09 11:30:39 -06:00
547 changed files with 34618 additions and 6444 deletions
+4
View File
@@ -14,3 +14,7 @@ _site/
*.ipr
*.iws
.factorypath
*.log
.shelf
*.swp
*.swo
+1 -1
View File
@@ -1 +1 @@
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip
-33
View File
@@ -1,33 +0,0 @@
sudo: false
cache:
directories:
- $HOME/.m2
language: java
jdk:
- oraclejdk8
services:
- rabbitmq
before_install:
- git config user.name "$GIT_NAME"
- git config user.email "$GIT_EMAIL"
- git config credential.helper "store --file=.git/credentials"
- echo "https://$GH_TOKEN:@github.com" > .git/credentials
- gem install asciidoctor
install:
- ./mvnw install -P docs -q -U -DskipTests=true $MVN_LOCAL_OPTS
- '[ "${MVN_GOAL}" == "deploy" ] && ./docs/src/main/asciidoc/ghpages.sh || echo "Not updating docs"'
script:
- './mvnw -s .settings.xml $MVN_GOAL $MVN_PROFILE -nsu $MVN_LOCAL_OPTS'
env:
global:
- MVN_LOCAL_OPTS="-Dmaven.source.skip=true -Dmaven.javadoc.skip=true -Dmaven.test.redirectTestOutputToFile=true"
- GIT_NAME="Dave Syer"
- GIT_EMAIL=dsyer@pivotal.io
- CI_DEPLOY_USERNAME=buildmaster
- FEATURE_BRANCH=$(echo ${TRAVIS_BRANCH} | grep -q "^.*/.*$" && echo true || echo false)
- SPRING_CLOUD_BUILD=$(echo ${TRAVIS_REPO_SLUG} | grep -q "^spring-cloud/.*$" && echo true || echo false)
- MVN_GOAL=install
- VERSION=$(mvn validate | grep Building | head -1 | sed -e 's/.* //')
- MILESTONE=$(echo ${VERSION} | egrep 'M|RC' && echo true || echo false)
- MVN_PROFILE=$([ "${MILESTONE}" == "true" ] && echo -P milestone)
- secure: aeLXRC5oFSddwnZt1/7G2/OHr7jDbxz0ET7sej3I+eSbe3N5vbzQ6FC08es4l89l54ciXd90I1g2BMw7DTYKOO373FP78XPdAEbifJTU4DGd6fCELmoTtUPhjunBIk7E49hisPbv82892IYYA7qi/hzG548cPyZ1IgiJjq0NCsc=
+8 -39
View File
@@ -1,6 +1,9 @@
// Do not edit this file (e.g. go instead to docs/src/main/asciidoc)
// Do not edit this file (e.g. go instead to src/main/asciidoc)
image::https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/master.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/master"]
image::https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/master/graph/badge.svg["Codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/master"]
image::https://api.codacy.com/project/badge/Grade/a6885a06921e4f72a0df0b7aabd6d118["Codacy code quality", link="https://www.codacy.com/app/Spring-Cloud/spring-cloud-netflix?utm_source=github.com&utm_medium=referral&utm_content=spring-cloud/spring-cloud-netflix&utm_campaign=Badge_Grade"]
image::https://travis-ci.org/spring-cloud/spring-cloud-netflix.svg?branch=master[Build Status, link=https://travis-ci.org/spring-cloud/spring-cloud-netflix]
This project provides Netflix OSS integrations for Spring Boot apps through autoconfiguration
and binding to the Spring Environment and other Spring programming model idioms. With a few
@@ -82,11 +85,11 @@ a modified file in the correct place. Just commit it and push the change.
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
http://eclipse.org/m2e/[m2eclipse] eclipse plugin for maven support. Other IDEs and tools
should also work without issue as long as they use Maven 3.3.3 or better.
==== Importing into eclipse with m2eclipse
We recommend the http://eclipse.org/m2e/[m2eclipe] eclipse plugin when working with
We recommend the http://eclipse.org/m2e/[m2eclipse] eclipse plugin when working with
eclipse. If you don't already have m2eclipse installed it is available from the "eclipse
marketplace".
@@ -113,40 +116,6 @@ from the `file` menu.
==== Adding Project Lombok Agent
Spring Cloud uses http://projectlombok.org/features/index.html[Project Lombok]
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
sign that you need to do this is a lot of compiler errors to do with
missing methods and fields, e.g.
[indent=0]
----
The method getInitialStatus() is undefined for the type EurekaInstanceConfigBean EurekaDiscoveryClientConfiguration.java /spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka line 120 Java Problem
The method getInitialStatus() is undefined for the type EurekaInstanceConfigBean EurekaDiscoveryClientConfiguration.java /spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka line 121 Java Problem
The method setNonSecurePort(int) is undefined for the type EurekaInstanceConfigBean EurekaDiscoveryClientConfiguration.java /spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka line 112 Java Problem
The type EurekaInstanceConfigBean.IdentifyingDataCenterInfo must implement the inherited abstract method DataCenterInfo.getName() EurekaInstanceConfigBean.java /spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka line 131 Java Problem
The method getId() is undefined for the type ProxyRouteLocator.ProxyRouteSpec PreDecorationFilter.java /spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/pre line 60 Java Problem
The method getLocation() is undefined for the type ProxyRouteLocator.ProxyRouteSpec PreDecorationFilter.java /spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/pre line 55 Java Problem
----
==== Importing into Intellij
Spring Cloud projects use annotation processing, particularly Lombok, which requires configuration
or you will encounter compile problems. It also needs a specific version of maven and a profile
enabled. Intellij 14.1+ requires some configuration to ensure these are setup properly.
1. Click Preferences, Plugins. *Ensure Lombok is installed*
2. Click New, Project from Existing Sources, choose your spring-cloud project directory
3. Choose Maven, and select Environment Settings. *Ensure you are using Maven 3.3.3*
4. In the next screen, *Select the profile `spring`* click Next until Finish.
5. Click Preferences, "Build, Execution, Deployment", Compiler, Annotation Processors. *Click Enable Annotation Processing*
6. Click Build, Rebuild Project, and you are ready to go!
==== 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,
@@ -157,7 +126,7 @@ follow the guidelines below.
=== Sign the Contributor License Agreement
Before we accept a non-trivial patch or pull request we will need you to sign the
https://support.springsource.com/spring_committer_signup[contributor's agreement].
https://cla.pivotal.io/sign/spring[Contributor License Agreement].
Signing the contributor's agreement does not grant anyone commit rights to the main
repository, but it does mean that we can accept your contributions, and you will get an
author credit if we do. Active contributors might be asked to join the core team, and
+27
View File
@@ -0,0 +1,27 @@
general:
branches:
ignore:
- gh-pages # list of branches to ignore
machine:
java:
version: oraclejdk8
environment:
_JAVA_OPTIONS: "-Xms1024m -Xmx2048m"
dependencies:
override:
- cd spring-cloud-netflix-hystrix-contract && ../mvnw clean install
- ./mvnw -s .settings.xml -U --fail-never dependency:go-offline || true
test:
override:
- ./mvnw -s .settings.xml clean org.jacoco:jacoco-maven-plugin:prepare-agent install -U -P sonar -nsu --batch-mode -Dmaven.test.redirectTestOutputToFile=true -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn
post:
- find . -type f -regex ".*/spring-cloud-*.*/target/*.*" | cpio -pdm $CIRCLE_ARTIFACTS
- mkdir -p $CIRCLE_TEST_REPORTS/junit/
- find . -type f -regex ".*/target/.*-reports/.*" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \;
- bash <(curl -s https://codecov.io/bash)
notify:
webhooks:
# A list of hook hashes, containing the url field
# gitter hook
- url: https://webhooks.gitter.im/e/22e6bb4eb945dd61ba54
+3
View File
@@ -0,0 +1,3 @@
releaser:
maven:
buildCommand: ./scripts/build.sh {{systemProps}}
+18 -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 http://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.1.3.BUILD-SNAPSHOT</version>
<version>1.4.1.RELEASE</version>
</parent>
<artifactId>spring-cloud-netflix-docs</artifactId>
<packaging>pom</packaging>
@@ -14,15 +14,16 @@
<properties>
<docs.main>spring-cloud-netflix</docs.main>
<main.basedir>${basedir}/..</main.basedir>
<docs.whitelisted.branches>1.2.x,1.3.x,1.4.x</docs.whitelisted.branches>
</properties>
<build>
<build>
<plugins>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
@@ -30,11 +31,19 @@
<id>docs</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<inherited>false</inherited>
</plugin>
<plugin>
<groupId>com.agilejava.docbkx</groupId>
<artifactId>docbkx-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
+4 -3
View File
@@ -1,4 +1,7 @@
image::https://travis-ci.org/spring-cloud/spring-cloud-netflix.svg?branch=master[Build Status, link=https://travis-ci.org/spring-cloud/spring-cloud-netflix]
image::https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/master.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/master"]
image::https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/master/graph/badge.svg["Codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/master"]
image::https://api.codacy.com/project/badge/Grade/a6885a06921e4f72a0df0b7aabd6d118["Codacy code quality", link="https://www.codacy.com/app/Spring-Cloud/spring-cloud-netflix?utm_source=github.com&utm_medium=referral&utm_content=spring-cloud/spring-cloud-netflix&utm_campaign=Badge_Grade"]
include::intro.adoc[]
@@ -17,8 +20,6 @@ include::intro.adoc[]
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/building-jdk8.adoc[]
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/building-lombok.adoc[]
== Contributing
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/contributing.adoc[]
+318 -42
View File
@@ -1,54 +1,330 @@
#!/bin/bash -x
git remote set-url --push origin `git config remote.origin.url | sed -e 's/^git:/https:/'`
set -e
if ! (git remote set-branches --add origin gh-pages && git fetch -q); then
echo "No gh-pages, so not syncing"
exit 0
fi
# Set default props like MAVEN_PATH, ROOT_FOLDER etc.
function set_default_props() {
# The script should be executed from the root folder
ROOT_FOLDER=`pwd`
echo "Current folder is ${ROOT_FOLDER}"
if ! [ -d docs/target/generated-docs ]; then
echo "No gh-pages sources in docs/target/generated-docs, so not syncing"
exit 0
fi
if [[ ! -e "${ROOT_FOLDER}/.git" ]]; then
echo "You're not in the root folder of the project!"
exit 1
fi
# Find name of current branch
###################################################################
branch=$TRAVIS_BRANCH
[ "$branch" == "" ] && branch=`git rev-parse --abbrev-ref HEAD`
target=.
if [ "$branch" != "master" ]; then target=./$branch; mkdir -p $target; fi
# Prop that will let commit the changes
COMMIT_CHANGES="no"
MAVEN_PATH=${MAVEN_PATH:-}
echo "Path to Maven is [${MAVEN_PATH}]"
REPO_NAME=${PWD##*/}
echo "Repo name is [${REPO_NAME}]"
SPRING_CLOUD_STATIC_REPO=${SPRING_CLOUD_STATIC_REPO:-git@github.com:spring-cloud/spring-cloud-static.git}
echo "Spring Cloud Static repo is [${SPRING_CLOUD_STATIC_REPO}"
}
# Check if gh-pages exists and docs have been built
function check_if_anything_to_sync() {
git remote set-url --push origin `git config remote.origin.url | sed -e 's/^git:/https:/'`
if ! (git remote set-branches --add origin gh-pages && git fetch -q); then
echo "No gh-pages, so not syncing"
exit 0
fi
if ! [ -d docs/target/generated-docs ] && ! [ "${BUILD}" == "yes" ]; then
echo "No gh-pages sources in docs/target/generated-docs, so not syncing"
exit 0
fi
}
function retrieve_current_branch() {
# Code getting the name of the current branch. For master we want to publish as we did until now
# http://stackoverflow.com/questions/1593051/how-to-programmatically-determine-the-current-checked-out-git-branch
# If there is a branch already passed will reuse it - otherwise will try to find it
CURRENT_BRANCH=${BRANCH}
if [[ -z "${CURRENT_BRANCH}" ]] ; then
CURRENT_BRANCH=$(git symbolic-ref -q HEAD)
CURRENT_BRANCH=${CURRENT_BRANCH##refs/heads/}
CURRENT_BRANCH=${CURRENT_BRANCH:-HEAD}
fi
echo "Current branch is [${CURRENT_BRANCH}]"
git checkout ${CURRENT_BRANCH} || echo "Failed to check the branch... continuing with the script"
}
# Switches to the provided value of the release version. We always prefix it with `v`
function switch_to_tag() {
git checkout v${VERSION}
}
# Build the docs if switch is on
function build_docs_if_applicable() {
if [[ "${BUILD}" == "yes" ]] ; then
./mvnw clean install -P docs -pl docs -DskipTests
fi
}
# Get the name of the `docs.main` property
# Get whitelisted branches - assumes that a `docs` module is available under `docs` profile
function retrieve_doc_properties() {
MAIN_ADOC_VALUE=$("${MAVEN_PATH}"mvn -q \
-Dexec.executable="echo" \
-Dexec.args='${docs.main}' \
--non-recursive \
org.codehaus.mojo:exec-maven-plugin:1.3.1:exec)
echo "Extracted 'main.adoc' from Maven build [${MAIN_ADOC_VALUE}]"
WHITELIST_PROPERTY=${WHITELIST_PROPERTY:-"docs.whitelisted.branches"}
WHITELISTED_BRANCHES_VALUE=$("${MAVEN_PATH}"mvn -q \
-Dexec.executable="echo" \
-Dexec.args="\${${WHITELIST_PROPERTY}}" \
org.codehaus.mojo:exec-maven-plugin:1.3.1:exec \
-P docs \
-pl docs)
echo "Extracted '${WHITELIST_PROPERTY}' from Maven build [${WHITELISTED_BRANCHES_VALUE}]"
}
# Stash any outstanding changes
###################################################################
git diff-index --quiet HEAD
dirty=$?
if [ "$dirty" != "0" ]; then git stash; fi
function stash_changes() {
git diff-index --quiet HEAD && dirty=$? || (echo "Failed to check if the current repo is dirty. Assuming that it is." && dirty="1")
if [ "$dirty" != "0" ]; then git stash; fi
}
# Switch to gh-pages branch to sync it with current branch
###################################################################
git checkout gh-pages
for f in docs/target/generated-docs/*; do
file=${f#docs/target/generated-docs/*}
if ! git ls-files -i -o --exclude-standard --directory | grep -q ^$file$; then
# Not ignored...
cp -rf $f $target
git add -A $target/$file
function add_docs_from_target() {
local DESTINATION_REPO_FOLDER
if [[ -z "${DESTINATION}" && -z "${CLONE}" ]] ; then
DESTINATION_REPO_FOLDER=${ROOT_FOLDER}
elif [[ "${CLONE}" == "yes" ]]; then
mkdir -p ${ROOT_FOLDER}/target
local clonedStatic=${ROOT_FOLDER}/target/spring-cloud-static
if [[ ! -e "${clonedStatic}/.git" ]]; then
echo "Cloning Spring Cloud Static to target"
git clone ${SPRING_CLOUD_STATIC_REPO} ${clonedStatic} && git checkout gh-pages
else
echo "Spring Cloud Static already cloned - will pull changes"
cd ${clonedStatic} && git checkout gh-pages && git pull origin gh-pages
fi
DESTINATION_REPO_FOLDER=${clonedStatic}/${REPO_NAME}
mkdir -p ${DESTINATION_REPO_FOLDER}
else
if [[ ! -e "${DESTINATION}/.git" ]]; then
echo "[${DESTINATION}] is not a git repository"
exit 1
fi
DESTINATION_REPO_FOLDER=${DESTINATION}/${REPO_NAME}
mkdir -p ${DESTINATION_REPO_FOLDER}
echo "Destination was provided [${DESTINATION}]"
fi
cd ${DESTINATION_REPO_FOLDER}
git checkout gh-pages
git pull origin gh-pages
# Add git branches
###################################################################
if [[ -z "${VERSION}" ]] ; then
copy_docs_for_current_version
else
copy_docs_for_provided_version
fi
commit_changes_if_applicable
}
# Copies the docs by using the retrieved properties from Maven build
function copy_docs_for_current_version() {
if [[ "${CURRENT_BRANCH}" == "master" ]] ; then
echo -e "Current branch is master - will copy the current docs only to the root folder"
for f in docs/target/generated-docs/*; do
file=${f#docs/target/generated-docs/*}
if ! git ls-files -i -o --exclude-standard --directory | grep -q ^$file$; then
# Not ignored...
cp -rf $f ${ROOT_FOLDER}/
git add -A ${ROOT_FOLDER}/$file
fi
done
COMMIT_CHANGES="yes"
else
echo -e "Current branch is [${CURRENT_BRANCH}]"
# http://stackoverflow.com/questions/29300806/a-bash-script-to-check-if-a-string-is-present-in-a-comma-separated-list-of-strin
if [[ ",${WHITELISTED_BRANCHES_VALUE}," = *",${CURRENT_BRANCH},"* ]] ; then
mkdir -p ${ROOT_FOLDER}/${CURRENT_BRANCH}
echo -e "Branch [${CURRENT_BRANCH}] is whitelisted! Will copy the current docs to the [${CURRENT_BRANCH}] folder"
for f in docs/target/generated-docs/*; do
file=${f#docs/target/generated-docs/*}
if ! git ls-files -i -o --exclude-standard --directory | grep -q ^$file$; then
# Not ignored...
# We want users to access 1.0.0.RELEASE/ instead of 1.0.0.RELEASE/spring-cloud.sleuth.html
if [[ "${file}" == "${MAIN_ADOC_VALUE}.html" ]] ; then
# We don't want to copy the spring-cloud-sleuth.html
# we want it to be converted to index.html
cp -rf $f ${ROOT_FOLDER}/${CURRENT_BRANCH}/index.html
git add -A ${ROOT_FOLDER}/${CURRENT_BRANCH}/index.html
else
cp -rf $f ${ROOT_FOLDER}/${CURRENT_BRANCH}
git add -A ${ROOT_FOLDER}/${CURRENT_BRANCH}/$file
fi
fi
done
COMMIT_CHANGES="yes"
else
echo -e "Branch [${CURRENT_BRANCH}] is not on the white list! Check out the Maven [${WHITELIST_PROPERTY}] property in
[docs] module available under [docs] profile. Won't commit any changes to gh-pages for this branch."
fi
fi
}
# Copies the docs by using the explicitly provided version
function copy_docs_for_provided_version() {
local FOLDER=${DESTINATION_REPO_FOLDER}/${VERSION}
mkdir -p ${FOLDER}
echo -e "Current tag is [v${VERSION}] Will copy the current docs to the [${FOLDER}] folder"
for f in ${ROOT_FOLDER}/docs/target/generated-docs/*; do
file=${f#${ROOT_FOLDER}/docs/target/generated-docs/*}
copy_docs_for_branch ${file} ${FOLDER}
done
COMMIT_CHANGES="yes"
CURRENT_BRANCH="v${VERSION}"
}
# Copies the docs from target to the provided destination
# Params:
# $1 - file from target
# $2 - destination to which copy the files
function copy_docs_for_branch() {
local file=$1
local destination=$2
if ! git ls-files -i -o --exclude-standard --directory | grep -q ^${file}$; then
# Not ignored...
# We want users to access 1.0.0.RELEASE/ instead of 1.0.0.RELEASE/spring-cloud.sleuth.html
if [[ ("${file}" == "${MAIN_ADOC_VALUE}.html") || ("${file}" == "${REPO_NAME}.html") ]] ; then
# We don't want to copy the spring-cloud-sleuth.html
# we want it to be converted to index.html
cp -rf $f ${destination}/index.html
git add -A ${destination}/index.html
else
cp -rf $f ${destination}
git add -A ${destination}/$file
fi
fi
}
function commit_changes_if_applicable() {
if [[ "${COMMIT_CHANGES}" == "yes" ]] ; then
COMMIT_SUCCESSFUL="no"
git commit -a -m "Sync docs from ${CURRENT_BRANCH} to gh-pages" && COMMIT_SUCCESSFUL="yes" || echo "Failed to commit changes"
# Uncomment the following push if you want to auto push to
# the gh-pages branch whenever you commit to master locally.
# This is a little extreme. Use with care!
###################################################################
if [[ "${COMMIT_SUCCESSFUL}" == "yes" ]] ; then
git push origin gh-pages
fi
fi
}
# Switch back to the previous branch and exit block
function checkout_previous_branch() {
# If -version was provided we need to come back to root project
cd ${ROOT_FOLDER}
git checkout ${CURRENT_BRANCH} || echo "Failed to check the branch... continuing with the script"
if [ "$dirty" != "0" ]; then git stash pop; fi
exit 0
}
# Assert if properties have been properly passed
function assert_properties() {
echo "VERSION [${VERSION}], DESTINATION [${DESTINATION}], CLONE [${CLONE}]"
if [[ "${VERSION}" != "" && (-z "${DESTINATION}" && -z "${CLONE}") ]] ; then echo "Version was set but destination / clone was not!"; exit 1;fi
if [[ ("${DESTINATION}" != "" && "${CLONE}" != "") && -z "${VERSION}" ]] ; then echo "Destination / clone was set but version was not!"; exit 1;fi
if [[ "${DESTINATION}" != "" && "${CLONE}" == "yes" ]] ; then echo "Destination and clone was set. Pick one!"; exit 1;fi
}
# Prints the usage
function print_usage() {
cat <<EOF
The idea of this script is to update gh-pages branch with the generated docs. Without any options
the script will work in the following manner:
- if there's no gh-pages / target for docs module then the script ends
- for master branch the generated docs are copied to the root of gh-pages branch
- for any other branch (if that branch is whitelisted) a subfolder with branch name is created
and docs are copied there
- if the version switch is passed (-v) then a tag with (v) prefix will be retrieved and a folder
with that version number will be created in the gh-pages branch. WARNING! No whitelist verification will take place
- if the destination switch is passed (-d) then the script will check if the provided dir is a git repo and then will
switch to gh-pages of that repo and copy the generated docs to `docs/<project-name>/<version>`
- if the destination switch is passed (-d) then the script will check if the provided dir is a git repo and then will
switch to gh-pages of that repo and copy the generated docs to `docs/<project-name>/<version>`
USAGE:
You can use the following options:
-v|--version - the script will apply the whole procedure for a particular library version
-d|--destination - the root of destination folder where the docs should be copied. You have to use the full path.
E.g. point to spring-cloud-static folder. Can't be used with (-c)
-b|--build - will run the standard build process after checking out the branch
-c|--clone - will automatically clone the spring-cloud-static repo instead of providing the destination.
Obviously can't be used with (-d)
EOF
}
# ==========================================
# ____ ____ _____ _____ _____ _______
# / ____|/ ____| __ \|_ _| __ \__ __|
# | (___ | | | |__) | | | | |__) | | |
# \___ \| | | _ / | | | ___/ | |
# ____) | |____| | \ \ _| |_| | | |
# |_____/ \_____|_| \_\_____|_| |_|
#
# ==========================================
while [[ $# > 0 ]]
do
key="$1"
case ${key} in
-v|--version)
VERSION="$2"
shift # past argument
;;
-d|--destination)
DESTINATION="$2"
shift # past argument
;;
-b|--build)
BUILD="yes"
;;
-c|--clone)
CLONE="yes"
;;
-h|--help)
print_usage
exit 0
;;
*)
echo "Invalid option: [$1]"
print_usage
exit 1
;;
esac
shift # past argument or value
done
git add -A README.adoc || echo "No change to README.adoc"
git commit -a -m "Sync docs from $branch to gh-pages" || echo "Nothing committed"
# Uncomment the following push if you want to auto push to
# the gh-pages branch whenever you commit to branch locally.
# This is a little extreme. Use with care!
###################################################################
git push origin gh-pages || echo "Cannot push gh-pages"
# Finally, switch back to the current branch and exit block
git checkout $branch
if [ "$dirty" != "0" ]; then git stash pop; fi
exit 0
assert_properties
set_default_props
check_if_anything_to_sync
if [[ -z "${VERSION}" ]] ; then
retrieve_current_branch
else
switch_to_tag
fi
build_docs_if_applicable
retrieve_doc_properties
stash_changes
add_docs_from_target
checkout_previous_branch
File diff suppressed because it is too large Load Diff
-30
View File
@@ -1,30 +0,0 @@
#!/usr/bin/env ruby
base_dir = File.join(File.dirname(__FILE__),'../../..')
src_dir = File.join(base_dir, "/src/main/asciidoc")
require 'asciidoctor'
require 'optparse'
options = {}
file = "#{src_dir}/README.adoc"
OptionParser.new do |o|
o.on('-o OUTPUT_FILE', 'Output file (default is stdout)') { |file| options[:to_file] = file unless file=='-' }
o.on('-h', '--help') { puts o; exit }
o.parse!
end
file = ARGV[0] if ARGV.length>0
srcDir = File.dirname(file)
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
unless options[:to_file]
puts out
else
File.open(options[:to_file],'w+') do |file|
file.write(out)
end
end
Vendored
+26 -16
View File
@@ -46,15 +46,6 @@ if [ -z "$MAVEN_SKIP_RC" ] ; then
fi
VERSION=$(awk '/<parent/ {l=4}; (l-- > 0) {$0=$0} 1' `dirname $0`/pom.xml| grep '<version' | head -1 | sed -e 's/.*>\(.*\)<.*/\1/')
if echo $VERSION | egrep -q 'M|RC'; then
echo Activating \"milestone\" profile for version=\"$VERSION\"
echo $MAVEN_ARGS | grep -q milestone || MAVEN_ARGS="$MAVEN_ARGS -Pmilestone"
else
echo Deactivating \"milestone\" profile for version=\"$VERSION\"
echo $MAVEN_ARGS | grep -q milestone && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pmilestone//')
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
@@ -66,27 +57,27 @@ case "`uname`" in
#
# Look for the Apple JDKs first to preserve the existing behaviour, and then look
# for the new JDKs provided by Oracle.
#
#
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
#
# Apple JDKs
#
export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
fi
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
#
# Apple JDKs
#
export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
fi
if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
#
# Oracle JDKs
#
export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
fi
fi
if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
#
@@ -228,16 +219,35 @@ concat_lines() {
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)}
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# Provide a "standardized" way to retrieve the CLI args that will
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
echo "Running version check"
VERSION=$( sed '\!<parent!,\!</parent!d' `dirname $0`/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
if echo $VERSION | egrep -q 'RELEASE'; then
echo Activating \"central\" profile for version=\"$VERSION\"
echo $MAVEN_ARGS | grep -q milestone || MAVEN_ARGS="$MAVEN_ARGS -Pcentral"
else
echo Deactivating \"central\" profile for version=\"$VERSION\"
echo $MAVEN_ARGS | grep -q central && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pcentral//')
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} "$@"
${WRAPPER_LAUNCHER} ${MAVEN_ARGS} "$@"
Vendored
+143 -143
View File
@@ -1,145 +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 http://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"
@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 http://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%
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%
+86 -39
View File
@@ -2,17 +2,17 @@
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>1.1.1.RELEASE</version>
<relativePath />
</parent>
<artifactId>spring-cloud-netflix</artifactId>
<version>1.1.3.BUILD-SNAPSHOT</version>
<version>1.4.1.RELEASE</version>
<packaging>pom</packaging>
<name>Spring Cloud Netflix</name>
<description>Spring Cloud Netflix</description>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>1.3.7.RELEASE</version>
<relativePath />
</parent>
<scm>
<url>https://github.com/spring-cloud/spring-cloud-netflix</url>
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-netflix.git</connection>
@@ -22,9 +22,17 @@
<properties>
<bintray.package>netflix</bintray.package>
<main.basedir>${basedir}</main.basedir>
<netty.version>4.0.27.Final</netty.version>
<netty.version>4.0.27.Final</netty.version>
<jackson.version>2.7.3</jackson.version>
<spring-cloud-commons.version>1.3.1.RELEASE</spring-cloud-commons.version>
<spring-cloud-config.version>1.4.1.RELEASE</spring-cloud-config.version>
<spring-cloud-stream.version>Ditmars.SR3</spring-cloud-stream.version>
<!-- Has to be a stable version (not one that depends on this version of netflix): -->
<donotreplacespring-cloud-contract.version>1.1.2.RELEASE</donotreplacespring-cloud-contract.version>
<!-- Plugin versions -->
<maven-compiler-plugin.version>3.6.1</maven-compiler-plugin.version>
<maven-eclipse-plugin.version>2.10</maven-eclipse-plugin.version>
<!-- Sonar -->
<surefire.plugin.version>2.19.1</surefire.plugin.version>
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
@@ -37,6 +45,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>${maven-eclipse-plugin.version}</version>
<configuration>
<useProjectReferences>false</useProjectReferences>
<additionalConfig>
@@ -53,6 +62,7 @@
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
@@ -62,6 +72,12 @@
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-hystrix-contract</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-dependencies</artifactId>
@@ -69,6 +85,41 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons-dependencies</artifactId>
<version>${spring-cloud-commons.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
<type>test-jar</type>
<scope>test</scope>
<version>${spring-cloud-commons.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-dependencies</artifactId>
<version>${spring-cloud-config.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-dependencies</artifactId>
<version>${spring-cloud-stream.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-dependencies</artifactId>
<version>${donotreplacespring-cloud-contract.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec-http</artifactId>
@@ -88,6 +139,8 @@
</dependencyManagement>
<modules>
<module>spring-cloud-netflix-dependencies</module>
<!-- Not part of the reactor build: build and deploy separately -->
<!--module>spring-cloud-netflix-hystrix-contract</module-->
<module>spring-cloud-netflix-core</module>
<module>spring-cloud-netflix-hystrix-dashboard</module>
<module>spring-cloud-netflix-hystrix-amqp</module>
@@ -98,6 +151,7 @@
<module>spring-cloud-netflix-turbine</module>
<module>spring-cloud-netflix-turbine-stream</module>
<module>spring-cloud-netflix-sidecar</module>
<module>spring-cloud-starter-netflix</module>
<module>spring-cloud-starter-archaius</module>
<module>spring-cloud-starter-atlas</module>
<module>spring-cloud-starter-eureka</module>
@@ -120,7 +174,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>
@@ -131,7 +185,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>
@@ -139,7 +193,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>
@@ -149,7 +203,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>
@@ -160,7 +214,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>
@@ -168,7 +222,7 @@
<pluginRepository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>http://repo.spring.io/libs-release-local</url>
<url>https://repo.spring.io/libs-release-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
@@ -177,52 +231,45 @@
</profile>
<profile>
<id>sonar</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.4.201502262128</version>
<configuration>
<destFile>${sonar.jacoco.reportPath}</destFile>
<append>true</append>
</configuration>
<executions>
<execution>
<id>agent</id>
<id>pre-unit-test</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<propertyName>surefireArgLine</propertyName>
<destFile>${project.build.directory}/jacoco.exec</destFile>
</configuration>
</execution>
<execution>
<id>post-unit-test</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<!-- Sets the path to the file which contains the execution data. -->
<dataFile>${project.build.directory}/jacoco.exec</dataFile>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<!-- Minimal supported version is 2.4 -->
<version>${surefire.plugin.version}</version>
<configuration>
<properties>
<property>
<name>listener</name>
<value>org.sonar.java.jacoco.JUnitListener</value>
</property>
</properties>
<!-- Sets the VM argument line used when unit tests are run. -->
<argLine>${surefireArgLine}</argLine>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.sonarsource.java</groupId>
<artifactId>sonar-jacoco-listeners</artifactId>
<version>3.8</version>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
</profiles>
</project>
+4
View File
@@ -0,0 +1,4 @@
#!/bin/bash
(cd spring-cloud-netflix-hystrix-contract && ../mvnw clean install -B -Pdocs ${@})
./mvnw clean install -B -Pdocs ${@}
+2 -2
View File
@@ -14,9 +14,9 @@ curl "${SCRIPT_URL}" --output runAcceptanceTests.sh
chmod +x runAcceptanceTests.sh
echo "Killing all running apps"
./runAcceptanceTests.sh -t "${AT_WHAT_TO_TEST}" -n
./runAcceptanceTests.sh -t "${AT_WHAT_TO_TEST}" --killnow
./runAcceptanceTests.sh -t "${AT_WHAT_TO_TEST}" -k
./runAcceptanceTests.sh -t "${AT_WHAT_TO_TEST}" --killattheend
SCRIPT_URL="https://raw.githubusercontent.com/spring-cloud-samples/tests/master/scripts/runTests.sh"
+25 -21
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>1.1.3.BUILD-SNAPSHOT</version>
<version>1.4.1.RELEASE</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-netflix-core</artifactId>
@@ -34,11 +34,26 @@
<artifactId>spring-boot-starter-actuator</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
@@ -82,27 +97,27 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-slf4j</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-hystrix</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<optional>true</optional>
</dependency>
@@ -162,30 +177,19 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<!-- Only needed at compile time -->
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
@@ -17,30 +17,12 @@
package org.springframework.cloud.netflix.archaius;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.PreDestroy;
import org.apache.commons.configuration.AbstractConfiguration;
import org.apache.commons.configuration.ConfigurationBuilder;
import org.apache.commons.configuration.EnvironmentConfiguration;
import org.apache.commons.configuration.SystemConfiguration;
import org.apache.commons.configuration.event.ConfigurationEvent;
import org.apache.commons.configuration.event.ConfigurationListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;
@@ -49,6 +31,30 @@ import com.netflix.config.DynamicProperty;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.config.DynamicURLConfiguration;
import org.apache.commons.configuration.AbstractConfiguration;
import org.apache.commons.configuration.ConfigurationBuilder;
import org.apache.commons.configuration.EnvironmentConfiguration;
import org.apache.commons.configuration.SystemConfiguration;
import org.apache.commons.configuration.event.ConfigurationEvent;
import org.apache.commons.configuration.event.ConfigurationListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.util.ReflectionUtils;
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;
@@ -56,27 +62,32 @@ 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 lombok.extern.apachecommons.CommonsLog;
/**
* @author Spencer Gibb
*/
@Configuration
@ConditionalOnClass({ ConcurrentCompositeConfiguration.class,
ConfigurationBuilder.class })
@CommonsLog
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
public class ArchaiusAutoConfiguration {
private static final Log log = LogFactory.getLog(ArchaiusAutoConfiguration.class);
private static final AtomicBoolean initialized = new AtomicBoolean(false);
@Autowired
private ConfigurableEnvironment env;
@Autowired
private List<AbstractConfiguration> externalConfigurations;
@Autowired(required = false)
private List<AbstractConfiguration> externalConfigurations = new ArrayList<>();
private DynamicURLConfiguration defaultURLConfig;
@PreDestroy
public void close() {
if (defaultURLConfig != null) {
defaultURLConfig.stopLoading();
}
setStatic(ConfigurationManager.class, "instance", null);
setStatic(ConfigurationManager.class, "customConfigurationInstalled", false);
setStatic(DynamicPropertyFactory.class, "config", null);
@@ -150,8 +161,7 @@ public class ArchaiusAutoConfiguration {
config.addConfiguration(envConfig,
ConfigurableEnvironmentConfiguration.class.getSimpleName());
// below come from ConfigurationManager.createDefaultConfigInstance()
DynamicURLConfiguration defaultURLConfig = new DynamicURLConfiguration();
defaultURLConfig = new DynamicURLConfiguration();
try {
config.addConfiguration(defaultURLConfig, URL_CONFIG_NAME);
}
@@ -16,6 +16,7 @@
package org.springframework.cloud.netflix.endpoint;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -60,8 +61,8 @@ public abstract class ServletWrappingEndpoint implements InitializingBean,
this.controller.setApplicationContext(applicationContext);
}
protected ServletWrappingEndpoint(Class<?> servletClass, String servletName,
String path, boolean sensitive, boolean enabled) {
protected ServletWrappingEndpoint(Class<? extends Servlet> servletClass,
String servletName, String path, boolean sensitive, boolean enabled) {
this.controller.setServletClass(servletClass);
this.controller.setServletName(servletName);
this.path = path;
@@ -76,12 +77,13 @@ public abstract class ServletWrappingEndpoint implements InitializingBean,
}
public boolean isEnabled() {
return enabled;
return this.enabled;
}
public ServletWrappingController getController() {
return controller;
return this.controller;
}
@Override
public String getPath() {
return this.path;
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
package org.springframework.cloud.netflix.feign;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collection;
import feign.MethodMetadata;
@@ -25,6 +26,7 @@ import feign.MethodMetadata;
* Feign contract method parameter processor.
*
* @author Jakub Narloch
* @author Abhijit Sarkar
*/
public interface AnnotatedParameterProcessor {
@@ -38,11 +40,12 @@ public interface AnnotatedParameterProcessor {
/**
* Process the annotated parameter.
*
* @param context the parameter context
* @param context the parameter context
* @param annotation the annotation instance
* @param method the method that contains the annotation
* @return whether the parameter is http
*/
boolean processArgument(AnnotatedParameterContext context, Annotation annotation);
boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method);
/**
* Specifies the parameter context.
@@ -0,0 +1,38 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.feign;
import feign.Logger;
import feign.slf4j.Slf4jLogger;
/**
* @author Venil Noronha
*/
public class DefaultFeignLoggerFactory implements FeignLoggerFactory {
private Logger logger;
public DefaultFeignLoggerFactory(Logger logger) {
this.logger = logger;
}
@Override
public Logger create(Class<?> type) {
return this.logger != null ? this.logger : new Slf4jLogger(type);
}
}
@@ -0,0 +1,33 @@
/*
* Copyright 2013-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.netflix.feign;
import feign.Feign;
import feign.Target;
/**
* @author Spencer Gibb
*/
class DefaultTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
Target.HardCodedTarget<T> target) {
return feign.target(target);
}
}
@@ -18,14 +18,37 @@ package org.springframework.cloud.netflix.feign;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
import org.springframework.cloud.netflix.feign.support.FeignHttpClientProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import feign.Client;
import feign.Feign;
import feign.httpclient.ApacheHttpClient;
import feign.okhttp.OkHttpClient;
import okhttp3.ConnectionPool;
import javax.annotation.PreDestroy;
/**
* @author Spencer Gibb
@@ -33,6 +56,7 @@ import feign.Feign;
*/
@Configuration
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class})
public class FeignAutoConfiguration {
@Autowired(required = false)
@@ -49,4 +73,138 @@ public class FeignAutoConfiguration {
context.setConfigurations(this.configurations);
return context;
}
@Configuration
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
protected static class HystrixFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new HystrixTargeter();
}
}
@Configuration
@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
protected static class DefaultFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new DefaultTargeter();
}
}
// the following configuration is for alternate feign clients if
// ribbon is not on the class path.
// see corresponding configurations in FeignRibbonClientAutoConfiguration
// for load balanced ribbon clients.
@Configuration
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnMissingBean(CloseableHttpClient.class)
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
protected static class HttpClientFeignConfiguration {
private final Timer connectionManagerTimer = new Timer(
"FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
@Autowired(required = false)
private RegistryBuilder registryBuilder;
private CloseableHttpClient httpClient;
@Bean
@ConditionalOnMissingBean(HttpClientConnectionManager.class)
public HttpClientConnectionManager connectionManager(
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
FeignHttpClientProperties httpClientProperties) {
final HttpClientConnectionManager connectionManager = connectionManagerFactory
.newConnectionManager(false, httpClientProperties.getMaxConnections(),
httpClientProperties.getMaxConnectionsPerRoute(),
httpClientProperties.getTimeToLive(),
httpClientProperties.getTimeToLiveUnit(), registryBuilder);
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
public void run() {
connectionManager.closeExpiredConnections();
}
}, 30000, httpClientProperties.getConnectionTimerRepeat());
return connectionManager;
}
@Bean
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
HttpClientConnectionManager httpClientConnectionManager,
FeignHttpClientProperties httpClientProperties) {
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setConnectTimeout(httpClientProperties.getConnectionTimeout())
.setRedirectsEnabled(httpClientProperties.isFollowRedirects())
.build();
this.httpClient = httpClientFactory.createBuilder().
setConnectionManager(httpClientConnectionManager).
setDefaultRequestConfig(defaultRequestConfig).build();
return this.httpClient;
}
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(HttpClient httpClient) {
return new ApacheHttpClient(httpClient);
}
@PreDestroy
public void destroy() throws Exception {
connectionManagerTimer.cancel();
if(httpClient != null) {
httpClient.close();
}
}
}
@Configuration
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
@ConditionalOnProperty(value = "feign.okhttp.enabled")
protected static class OkHttpFeignConfiguration {
private okhttp3.OkHttpClient okHttpClient;
@Bean
@ConditionalOnMissingBean(ConnectionPool.class)
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
Long timeToLive = httpClientProperties.getTimeToLive();
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
}
@Bean
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
Boolean followRedirects = httpClientProperties.isFollowRedirects();
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
this.okHttpClient = httpClientFactory.createBuilder(false).
connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).
followRedirects(followRedirects).
connectionPool(connectionPool).build();
return this.okHttpClient;
}
@PreDestroy
public void destroy() {
if(okHttpClient != null) {
okHttpClient.dispatcher().executorService().shutdown();
okHttpClient.connectionPool().evictAll();
}
}
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient() {
return new OkHttpClient(this.okHttpClient);
}
}
}
@@ -59,6 +59,11 @@ public @interface FeignClient {
*/
@AliasFor("value")
String name() default "";
/**
* Sets the <code>@Qualifier</code> value for the feign client.
*/
String qualifier() default "";
/**
* An absolute URL or resolvable hostname (the protocol is optional).
@@ -85,10 +90,25 @@ public @interface FeignClient {
*/
Class<?> fallback() default void.class;
/**
* Define a fallback factory for the specified Feign client interface. The fallback
* factory must produce instances of fallback classes that implement the interface
* annotated by {@link FeignClient}. The fallback factory must be a valid spring
* bean.
*
* @see feign.hystrix.FallbackFactory for details.
*/
Class<?> fallbackFactory() default void.class;
/**
* Path prefix to be used by all method-level mappings. Can be used with or without
* <code>@RibbonClient</code>.
*/
String path() default "";
/**
* Whether to mark the feign proxy as a primary bean. Defaults to true.
*/
boolean primary() default true;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2016 the original author or authors.
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,14 +17,17 @@
package org.springframework.cloud.netflix.feign;
import java.util.Map;
import java.util.Objects;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.cloud.netflix.feign.ribbon.LoadBalancerFeignClient;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import feign.Client;
@@ -38,32 +41,18 @@ import feign.Target.HardCodedTarget;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.codec.ErrorDecoder;
import feign.slf4j.Slf4jLogger;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author Spencer Gibb
* @author Venil Noronha
* @author Eko Kurniawan Khannedy
* @author Gregor Zurowski
*/
@Data
@EqualsAndHashCode(callSuper = false)
class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
ApplicationContextAware {
private static final Targeter targeter;
static {
Targeter targeterToUse;
if (ClassUtils.isPresent("feign.hystrix.HystrixFeign",
FeignClientFactoryBean.class.getClassLoader())) {
targeterToUse = new HystrixTargeter();
}
else {
targeterToUse = new DefaultTargeter();
}
targeter = targeterToUse;
}
/***********************************
* WARNING! Nothing in this class should be @Autowired. It causes NPEs because of some lifecycle race condition.
***********************************/
private Class<?> type;
@@ -79,6 +68,8 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
private Class<?> fallback = void.class;
private Class<?> fallbackFactory = void.class;
@Override
public void afterPropertiesSet() throws Exception {
Assert.hasText(this.name, "Name must be set");
@@ -90,11 +81,8 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
}
protected Feign.Builder feign(FeignContext context) {
Logger logger = getOptional(context, Logger.class);
if (logger == null) {
logger = new Slf4jLogger(this.type);
}
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
Logger logger = loggerFactory.create(this.type);
// @formatter:off
Feign.Builder builder = get(context, Feign.Builder.class)
@@ -105,7 +93,29 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
.contract(get(context, Contract.class));
// @formatter:on
// optional values
configureFeign(context, builder);
return builder;
}
protected void configureFeign(FeignContext context, Feign.Builder builder) {
FeignClientProperties properties = applicationContext.getBean(FeignClientProperties.class);
if (properties != null) {
if (properties.isDefaultToProperties()) {
configureUsingConfiguration(context, builder);
configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
configureUsingProperties(properties.getConfig().get(this.name), builder);
} else {
configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
configureUsingProperties(properties.getConfig().get(this.name), builder);
configureUsingConfiguration(context, builder);
}
} else {
configureUsingConfiguration(context, builder);
}
}
protected void configureUsingConfiguration(FeignContext context, Feign.Builder builder) {
Logger.Level level = getOptional(context, Logger.Level.class);
if (level != null) {
builder.logLevel(level);
@@ -131,8 +141,52 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
if (decode404) {
builder.decode404();
}
}
return builder;
protected void configureUsingProperties(FeignClientProperties.FeignClientConfiguration config, Feign.Builder builder) {
if (config == null) {
return;
}
if (config.getLoggerLevel() != null) {
builder.logLevel(config.getLoggerLevel());
}
if (config.getConnectTimeout() != null && config.getReadTimeout() != null) {
builder.options(new Request.Options(config.getConnectTimeout(), config.getReadTimeout()));
}
if (config.getRetryer() != null) {
Retryer retryer = getOrInstantiate(config.getRetryer());
builder.retryer(retryer);
}
if (config.getErrorDecoder() != null) {
ErrorDecoder errorDecoder = getOrInstantiate(config.getErrorDecoder());
builder.errorDecoder(errorDecoder);
}
if (config.getRequestInterceptors() != null && !config.getRequestInterceptors().isEmpty()) {
// this will add request interceptor to builder, not replace existing
for (Class<RequestInterceptor> bean : config.getRequestInterceptors()) {
RequestInterceptor interceptor = getOrInstantiate(bean);
builder.requestInterceptor(interceptor);
}
}
if (config.getDecode404() != null) {
if (config.getDecode404()) {
builder.decode404();
}
}
}
private <T> T getOrInstantiate(Class<T> tClass) {
try {
return applicationContext.getBean(tClass);
} catch (NoSuchBeanDefinitionException e) {
return BeanUtils.instantiateClass(tClass);
}
}
protected <T> T get(FeignContext context, Class<T> type) {
@@ -153,11 +207,12 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, target);
}
throw new IllegalStateException(
"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-ribbon?");
"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
}
@Override
@@ -181,6 +236,16 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
this.url = "http://" + this.url;
}
String url = this.url + cleanPath();
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof LoadBalancerFeignClient) {
// not lod balancing because we have a url,
// but ribbon is on the classpath, so unwrap
client = ((LoadBalancerFeignClient)client).getDelegate();
}
builder.client(client);
}
Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, new HardCodedTarget<>(
this.type, this.name, url));
}
@@ -208,48 +273,99 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
return true;
}
interface Targeter {
<T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
HardCodedTarget<T> target);
public Class<?> getType() {
return type;
}
static class DefaultTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
HardCodedTarget<T> target) {
return feign.target(target);
}
public void setType(Class<?> type) {
this.type = type;
}
@SuppressWarnings("unchecked")
static class HystrixTargeter implements Targeter {
public String getName() {
return name;
}
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
HardCodedTarget<T> target) {
if (factory.fallback == void.class
|| !(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
public void setName(String name) {
this.name = name;
}
Object fallbackInstance = context.getInstance(factory.name, factory.fallback);
if (fallbackInstance == null) {
throw new IllegalStateException(String.format(
"No fallback instance of type %s found for feign client %s",
factory.fallback, factory.name));
}
public String getUrl() {
return url;
}
if (!target.type().isAssignableFrom(factory.fallback)) {
throw new IllegalStateException(
String.format(
"Incompatible fallback instance. Fallback of type %s is not assignable to %s for feign client %s",
factory.fallback, target.type(), factory.name));
}
public void setUrl(String url) {
this.url = url;
}
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
return builder.target(target, (T) fallbackInstance);
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public boolean isDecode404() {
return decode404;
}
public void setDecode404(boolean decode404) {
this.decode404 = decode404;
}
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public Class<?> getFallback() {
return fallback;
}
public void setFallback(Class<?> fallback) {
this.fallback = fallback;
}
public Class<?> getFallbackFactory() {
return fallbackFactory;
}
public void setFallbackFactory(Class<?> fallbackFactory) {
this.fallbackFactory = fallbackFactory;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FeignClientFactoryBean that = (FeignClientFactoryBean) o;
return Objects.equals(applicationContext, that.applicationContext) &&
decode404 == that.decode404 &&
Objects.equals(fallback, that.fallback) &&
Objects.equals(fallbackFactory, that.fallbackFactory) &&
Objects.equals(name, that.name) &&
Objects.equals(path, that.path) &&
Objects.equals(type, that.type) &&
Objects.equals(url, that.url);
}
@Override
public int hashCode() {
return Objects.hash(applicationContext, decode404, fallback, fallbackFactory,
name, path, type, url);
}
@Override
public String toString() {
return new StringBuilder("FeignClientFactoryBean{")
.append("type=").append(type).append(", ")
.append("name='").append(name).append("', ")
.append("url='").append(url).append("', ")
.append("path='").append(path).append("', ")
.append("decode404=").append(decode404).append(", ")
.append("applicationContext=").append(applicationContext).append(", ")
.append("fallback=").append(fallback).append(", ")
.append("fallbackFactory=").append(fallbackFactory)
.append("}").toString();
}
}
@@ -0,0 +1,173 @@
/*
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.feign;
import feign.Logger;
import feign.RequestInterceptor;
import feign.Retryer;
import feign.codec.ErrorDecoder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* @author Eko Kurniawan Khannedy
*/
@ConfigurationProperties("feign.client")
public class FeignClientProperties {
private boolean defaultToProperties = true;
private String defaultConfig = "default";
private Map<String, FeignClientConfiguration> config = new HashMap<>();
public boolean isDefaultToProperties() {
return defaultToProperties;
}
public void setDefaultToProperties(boolean defaultToProperties) {
this.defaultToProperties = defaultToProperties;
}
public String getDefaultConfig() {
return defaultConfig;
}
public void setDefaultConfig(String defaultConfig) {
this.defaultConfig = defaultConfig;
}
public Map<String, FeignClientConfiguration> getConfig() {
return config;
}
public void setConfig(Map<String, FeignClientConfiguration> config) {
this.config = config;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FeignClientProperties that = (FeignClientProperties) o;
return defaultToProperties == that.defaultToProperties &&
Objects.equals(defaultConfig, that.defaultConfig) &&
Objects.equals(config, that.config);
}
@Override
public int hashCode() {
return Objects.hash(defaultToProperties, defaultConfig, config);
}
public static class FeignClientConfiguration {
private Logger.Level loggerLevel;
private Integer connectTimeout;
private Integer readTimeout;
private Class<Retryer> retryer;
private Class<ErrorDecoder> errorDecoder;
private List<Class<RequestInterceptor>> requestInterceptors;
private Boolean decode404;
public Logger.Level getLoggerLevel() {
return loggerLevel;
}
public void setLoggerLevel(Logger.Level loggerLevel) {
this.loggerLevel = loggerLevel;
}
public Integer getConnectTimeout() {
return connectTimeout;
}
public void setConnectTimeout(Integer connectTimeout) {
this.connectTimeout = connectTimeout;
}
public Integer getReadTimeout() {
return readTimeout;
}
public void setReadTimeout(Integer readTimeout) {
this.readTimeout = readTimeout;
}
public Class<Retryer> getRetryer() {
return retryer;
}
public void setRetryer(Class<Retryer> retryer) {
this.retryer = retryer;
}
public Class<ErrorDecoder> getErrorDecoder() {
return errorDecoder;
}
public void setErrorDecoder(Class<ErrorDecoder> errorDecoder) {
this.errorDecoder = errorDecoder;
}
public List<Class<RequestInterceptor>> getRequestInterceptors() {
return requestInterceptors;
}
public void setRequestInterceptors(List<Class<RequestInterceptor>> requestInterceptors) {
this.requestInterceptors = requestInterceptors;
}
public Boolean getDecode404() {
return decode404;
}
public void setDecode404(Boolean decode404) {
this.decode404 = decode404;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FeignClientConfiguration that = (FeignClientConfiguration) o;
return loggerLevel == that.loggerLevel &&
Objects.equals(connectTimeout, that.connectTimeout) &&
Objects.equals(readTimeout, that.readTimeout) &&
Objects.equals(retryer, that.retryer) &&
Objects.equals(errorDecoder, that.errorDecoder) &&
Objects.equals(requestInterceptors, that.requestInterceptors) &&
Objects.equals(decode404, that.decode404);
}
@Override
public int hashCode() {
return Objects.hash(loggerLevel, connectTimeout, readTimeout, retryer,
errorDecoder, requestInterceptors, decode404);
}
}
}
@@ -18,20 +18,62 @@ package org.springframework.cloud.netflix.feign;
import org.springframework.cloud.context.named.NamedContextFactory;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Arrays;
import java.util.Objects;
/**
* @author Dave Syer
* @author Gregor Zurowski
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FeignClientSpecification implements NamedContextFactory.Specification {
class FeignClientSpecification implements NamedContextFactory.Specification {
private String name;
private Class<?>[] configuration;
public FeignClientSpecification() {}
public FeignClientSpecification(String name, Class<?>[] configuration) {
this.name = name;
this.configuration = configuration;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Class<?>[] getConfiguration() {
return configuration;
}
public void setConfiguration(Class<?>[] configuration) {
this.configuration = configuration;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FeignClientSpecification that = (FeignClientSpecification) o;
return Objects.equals(name, that.name) &&
Arrays.equals(configuration, that.configuration);
}
@Override
public int hashCode() {
return Objects.hash(name, configuration);
}
@Override
public String toString() {
return new StringBuilder("FeignClientSpecification{")
.append("name='").append(name).append("', ")
.append("configuration=").append(Arrays.toString(configuration))
.append("}").toString();
}
}
@@ -19,7 +19,6 @@ package org.springframework.cloud.netflix.feign;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.client.HttpClient;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -39,16 +38,17 @@ import org.springframework.format.support.FormattingConversionService;
import com.netflix.hystrix.HystrixCommand;
import feign.Client;
import feign.Contract;
import feign.Feign;
import feign.Logger;
import feign.Retryer;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.httpclient.ApacheHttpClient;
import feign.hystrix.HystrixFeign;
/**
* @author Dave Syer
* @author Venil Noronha
*/
@Configuration
public class FeignClientsConfiguration {
@@ -62,6 +62,9 @@ public class FeignClientsConfiguration {
@Autowired(required = false)
private List<FeignFormatterRegistrar> feignFormatterRegistrars = new ArrayList<>();
@Autowired(required = false)
private Logger logger;
@Bean
@ConditionalOnMissingBean
public Decoder feignDecoder() {
@@ -95,33 +98,29 @@ public class FeignClientsConfiguration {
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "feign.hystrix.enabled", matchIfMissing = true)
@ConditionalOnProperty(name = "feign.hystrix.enabled", matchIfMissing = false)
public Feign.Builder feignHystrixBuilder() {
return HystrixFeign.builder();
}
}
@Bean
@ConditionalOnMissingBean
public Retryer feignRetryer() {
return Retryer.NEVER_RETRY;
}
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
public Feign.Builder feignBuilder() {
return Feign.builder();
public Feign.Builder feignBuilder(Retryer retryer) {
return Feign.builder().retryer(retryer);
}
@Configuration
@ConditionalOnClass(ApacheHttpClient.class)
protected static class HttpClientConfiguration {
@Autowired(required = false)
private HttpClient httpClient;
@ConditionalOnMissingBean
@Bean
public Client feignClient() {
if (this.httpClient != null) {
return new ApacheHttpClient(this.httpClient);
}
return new ApacheHttpClient();
}
@Bean
@ConditionalOnMissingBean(FeignLoggerFactory.class)
public FeignLoggerFactory feignLoggerFactory() {
return new DefaultFeignLoggerFactory(logger);
}
}
@@ -37,9 +37,12 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
@@ -57,8 +60,8 @@ import org.springframework.util.StringUtils;
* @author Jakub Narloch
* @author Venil Noronha
*/
public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
ResourceLoaderAware, BeanClassLoaderAware {
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
// patterned after Spring Integration IntegrationComponentScanRegistrar
// and RibbonClientsConfigurationRegistgrar
@@ -67,6 +70,8 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
private ClassLoader classLoader;
private Environment environment;
public FeignClientsRegistrar() {
}
@@ -178,30 +183,33 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
definition.addPropertyValue("type", className);
definition.addPropertyValue("decode404", attributes.get("decode404"));
definition.addPropertyValue("fallback", attributes.get("fallback"));
definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
String alias = name + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
beanDefinition.setPrimary(true);
boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null
beanDefinition.setPrimary(primary);
String qualifier = getQualifier(attributes);
if (StringUtils.hasText(qualifier)) {
alias = qualifier;
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
private void validate(Map<String, Object> attributes) {
if (StringUtils.hasText((String) attributes.get("value"))) {
Assert.isTrue(!StringUtils.hasText((String) attributes.get("name")),
"Either name or value can be specified, but not both");
Assert.isTrue(!StringUtils.hasText((String) attributes.get("serviceId")),
"Either name (serviceId) or value can be specified, but not both");
}
if (StringUtils.hasText((String) attributes.get("name"))) {
Assert.isTrue(!StringUtils.hasText((String) attributes.get("serviceId")),
"Either name or serviceId can be specified, but not both");
}
AnnotationAttributes annotation = AnnotationAttributes.fromMap(attributes);
// This blows up if an aliased property is overspecified
annotation.getAliasedString("name", FeignClient.class, null);
}
private String getName(Map<String, Object> attributes) {
/* for testing */ String getName(Map<String, Object> attributes) {
String name = (String) attributes.get("serviceId");
if (!StringUtils.hasText(name)) {
name = (String) attributes.get("name");
@@ -216,7 +224,14 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
String host = null;
try {
host = new URI("http://" + name).getHost();
String url;
if (!name.startsWith("http://") && !name.startsWith("https://")) {
url = "http://" + name;
} else {
url = name;
}
host = new URI(url).getHost();
}
catch (URISyntaxException e) {
}
@@ -225,17 +240,15 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
}
private String resolve(String value) {
if (StringUtils.hasText(value)
&& this.resourceLoader instanceof ConfigurableApplicationContext) {
return ((ConfigurableApplicationContext) this.resourceLoader).getEnvironment()
.resolvePlaceholders(value);
if (StringUtils.hasText(value)) {
return this.environment.resolvePlaceholders(value);
}
return value;
}
private String getUrl(Map<String, Object> attributes) {
String url = resolve((String) attributes.get("url"));
if (StringUtils.hasText(url)) {
if (StringUtils.hasText(url) && !(url.startsWith("#{") && url.contains("}"))) {
if (!url.contains("://")) {
url = "http://" + url;
}
@@ -264,7 +277,7 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
}
protected ClassPathScanningCandidateComponentProvider getScanner() {
return new ClassPathScanningCandidateComponentProvider(false) {
return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
@Override
protected boolean isCandidateComponent(
@@ -323,6 +336,17 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
}
return basePackages;
}
private String getQualifier(Map<String, Object> client) {
if (client == null) {
return null;
}
String qualifier = (String) client.get("qualifier");
if (StringUtils.hasText(qualifier)) {
return qualifier;
}
return null;
}
private String getClientName(Map<String, Object> client) {
if (client == null) {
@@ -354,6 +378,11 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
builder.getBeanDefinition());
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
/**
* Helper class to create a {@link TypeFilter} that matches if all the delegates
* match.
@@ -0,0 +1,36 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.feign;
import feign.Logger;
/**
* Allows an application to use a custom Feign {@link Logger}.
*
* @author Venil Noronha
*/
public interface FeignLoggerFactory {
/**
* Factory method to provide a {@link Logger} for a given {@link Class}.
*
* @param type the {@link Class} for which a {@link Logger} instance is to be created
* @return a {@link Logger} instance
*/
public Logger create(Class<?> type);
}
@@ -0,0 +1,111 @@
/*
* Copyright 2013-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.netflix.feign;
import org.springframework.util.Assert;
import feign.Feign;
import feign.Target;
import feign.hystrix.FallbackFactory;
import feign.hystrix.HystrixFeign;
import feign.hystrix.SetterFactory;
/**
* @author Spencer Gibb
* @author Erik Kringen
*/
@SuppressWarnings("unchecked")
class HystrixTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
Target.HardCodedTarget<T> target) {
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
SetterFactory setterFactory = getOptional(factory.getName(), context,
SetterFactory.class);
if (setterFactory != null) {
builder.setterFactory(setterFactory);
}
Class<?> fallback = factory.getFallback();
if (fallback != void.class) {
return targetWithFallback(factory.getName(), context, target, builder, fallback);
}
Class<?> fallbackFactory = factory.getFallbackFactory();
if (fallbackFactory != void.class) {
return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory);
}
return feign.target(target);
}
private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
Target.HardCodedTarget<T> target,
HystrixFeign.Builder builder,
Class<?> fallbackFactoryClass) {
FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>)
getFromContext("fallbackFactory", feignClientName, context, fallbackFactoryClass, FallbackFactory.class);
/* We take a sample fallback from the fallback factory to check if it returns a fallback
that is compatible with the annotated feign interface. */
Object exampleFallback = fallbackFactory.create(new RuntimeException());
Assert.notNull(exampleFallback,
String.format(
"Incompatible fallbackFactory instance for feign client %s. Factory may not produce null!",
feignClientName));
if (!target.type().isAssignableFrom(exampleFallback.getClass())) {
throw new IllegalStateException(
String.format(
"Incompatible fallbackFactory instance for feign client %s. Factory produces instances of '%s', but should produce instances of '%s'",
feignClientName, exampleFallback.getClass(), target.type()));
}
return builder.target(target, fallbackFactory);
}
private <T> T targetWithFallback(String feignClientName, FeignContext context,
Target.HardCodedTarget<T> target,
HystrixFeign.Builder builder, Class<?> fallback) {
T fallbackInstance = getFromContext("fallback", feignClientName, context, fallback, target.type());
return builder.target(target, fallbackInstance);
}
private <T> T getFromContext(String fallbackMechanism, String feignClientName, FeignContext context,
Class<?> beanType, Class<T> targetType) {
Object fallbackInstance = context.getInstance(feignClientName, beanType);
if (fallbackInstance == null) {
throw new IllegalStateException(String.format(
"No " + fallbackMechanism + " instance of type %s found for feign client %s",
beanType, feignClientName));
}
if (!targetType.isAssignableFrom(beanType)) {
throw new IllegalStateException(
String.format(
"Incompatible " + fallbackMechanism + " instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
beanType, targetType, feignClientName));
}
return (T) fallbackInstance;
}
private <T> T getOptional(String feignClientName, FeignContext context,
Class<T> beanType) {
return context.getInstance(feignClientName, beanType);
}
}
@@ -0,0 +1,29 @@
/*
* Copyright 2013-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.netflix.feign;
import feign.Feign;
import feign.Target;
/**
* @author Spencer Gibb
*/
interface Targeter {
<T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
Target.HardCodedTarget<T> target);
}
@@ -17,6 +17,7 @@
package org.springframework.cloud.netflix.feign.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
@@ -32,44 +33,45 @@ import static feign.Util.emptyToNull;
* {@link PathVariable} parameter processor.
*
* @author Jakub Narloch
* @author Abhijit Sarkar
* @see AnnotatedParameterProcessor
*/
public class PathVariableParameterProcessor implements AnnotatedParameterProcessor {
private static final Class<PathVariable> ANNOTATION = PathVariable.class;
private static final Class<PathVariable> ANNOTATION = PathVariable.class;
@Override
public Class<? extends Annotation> getAnnotationType() {
return ANNOTATION;
}
@Override
public Class<? extends Annotation> getAnnotationType() {
return ANNOTATION;
}
@Override
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation) {
String name = ANNOTATION.cast(annotation).value();
checkState(emptyToNull(name) != null,
"PathVariable annotation was empty on param %s.", context.getParameterIndex());
context.setParameterName(name);
@Override
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
String name = ANNOTATION.cast(annotation).value();
checkState(emptyToNull(name) != null,
"PathVariable annotation was empty on param %s.", context.getParameterIndex());
context.setParameterName(name);
MethodMetadata data = context.getMethodMetadata();
String varName = '{' + name + '}';
if (!data.template().url().contains(varName)
&& !searchMapValues(data.template().queries(), varName)
&& !searchMapValues(data.template().headers(), varName)) {
data.formParams().add(name);
}
return true;
}
MethodMetadata data = context.getMethodMetadata();
String varName = '{' + name + '}';
if (!data.template().url().contains(varName)
&& !searchMapValues(data.template().queries(), varName)
&& !searchMapValues(data.template().headers(), varName)) {
data.formParams().add(name);
}
return true;
}
private <K, V> boolean searchMapValues(Map<K, Collection<V>> map, V search) {
Collection<Collection<V>> values = map.values();
if (values == null) {
return false;
}
for (Collection<V> entry : values) {
if (entry.contains(search)) {
return true;
}
}
return false;
}
private <K, V> boolean searchMapValues(Map<K, Collection<V>> map, V search) {
Collection<Collection<V>> values = map.values();
if (values == null) {
return false;
}
for (Collection<V> entry : values) {
if (entry.contains(search)) {
return true;
}
}
return false;
}
}
@@ -17,7 +17,9 @@
package org.springframework.cloud.netflix.feign.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import org.springframework.cloud.netflix.feign.AnnotatedParameterProcessor;
import org.springframework.web.bind.annotation.RequestHeader;
@@ -31,27 +33,38 @@ import static feign.Util.emptyToNull;
* {@link RequestHeader} parameter processor.
*
* @author Jakub Narloch
* @author Abhijit Sarkar
* @see AnnotatedParameterProcessor
*/
public class RequestHeaderParameterProcessor implements AnnotatedParameterProcessor {
private static final Class<RequestHeader> ANNOTATION = RequestHeader.class;
private static final Class<RequestHeader> ANNOTATION = RequestHeader.class;
@Override
public Class<? extends Annotation> getAnnotationType() {
return ANNOTATION;
}
@Override
public Class<? extends Annotation> getAnnotationType() {
return ANNOTATION;
}
@Override
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation) {
String name = ANNOTATION.cast(annotation).value();
checkState(emptyToNull(name) != null,
"RequestHeader.value() was empty on parameter %s", context.getParameterIndex());
context.setParameterName(name);
@Override
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
int parameterIndex = context.getParameterIndex();
Class<?> parameterType = method.getParameterTypes()[parameterIndex];
MethodMetadata data = context.getMethodMetadata();
MethodMetadata data = context.getMethodMetadata();
Collection<String> header = context.setTemplateParameter(name, data.template().headers().get(name));
data.template().header(name, header);
return true;
}
if (Map.class.isAssignableFrom(parameterType)) {
checkState(data.headerMapIndex() == null, "Header map can only be present once.");
data.headerMapIndex(parameterIndex);
return true;
}
String name = ANNOTATION.cast(annotation).value();
checkState(emptyToNull(name) != null,
"RequestHeader.value() was empty on parameter %s", parameterIndex);
context.setParameterName(name);
Collection<String> header = context.setTemplateParameter(name, data.template().headers().get(name));
data.template().header(name, header);
return true;
}
}
@@ -17,7 +17,9 @@
package org.springframework.cloud.netflix.feign.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import org.springframework.cloud.netflix.feign.AnnotatedParameterProcessor;
import org.springframework.web.bind.annotation.RequestParam;
@@ -31,6 +33,7 @@ import feign.MethodMetadata;
* {@link RequestParam} parameter processor.
*
* @author Jakub Narloch
* @author Abhijit Sarkar
* @see AnnotatedParameterProcessor
*/
public class RequestParamParameterProcessor implements AnnotatedParameterProcessor {
@@ -43,16 +46,25 @@ public class RequestParamParameterProcessor implements AnnotatedParameterProcess
}
@Override
public boolean processArgument(AnnotatedParameterContext context,
Annotation annotation) {
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
int parameterIndex = context.getParameterIndex();
Class<?> parameterType = method.getParameterTypes()[parameterIndex];
MethodMetadata data = context.getMethodMetadata();
if (Map.class.isAssignableFrom(parameterType)) {
checkState(data.queryMapIndex() == null, "Query map can only be present once.");
data.queryMapIndex(parameterIndex);
return true;
}
RequestParam requestParam = ANNOTATION.cast(annotation);
String name = requestParam.value();
checkState(emptyToNull(name) != null,
"RequestParam.value() was empty on parameter %s",
context.getParameterIndex());
parameterIndex);
context.setParameterName(name);
MethodMetadata data = context.getMethodMetadata();
Collection<String> query = context.setTemplateParameter(name,
data.template().queries().get(name));
data.template().query(name, query);
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,8 +18,6 @@ package org.springframework.cloud.netflix.feign.encoding;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.AccessLevel;
import lombok.Getter;
import org.springframework.util.Assert;
/**
@@ -29,33 +27,37 @@ import org.springframework.util.Assert;
*/
public abstract class BaseRequestInterceptor implements RequestInterceptor {
/**
* The encoding properties.
*/
@Getter(AccessLevel.PROTECTED)
private final FeignClientEncodingProperties properties;
/**
* The encoding properties.
*/
private final FeignClientEncodingProperties properties;
/**
* Creates new instance of {@link BaseRequestInterceptor}.
*
* @param properties the encoding properties
*/
protected BaseRequestInterceptor(FeignClientEncodingProperties properties) {
Assert.notNull(properties, "Properties can not be null");
this.properties = properties;
}
/**
* Creates new instance of {@link BaseRequestInterceptor}.
*
* @param properties the encoding properties
*/
protected BaseRequestInterceptor(FeignClientEncodingProperties properties) {
Assert.notNull(properties, "Properties can not be null");
this.properties = properties;
}
/**
* Adds the header if it wasn't yet specified.
*
* @param requestTemplate the request
* @param name the header name
* @param values the header values
*/
protected void addHeader(RequestTemplate requestTemplate, String name, String... values) {
/**
* Adds the header if it wasn't yet specified.
*
* @param requestTemplate the request
* @param name the header name
* @param values the header values
*/
protected void addHeader(RequestTemplate requestTemplate, String name, String... values) {
if (!requestTemplate.headers().containsKey(name)) {
requestTemplate.header(name, values);
}
}
protected FeignClientEncodingProperties getProperties() {
return properties;
}
if (!requestTemplate.headers().containsKey(name)) {
requestTemplate.header(name, values);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,8 @@ package org.springframework.cloud.netflix.feign.encoding;
import feign.Feign;
import feign.httpclient.ApacheHttpClient;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -38,11 +39,11 @@ import org.springframework.context.annotation.Configuration;
@ConditionalOnClass(Feign.class)
@ConditionalOnBean(ApacheHttpClient.class)
@ConditionalOnProperty(value = "feign.compression.response.enabled", matchIfMissing = false)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@AutoConfigureAfter(FeignAutoConfiguration.class)
public class FeignAcceptGzipEncodingAutoConfiguration {
@Bean
public FeignAcceptGzipEncodingInterceptor feignAcceptGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
return new FeignAcceptGzipEncodingInterceptor(properties);
}
@Bean
public FeignAcceptGzipEncodingInterceptor feignAcceptGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
return new FeignAcceptGzipEncodingInterceptor(properties);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,22 +28,22 @@ import feign.RequestTemplate;
*/
public class FeignAcceptGzipEncodingInterceptor extends BaseRequestInterceptor {
/**
* Creates new instance of {@link FeignAcceptGzipEncodingInterceptor}.
*
* @param properties the encoding properties
*/
protected FeignAcceptGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
super(properties);
}
/**
* Creates new instance of {@link FeignAcceptGzipEncodingInterceptor}.
*
* @param properties the encoding properties
*/
protected FeignAcceptGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
super(properties);
}
/**
* {@inheritDoc}
*/
@Override
public void apply(RequestTemplate template) {
/**
* {@inheritDoc}
*/
@Override
public void apply(RequestTemplate template) {
addHeader(template, HttpEncoding.ACCEPT_ENCODING_HEADER, HttpEncoding.GZIP_ENCODING,
HttpEncoding.DEFLATE_ENCODING);
}
addHeader(template, HttpEncoding.ACCEPT_ENCODING_HEADER, HttpEncoding.GZIP_ENCODING,
HttpEncoding.DEFLATE_ENCODING);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,25 +16,65 @@
package org.springframework.cloud.netflix.feign.encoding;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.Arrays;
import java.util.Objects;
/**
* The Feign encoding properties.
*
* @author Jakub Narloch
*/
@Data
@ConfigurationProperties("feign.compression.request")
public class FeignClientEncodingProperties {
/**
* The list of supported mime types.
*/
private String[] mimeTypes = new String[]{"text/xml", "application/xml", "application/json"};
/**
* The list of supported mime types.
*/
private String[] mimeTypes = new String[]{"text/xml", "application/xml", "application/json"};
/**
* The minimum threshold content size.
*/
private int minRequestSize = 2048;
public String[] getMimeTypes() {
return mimeTypes;
}
public void setMimeTypes(String[] mimeTypes) {
this.mimeTypes = mimeTypes;
}
public int getMinRequestSize() {
return minRequestSize;
}
public void setMinRequestSize(int minRequestSize) {
this.minRequestSize = minRequestSize;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FeignClientEncodingProperties that = (FeignClientEncodingProperties) o;
return Arrays.equals(mimeTypes, that.mimeTypes) &&
Objects.equals(minRequestSize, that.minRequestSize);
}
@Override
public int hashCode() {
return Objects.hash(mimeTypes, minRequestSize);
}
@Override
public String toString() {
return new StringBuilder("FeignClientEncodingProperties{")
.append("mimeTypes=").append(Arrays.toString(mimeTypes)).append(", ")
.append("minRequestSize=").append(minRequestSize)
.append("}").toString();
}
/**
* The minimum threshold content size.
*/
private int minRequestSize = 2048;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,8 @@ package org.springframework.cloud.netflix.feign.encoding;
import feign.Feign;
import feign.httpclient.ApacheHttpClient;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -38,11 +39,11 @@ import org.springframework.context.annotation.Configuration;
@ConditionalOnClass(Feign.class)
@ConditionalOnBean(ApacheHttpClient.class)
@ConditionalOnProperty(value = "feign.compression.request.enabled", matchIfMissing = false)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@AutoConfigureAfter(FeignAutoConfiguration.class)
public class FeignContentGzipEncodingAutoConfiguration {
@Bean
public FeignContentGzipEncodingInterceptor feignContentGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
return new FeignContentGzipEncodingInterceptor(properties);
}
@Bean
public FeignContentGzipEncodingInterceptor feignContentGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
return new FeignContentGzipEncodingInterceptor(properties);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,84 +28,84 @@ import java.util.Map;
*/
public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor {
/**
* Creates new instance of {@link FeignContentGzipEncodingInterceptor}.
*
* @param properties the encoding properties
*/
protected FeignContentGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
super(properties);
}
/**
* Creates new instance of {@link FeignContentGzipEncodingInterceptor}.
*
* @param properties the encoding properties
*/
protected FeignContentGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
super(properties);
}
/**
* {@inheritDoc}
*/
@Override
public void apply(RequestTemplate template) {
/**
* {@inheritDoc}
*/
@Override
public void apply(RequestTemplate template) {
if (requiresCompression(template)) {
addHeader(template, HttpEncoding.CONTENT_ENCODING_HEADER, HttpEncoding.GZIP_ENCODING,
HttpEncoding.DEFLATE_ENCODING);
}
}
if (requiresCompression(template)) {
addHeader(template, HttpEncoding.CONTENT_ENCODING_HEADER, HttpEncoding.GZIP_ENCODING,
HttpEncoding.DEFLATE_ENCODING);
}
}
/**
* Returns whether the request requires GZIP compression.
*
* @param template the request template
* @return true if request requires compression, false otherwise
*/
private boolean requiresCompression(RequestTemplate template) {
/**
* Returns whether the request requires GZIP compression.
*
* @param template the request template
* @return true if request requires compression, false otherwise
*/
private boolean requiresCompression(RequestTemplate template) {
final Map<String, Collection<String>> headers = template.headers();
return matchesMimeType(headers.get(HttpEncoding.CONTENT_TYPE))
&& contentLengthExceedThreshold(headers.get(HttpEncoding.CONTENT_LENGTH));
}
final Map<String, Collection<String>> headers = template.headers();
return matchesMimeType(headers.get(HttpEncoding.CONTENT_TYPE))
&& contentLengthExceedThreshold(headers.get(HttpEncoding.CONTENT_LENGTH));
}
/**
* Returns whether the request content length exceed configured minimum size.
*
* @param contentLength the content length header value
* @return true if length is grater than minimum size, false otherwise
*/
private boolean contentLengthExceedThreshold(Collection<String> contentLength) {
/**
* Returns whether the request content length exceed configured minimum size.
*
* @param contentLength the content length header value
* @return true if length is grater than minimum size, false otherwise
*/
private boolean contentLengthExceedThreshold(Collection<String> contentLength) {
try {
if (contentLength.size() != 1) {
return false;
}
try {
if (contentLength == null || contentLength.size() != 1) {
return false;
}
final String strLen = contentLength.iterator().next();
final long length = Long.parseLong(strLen);
return length > getProperties().getMinRequestSize();
} catch (NumberFormatException ex) {
// ignores the exception
}
return false;
}
final String strLen = contentLength.iterator().next();
final long length = Long.parseLong(strLen);
return length > getProperties().getMinRequestSize();
} catch (NumberFormatException ex) {
// ignores the exception
}
return false;
}
/**
* Returns whether the content mime types matches the configures mime types.
*
* @param contentTypes the content types
* @return true if any specified content type matches the request content types
*/
private boolean matchesMimeType(Collection<String> contentTypes) {
if (contentTypes == null || contentTypes.size() == 0) {
return false;
}
/**
* Returns whether the content mime types matches the configures mime types.
*
* @param contentTypes the content types
* @return true if any specified content type matches the request content types
*/
private boolean matchesMimeType(Collection<String> contentTypes) {
if (contentTypes == null || contentTypes.size() == 0) {
return false;
}
if (getProperties().getMimeTypes() == null || getProperties().getMimeTypes().length == 0) {
// no specific mime types has been set - matching everything
return true;
}
if (getProperties().getMimeTypes() == null || getProperties().getMimeTypes().length == 0) {
// no specific mime types has been set - matching everything
return true;
}
for (String mimeType : getProperties().getMimeTypes()) {
if (contentTypes.contains(mimeType)) {
return true;
}
}
for (String mimeType : getProperties().getMimeTypes()) {
if (contentTypes.contains(mimeType)) {
return true;
}
}
return false;
}
return false;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2017 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.
@@ -23,33 +23,33 @@ package org.springframework.cloud.netflix.feign.encoding;
*/
public interface HttpEncoding {
/**
* The HTTP Content-Length header.
*/
String CONTENT_LENGTH = "Content-Length";
/**
* The HTTP Content-Length header.
*/
String CONTENT_LENGTH = "Content-Length";
/**
* The HTTP Content-Type header.
*/
String CONTENT_TYPE = "Content-Type";
/**
* The HTTP Content-Type header.
*/
String CONTENT_TYPE = "Content-Type";
/**
* The HTTP Accept-Encoding header.
*/
String ACCEPT_ENCODING_HEADER = "Accept-Encoding";
/**
* The HTTP Accept-Encoding header.
*/
String ACCEPT_ENCODING_HEADER = "Accept-Encoding";
/**
* The HTTP Content-Encoding header.
*/
String CONTENT_ENCODING_HEADER = "Content-Encoding";
/**
* The HTTP Content-Encoding header.
*/
String CONTENT_ENCODING_HEADER = "Content-Encoding";
/**
* The GZIP encoding.
*/
String GZIP_ENCODING = "gzip";
/**
* The GZIP encoding.
*/
String GZIP_ENCODING = "gzip";
/**
* The Deflate encoding.
*/
String DEFLATE_ENCODING = "deflate";
/**
* The Deflate encoding.
*/
String DEFLATE_ENCODING = "deflate";
}
@@ -18,6 +18,10 @@ package org.springframework.cloud.netflix.feign.ribbon;
import java.util.Map;
import org.springframework.cloud.client.loadbalancer.LoadBalancedBackOffPolicyFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryListenerFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicyFactory;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.util.ConcurrentReferenceHashMap;
@@ -30,15 +34,67 @@ import com.netflix.loadbalancer.ILoadBalancer;
*
* @author Spencer Gibb
* @author Dave Syer
* @author Ryan Baxter
* @author Gang Li
*/
public class CachingSpringLoadBalancerFactory {
private final SpringClientFactory factory;
private final LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory;
private final LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory;
private final LoadBalancedRetryListenerFactory loadBalancedRetryListenerFactory;
private boolean enableRetry = false;
private volatile Map<String, FeignLoadBalancer> cache = new ConcurrentReferenceHashMap<>();
public CachingSpringLoadBalancerFactory(SpringClientFactory factory) {
this.factory = factory;
this.loadBalancedRetryPolicyFactory = new RibbonLoadBalancedRetryPolicyFactory(factory);
this.loadBalancedBackOffPolicyFactory = null;
this.loadBalancedRetryListenerFactory = null;
}
@Deprecated
//TODO remove in 2.0.x
public CachingSpringLoadBalancerFactory(SpringClientFactory factory,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
this.factory = factory;
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
this.loadBalancedBackOffPolicyFactory = null;
this.loadBalancedRetryListenerFactory = null;
}
@Deprecated
//TODO remove in 2.0.0x
public CachingSpringLoadBalancerFactory(SpringClientFactory factory,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory, boolean enableRetry) {
this.factory = factory;
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
this.enableRetry = enableRetry;
this.loadBalancedBackOffPolicyFactory = null;
this.loadBalancedRetryListenerFactory = null;
}
@Deprecated
//TODO remove in 2.0.0x
public CachingSpringLoadBalancerFactory(SpringClientFactory factory,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory,
LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory) {
this.factory = factory;
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
this.loadBalancedBackOffPolicyFactory = loadBalancedBackOffPolicyFactory;
this.loadBalancedRetryListenerFactory = null;
this.enableRetry = true;
}
public CachingSpringLoadBalancerFactory(SpringClientFactory factory, LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory,
LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory,
LoadBalancedRetryListenerFactory loadBalancedRetryListenerFactory) {
this.factory = factory;
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
this.loadBalancedBackOffPolicyFactory = loadBalancedBackOffPolicyFactory;
this.loadBalancedRetryListenerFactory = loadBalancedRetryListenerFactory;
this.enableRetry = true;
}
public FeignLoadBalancer create(String clientName) {
@@ -48,9 +104,10 @@ public class CachingSpringLoadBalancerFactory {
IClientConfig config = this.factory.getClientConfig(clientName);
ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
ServerIntrospector serverIntrospector = this.factory.getInstance(clientName, ServerIntrospector.class);
FeignLoadBalancer client = new FeignLoadBalancer(lb, config, serverIntrospector);
FeignLoadBalancer client = enableRetry ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
loadBalancedRetryPolicyFactory, loadBalancedBackOffPolicyFactory, loadBalancedRetryListenerFactory) : new FeignLoadBalancer(lb, config, serverIntrospector);
this.cache.put(clientName, client);
return client;
}
}
}
@@ -0,0 +1,38 @@
/*
* Copyright 2013-2017 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 feign.Client;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Spencer Gibb
*/
@Configuration
class DefaultFeignLoadBalancedConfiguration {
@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
return new LoadBalancerFeignClient(new Client.Default(null, null),
cachingFactory, clientFactory);
}
}
@@ -16,15 +16,22 @@
package org.springframework.cloud.netflix.feign.ribbon;
import feign.Client;
import feign.Request;
import feign.Response;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
import com.netflix.client.AbstractLoadBalancerAwareClient;
import com.netflix.client.ClientException;
import com.netflix.client.ClientRequest;
@@ -36,22 +43,18 @@ import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import feign.Client;
import feign.Request;
import feign.RequestTemplate;
import feign.Response;
import feign.Util;
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttpsIfNeeded;
public class FeignLoadBalancer extends
AbstractLoadBalancerAwareClient<FeignLoadBalancer.RibbonRequest, FeignLoadBalancer.RibbonResponse> {
private final int connectTimeout;
private final int readTimeout;
private final IClientConfig clientConfig;
private final ServerIntrospector serverIntrospector;
protected int connectTimeout;
protected int readTimeout;
protected IClientConfig clientConfig;
protected ServerIntrospector serverIntrospector;
public FeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig,
ServerIntrospector serverIntrospector) {
ServerIntrospector serverIntrospector) {
super(lb, clientConfig);
this.setRetryHandler(RetryHandler.DEFAULT);
this.clientConfig = clientConfig;
@@ -98,13 +101,8 @@ public class FeignLoadBalancer extends
@Override
public URI reconstructURIWithServer(Server server, URI original) {
String scheme = original.getScheme();
if (!"https".equals(scheme) && (this.serverIntrospector.isSecure(server)
|| this.clientConfig.get(CommonClientConfigKey.IsSecure, false))) {
original = UriComponentsBuilder.fromUri(original).scheme("https").build()
.toUri();
}
return super.reconstructURIWithServer(server, original);
URI uri = updateToHttpsIfNeeded(original, this.clientConfig, this.serverIntrospector, server);
return super.reconstructURIWithServer(server, uri);
}
static class RibbonRequest extends ClientRequest implements Cloneable {
@@ -121,11 +119,7 @@ public class FeignLoadBalancer extends
private Request toRequest(Request request) {
Map<String, Collection<String>> headers = new LinkedHashMap<>(
request.headers());
// Apache client barfs if you set the content length
headers.remove(Util.CONTENT_LENGTH);
return new RequestTemplate().method(request.method())
.append(getUri().toASCIIString())
.body(request.body(), request.charset()).headers(headers).request();
return Request.create(request.method(),getUri().toASCIIString(),headers,request.body(),request.charset());
}
Request toRequest() {
@@ -136,6 +130,34 @@ public class FeignLoadBalancer extends
return this.client;
}
HttpRequest toHttpRequest() {
return new HttpRequest() {
@Override
public HttpMethod getMethod() {
return HttpMethod.resolve(RibbonRequest.this.toRequest().method());
}
@Override
public URI getURI() {
return RibbonRequest.this.getUri();
}
@Override
public HttpHeaders getHeaders() {
Map<String, List<String>> headers = new HashMap<String, List<String>>();
Map<String, Collection<String>> feignHeaders = RibbonRequest.this.toRequest().headers();
for(String key : feignHeaders.keySet()) {
headers.put(key, new ArrayList<String>(feignHeaders.get(key)));
}
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(headers);
return httpHeaders;
}
};
}
@Override
public Object clone() {
return new RibbonRequest(this.client, this.request, getUri());
@@ -0,0 +1,119 @@
/*
*
* * Copyright 2013-2016 the original author or authors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.cloud.netflix.feign.ribbon;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.InterceptorRetryPolicy;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
import org.springframework.http.HttpRequest;
import org.springframework.retry.RetryContext;
/**
* @author Ryan Baxter
*/
public class FeignRetryPolicy extends InterceptorRetryPolicy {
private HttpRequest request;
private String serviceId;
public FeignRetryPolicy(HttpRequest request, LoadBalancedRetryPolicy policy, ServiceInstanceChooser serviceInstanceChooser, String serviceName) {
super(request, policy, serviceInstanceChooser, serviceName);
this.request = request;
this.serviceId = serviceName;
}
@Override
public boolean canRetry(RetryContext context) {
/*
* In InterceptorRetryPolicy.canRetry we ask the LoadBalancer to choose a server if one is not
* set in the retry context and then return true. RetryTemplat calls the canRetry method of
* the policy even on its first execution. So the fact that we didnt have a service instance set
* in the RetryContext signaled that it was the first execution and we should return true.
*
* In the Feign scenario, Feign as actually already queried the load balancer for a service instance
* and we set that service instance in the context when we call the open method of the policy. So in
* the Feign case we just return true if the retry count is 0 indicating we haven't yet made a failed
* request.
*/
if(context.getRetryCount() == 0) {
return true;
}
return super.canRetry(context);
}
@Override
public RetryContext open(RetryContext parent) {
/*
* With Feign (unlike Ribbon) the request already has the URI for the service instance
* we are going to make the request to, so extract that information and set the service
* instance in the context. In the Ribbon scenario the URI in the request object still has
* the service id so we choose and set the service instance later on.
*/
LoadBalancedRetryContext context = new LoadBalancedRetryContext(parent, this.request);
context.setServiceInstance(new FeignRetryPolicyServiceInstance(serviceId, request));
return context;
}
class FeignRetryPolicyServiceInstance implements ServiceInstance {
private String serviceId;
private HttpRequest request;
private Map<String, String> metadata;
FeignRetryPolicyServiceInstance(String serviceId, HttpRequest request) {
this.serviceId = serviceId;
this.request = request;
this.metadata = new HashMap<String, String>();
}
@Override
public String getServiceId() {
return serviceId;
}
@Override
public String getHost() {
return request.getURI().getHost();
}
@Override
public int getPort() {
return request.getURI().getPort();
}
@Override
public boolean isSecure() {
return "https".equals(request.getURI().getScheme());
}
@Override
public URI getUri() {
return request.getURI();
}
@Override
public Map<String, String> getMetadata() {
return metadata;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,25 +16,26 @@
package org.springframework.cloud.netflix.feign.ribbon;
import org.apache.http.client.HttpClient;
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.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancedBackOffPolicyFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryListenerFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.netflix.feign.FeignAutoConfiguration;
import org.springframework.cloud.netflix.feign.support.FeignHttpClientProperties;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import com.netflix.loadbalancer.ILoadBalancer;
import feign.Client;
import feign.Feign;
import feign.Request;
import feign.httpclient.ApacheHttpClient;
import feign.okhttp.OkHttpClient;
/**
* Autoconfiguration to be activated if Feign is in use and needs to be use Ribbon as a
@@ -45,21 +46,31 @@ import feign.okhttp.OkHttpClient;
@ConditionalOnClass({ ILoadBalancer.class, Feign.class })
@Configuration
@AutoConfigureBefore(FeignAutoConfiguration.class)
@EnableConfigurationProperties({ FeignHttpClientProperties.class })
//Order is important here, last should be the default, first should be optional
// see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
OkHttpFeignLoadBalancedConfiguration.class,
DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {
@Bean
@Primary
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
public CachingSpringLoadBalancerFactory cachingLBClientFactory(
SpringClientFactory factory) {
return new CachingSpringLoadBalancerFactory(factory);
}
@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
return new LoadBalancerFeignClient(new Client.Default(null, null),
cachingFactory, clientFactory);
@Primary
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(
SpringClientFactory factory,
LoadBalancedRetryPolicyFactory retryPolicyFactory,
LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory,
LoadBalancedRetryListenerFactory loadBalancedRetryListenerFactory) {
return new CachingSpringLoadBalancerFactory(factory, retryPolicyFactory, loadBalancedBackOffPolicyFactory, loadBalancedRetryListenerFactory);
}
@Bean
@@ -67,50 +78,4 @@ public class FeignRibbonClientAutoConfiguration {
public Request.Options feignRequestOptions() {
return LoadBalancerFeignClient.DEFAULT_OPTIONS;
}
@Configuration
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
protected static class HttpClientConfiguration {
@Autowired(required = false)
private HttpClient httpClient;
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
ApacheHttpClient delegate;
if (this.httpClient != null) {
delegate = new ApacheHttpClient(this.httpClient);
}
else {
delegate = new ApacheHttpClient();
}
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
}
@Configuration
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnProperty(value = "feign.okhttp.enabled", matchIfMissing = true)
protected static class OkHttpConfiguration {
@Autowired(required = false)
private com.squareup.okhttp.OkHttpClient okHttpClient;
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
OkHttpClient delegate;
if (this.okHttpClient != null) {
delegate = new OkHttpClient(this.okHttpClient);
}
else {
delegate = new OkHttpClient();
}
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
}
}
}
@@ -0,0 +1,113 @@
/*
* Copyright 2013-2017 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 feign.Client;
import feign.httpclient.ApacheHttpClient;
import java.util.Timer;
import java.util.TimerTask;
import javax.annotation.PreDestroy;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.netflix.feign.support.FeignHttpClientProperties;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Spencer Gibb
*/
@Configuration
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
class HttpClientFeignLoadBalancedConfiguration {
@Configuration
@ConditionalOnMissingBean(CloseableHttpClient.class)
protected static class HttpClientFeignConfiguration {
private final Timer connectionManagerTimer = new Timer(
"FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
private CloseableHttpClient httpClient;
@Autowired(required = false)
private RegistryBuilder registryBuilder;
@Bean
@ConditionalOnMissingBean(HttpClientConnectionManager.class)
public HttpClientConnectionManager connectionManager(
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
FeignHttpClientProperties httpClientProperties) {
final HttpClientConnectionManager connectionManager = connectionManagerFactory
.newConnectionManager(false, httpClientProperties.getMaxConnections(),
httpClientProperties.getMaxConnectionsPerRoute(),
httpClientProperties.getTimeToLive(),
httpClientProperties.getTimeToLiveUnit(), registryBuilder);
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
public void run() {
connectionManager.closeExpiredConnections();
}
}, 30000, httpClientProperties.getConnectionTimerRepeat());
return connectionManager;
}
@Bean
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
HttpClientConnectionManager httpClientConnectionManager,
FeignHttpClientProperties httpClientProperties) {
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setConnectTimeout(httpClientProperties.getConnectionTimeout())
.setRedirectsEnabled(httpClientProperties.isFollowRedirects())
.build();
this.httpClient = httpClientFactory.createBuilder().
setDefaultRequestConfig(defaultRequestConfig).
setConnectionManager(httpClientConnectionManager).build();
return this.httpClient;
}
@PreDestroy
public void destroy() throws Exception {
connectionManagerTimer.cancel();
if(httpClient != null) {
httpClient.close();
}
}
}
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory, HttpClient httpClient) {
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
}
@@ -0,0 +1,88 @@
/*
* Copyright 2013-2017 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 feign.Client;
import feign.okhttp.OkHttpClient;
import okhttp3.ConnectionPool;
import java.util.concurrent.TimeUnit;
import javax.annotation.PreDestroy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
import org.springframework.cloud.netflix.feign.support.FeignHttpClientProperties;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Spencer Gibb
*/
@Configuration
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnProperty(value = "feign.okhttp.enabled")
class OkHttpFeignLoadBalancedConfiguration {
@Configuration
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
protected static class OkHttpFeignConfiguration {
private okhttp3.OkHttpClient okHttpClient;
@Bean
@ConditionalOnMissingBean(ConnectionPool.class)
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
Long timeToLive = httpClientProperties.getTimeToLive();
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
}
@Bean
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
Boolean followRedirects = httpClientProperties.isFollowRedirects();
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
this.okHttpClient = httpClientFactory.createBuilder(false).
connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).
followRedirects(followRedirects).
connectionPool(connectionPool).build();
return this.okHttpClient;
}
@PreDestroy
public void destroy() {
if(okHttpClient != null) {
okHttpClient.dispatcher().executorService().shutdown();
okHttpClient.connectionPool().evictAll();
}
}
}
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) {
OkHttpClient delegate = new OkHttpClient(okHttpClient);
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
}
@@ -0,0 +1,157 @@
/*
*
* * Copyright 2013-2016 the original author or authors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.cloud.netflix.feign.ribbon;
import feign.Request;
import feign.Response;
import java.io.IOException;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancedBackOffPolicyFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryListenerFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.client.loadbalancer.RetryableStatusCodeException;
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;
import org.springframework.retry.backoff.BackOffPolicy;
import org.springframework.retry.backoff.NoBackOffPolicy;
import org.springframework.retry.policy.NeverRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import com.netflix.client.DefaultLoadBalancerRetryHandler;
import com.netflix.client.RequestSpecificRetryHandler;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
/**
* A {@link FeignLoadBalancer} that leverages Spring Retry to retry failed requests.
* @author Ryan Baxter
* @author Gang Li
*/
public class RetryableFeignLoadBalancer extends FeignLoadBalancer implements ServiceInstanceChooser {
private final LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory;
private final LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory;
private final LoadBalancedRetryListenerFactory loadBalancedRetryListenerFactory;
@Deprecated
//TODO remove in 2.0.x
public RetryableFeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig,
ServerIntrospector serverIntrospector, LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
super(lb, clientConfig, serverIntrospector);
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
this.setRetryHandler(new DefaultLoadBalancerRetryHandler(clientConfig));
this.loadBalancedBackOffPolicyFactory = new LoadBalancedBackOffPolicyFactory.NoBackOffPolicyFactory();
this.loadBalancedRetryListenerFactory = new LoadBalancedRetryListenerFactory.DefaultRetryListenerFactory();
}
@Deprecated
//TODO remove in 2.0.x
public RetryableFeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig,
ServerIntrospector serverIntrospector, LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory,
LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory) {
super(lb, clientConfig, serverIntrospector);
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
this.setRetryHandler(new DefaultLoadBalancerRetryHandler(clientConfig));
this.loadBalancedBackOffPolicyFactory = loadBalancedBackOffPolicyFactory == null ?
new LoadBalancedBackOffPolicyFactory.NoBackOffPolicyFactory() : loadBalancedBackOffPolicyFactory;
this.loadBalancedRetryListenerFactory = new LoadBalancedRetryListenerFactory.DefaultRetryListenerFactory();
}
public RetryableFeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig, ServerIntrospector serverIntrospector,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory,
LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory,
LoadBalancedRetryListenerFactory loadBalancedRetryListenerFactory) {
super(lb, clientConfig, serverIntrospector);
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
this.setRetryHandler(new DefaultLoadBalancerRetryHandler(clientConfig));
this.loadBalancedBackOffPolicyFactory = loadBalancedBackOffPolicyFactory == null ?
new LoadBalancedBackOffPolicyFactory.NoBackOffPolicyFactory() : loadBalancedBackOffPolicyFactory;
this.loadBalancedRetryListenerFactory = loadBalancedRetryListenerFactory == null ?
new LoadBalancedRetryListenerFactory.DefaultRetryListenerFactory() : loadBalancedRetryListenerFactory;
}
@Override
public RibbonResponse execute(final RibbonRequest request, IClientConfig configOverride)
throws IOException {
final Request.Options options;
if (configOverride != null) {
options = new Request.Options(
configOverride.get(CommonClientConfigKey.ConnectTimeout,
this.connectTimeout),
(configOverride.get(CommonClientConfigKey.ReadTimeout,
this.readTimeout)));
}
else {
options = new Request.Options(this.connectTimeout, this.readTimeout);
}
final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
RetryTemplate retryTemplate = new RetryTemplate();
BackOffPolicy backOffPolicy = loadBalancedBackOffPolicyFactory.createBackOffPolicy(this.getClientName());
retryTemplate.setBackOffPolicy(backOffPolicy == null ? new NoBackOffPolicy() : backOffPolicy);
RetryListener[] retryListeners = this.loadBalancedRetryListenerFactory.createRetryListeners(this.getClientName());
if (retryListeners != null && retryListeners.length != 0) {
retryTemplate.setListeners(retryListeners);
}
retryTemplate.setRetryPolicy(retryPolicy == null ? new NeverRetryPolicy()
: new FeignRetryPolicy(request.toHttpRequest(), retryPolicy, this, this.getClientName()));
return retryTemplate.execute(new RetryCallback<RibbonResponse, IOException>() {
@Override
public RibbonResponse doWithRetry(RetryContext retryContext) throws IOException {
Request feignRequest = null;
//on retries the policy will choose the server and set it in the context
//extract the server and update the request being made
if(retryContext instanceof LoadBalancedRetryContext) {
ServiceInstance service = ((LoadBalancedRetryContext)retryContext).getServiceInstance();
if(service != null) {
feignRequest = ((RibbonRequest)request.replaceUri(reconstructURIWithServer(new Server(service.getHost(), service.getPort()), request.getUri()))).toRequest();
}
}
if(feignRequest == null) {
feignRequest = request.toRequest();
}
Response response = request.client().execute(feignRequest, options);
if(retryPolicy.retryableStatusCode(response.status())) {
response.close();
throw new RetryableStatusCodeException(RetryableFeignLoadBalancer.this.getClientName(), response.status());
}
return new RibbonResponse(request.getUri(), response);
}
});
}
@Override
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
FeignLoadBalancer.RibbonRequest request, IClientConfig requestConfig) {
return new RequestSpecificRetryHandler(false, false, this.getRetryHandler(), requestConfig);
}
@Override
public ServiceInstance choose(String serviceId) {
return new RibbonLoadBalancerClient.RibbonServer(serviceId,
this.getLoadBalancer().chooseServer(serviceId));
}
}
@@ -0,0 +1,110 @@
/*
*
* * Copyright 2013-2016 the original author or authors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.cloud.netflix.feign.support;
import java.util.concurrent.TimeUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author Ryan Baxter
*/
@ConfigurationProperties(prefix = "feign.httpclient")
public class FeignHttpClientProperties {
public static final boolean DEFAULT_DISABLE_SSL_VALIDATION = false;
public static final int DEFAULT_MAX_CONNECTIONS = 200;
public static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 50;
public static final long DEFAULT_TIME_TO_LIVE = 900L;
public static final TimeUnit DEFAULT_TIME_TO_LIVE_UNIT = TimeUnit.SECONDS;
public static final boolean DEFAULT_FOLLOW_REDIRECTS = true;
public static final int DEFAULT_CONNECTION_TIMEOUT = 2000;
public static final int DEFAULT_CONNECTION_TIMER_REPEAT = 3000;
private boolean disableSslValidation = DEFAULT_DISABLE_SSL_VALIDATION;
private int maxConnections = DEFAULT_MAX_CONNECTIONS;
private int maxConnectionsPerRoute = DEFAULT_MAX_CONNECTIONS_PER_ROUTE;
private long timeToLive = DEFAULT_TIME_TO_LIVE;
private TimeUnit timeToLiveUnit = DEFAULT_TIME_TO_LIVE_UNIT;
private boolean followRedirects = DEFAULT_FOLLOW_REDIRECTS;
private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
private int connectionTimerRepeat = DEFAULT_CONNECTION_TIMER_REPEAT;
public int getConnectionTimerRepeat() {
return connectionTimerRepeat;
}
public void setConnectionTimerRepeat(int connectionTimerRepeat) {
this.connectionTimerRepeat = connectionTimerRepeat;
}
public boolean isDisableSslValidation() {
return disableSslValidation;
}
public void setDisableSslValidation(boolean disableSslValidation) {
this.disableSslValidation = disableSslValidation;
}
public int getMaxConnections() {
return maxConnections;
}
public void setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
}
public int getMaxConnectionsPerRoute() {
return maxConnectionsPerRoute;
}
public void setMaxConnectionsPerRoute(int maxConnectionsPerRoute) {
this.maxConnectionsPerRoute = maxConnectionsPerRoute;
}
public long getTimeToLive() {
return timeToLive;
}
public void setTimeToLive(long timeToLive) {
this.timeToLive = timeToLive;
}
public TimeUnit getTimeToLiveUnit() {
return timeToLiveUnit;
}
public void setTimeToLiveUnit(TimeUnit timeToLiveUnit) {
this.timeToLiveUnit = timeToLiveUnit;
}
public boolean isFollowRedirects() {
return followRedirects;
}
public void setFollowRedirects(boolean followRedirects) {
this.followRedirects = followRedirects;
}
public int getConnectionTimeout() {
return connectionTimeout;
}
public void setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
}
@@ -14,14 +14,12 @@ import org.springframework.util.MultiValueMap;
import feign.FeignException;
import feign.Response;
import feign.codec.Decoder;
import lombok.extern.slf4j.Slf4j;
/**
* 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;
@@ -22,6 +22,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
@@ -47,9 +48,10 @@ public class SpringDecoder implements Decoder {
}
@Override
public Object decode(final Response response, Type type) throws IOException,
FeignException {
if (type instanceof Class || type instanceof ParameterizedType) {
public Object decode(final Response response, Type type)
throws IOException, FeignException {
if (type instanceof Class || type instanceof ParameterizedType
|| type instanceof WildcardType) {
@SuppressWarnings({ "unchecked", "rawtypes" })
HttpMessageConverterExtractor<?> extractor = new HttpMessageConverterExtractor(
type, this.messageConverters.getObject().getConverters());
@@ -23,17 +23,19 @@ import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.Collection;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import feign.RequestTemplate;
import feign.codec.EncodeException;
import feign.codec.Encoder;
import lombok.extern.apachecommons.CommonsLog;
import static org.springframework.cloud.netflix.feign.support.FeignUtils.getHeaders;
import static org.springframework.cloud.netflix.feign.support.FeignUtils.getHttpHeaders;
@@ -41,9 +43,10 @@ import static org.springframework.cloud.netflix.feign.support.FeignUtils.getHttp
/**
* @author Spencer Gibb
*/
@CommonsLog
public class SpringEncoder implements Encoder {
private static final Log log = LogFactory.getLog(SpringEncoder.class);
private ObjectFactory<HttpMessageConverters> messageConverters;
public SpringEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
@@ -95,8 +98,12 @@ public class SpringEncoder implements Encoder {
// with the modified headers
request.headers(getHeaders(outputMessage.getHeaders()));
request.body(outputMessage.getOutputStream().toByteArray(),
Charset.forName("UTF-8")); // TODO: set charset
// do not use charset for binary data
if (messageConverter instanceof ByteArrayHttpMessageConverter) {
request.body(outputMessage.getOutputStream().toByteArray(), null);
} else {
request.body(outputMessage.getOutputStream().toByteArray(), Charset.forName("UTF-8"));
}
return;
}
}
@@ -18,6 +18,7 @@ package org.springframework.cloud.netflix.feign.support;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -56,6 +57,7 @@ import feign.Param;
/**
* @author Spencer Gibb
* @author Abhijit Sarkar
*/
public class SpringMvcContract extends Contract.BaseContract
implements ResourceLoaderAware {
@@ -153,7 +155,8 @@ public class SpringMvcContract extends Contract.BaseContract
@Override
protected void processAnnotationOnMethod(MethodMetadata data,
Annotation methodAnnotation, Method method) {
if (!(methodAnnotation instanceof RequestMapping)) {
if (!RequestMapping.class.isInstance(methodAnnotation) && !methodAnnotation
.annotationType().isAnnotationPresent(RequestMapping.class)) {
return;
}
@@ -233,11 +236,10 @@ public class SpringMvcContract extends Contract.BaseContract
processParameterAnnotation = synthesizeWithMethodParameterNameAsFallbackValue(
parameterAnnotation, method, paramIndex);
isHttpAnnotation |= processor.processArgument(context,
processParameterAnnotation);
processParameterAnnotation, method);
}
}
if (isHttpAnnotation && data.indexToExpander().get(paramIndex) == null
&& !isMultiValued(method.getParameterTypes()[paramIndex])
&& this.conversionService.canConvert(
method.getParameterTypes()[paramIndex], String.class)) {
data.indexToExpander().put(paramIndex, this.expander);
@@ -245,13 +247,6 @@ public class SpringMvcContract extends Contract.BaseContract
return isHttpAnnotation;
}
private boolean isMultiValued(Class<?> type) {
// Feign will deal with each element in a collection individually (with no
// expander as of 8.16.2, but we'd rather have no conversion than convert a
// collection to a String (which ends up being a csv).
return Collection.class.isAssignableFrom(type);
}
private void parseProduces(MethodMetadata md, Method method,
RequestMapping annotation) {
checkAtMostOne(method, annotation.produces(), "produces");
@@ -280,8 +275,10 @@ public class SpringMvcContract extends Contract.BaseContract
if (annotation.headers() != null && annotation.headers().length > 0) {
for (String header : annotation.headers()) {
int index = header.indexOf('=');
md.template().header(resolve(header.substring(0, index)),
if (!header.contains("!=") && index >= 0) {
md.template().header(resolve(header.substring(0, index)),
resolve(header.substring(index + 1).trim()));
}
}
}
}
@@ -313,8 +310,9 @@ public class SpringMvcContract extends Contract.BaseContract
Object defaultValue = AnnotationUtils.getDefaultValue(parameterAnnotation);
if (defaultValue instanceof String
&& defaultValue.equals(annotationAttributes.get(AnnotationUtils.VALUE))) {
Type[] parameterTypes = method.getGenericParameterTypes();
String[] parameterNames = PARAMETER_NAME_DISCOVERER.getParameterNames(method);
if (parameterNames != null && parameterNames.length > parameterIndex) {
if (shouldAddParameterName(parameterIndex, parameterTypes, parameterNames)) {
annotationAttributes.put(AnnotationUtils.VALUE,
parameterNames[parameterIndex]);
}
@@ -323,6 +321,13 @@ public class SpringMvcContract extends Contract.BaseContract
parameterAnnotation.annotationType(), null);
}
private boolean shouldAddParameterName(int parameterIndex, Type[] parameterTypes, String[] parameterNames) {
// has a parameter name
return parameterNames != null && parameterNames.length > parameterIndex
// has a type
&& parameterTypes != null && parameterTypes.length > parameterIndex;
}
private class SimpleAnnotatedParameterContext
implements AnnotatedParameterProcessor.AnnotatedParameterContext {
@@ -82,7 +82,7 @@ public class HystrixCircuitBreakerConfiguration {
}
@Bean
public HasFeatures hystrixFeature() {
public HasFeatures hystrixStreamFeature() {
return HasFeatures.namedFeature("Hystrix Stream Servlet", HystrixStreamEndpoint.class);
}
}
@@ -19,8 +19,12 @@ package org.springframework.cloud.netflix.hystrix;
/**
* @author Spencer Gibb
*/
public interface HystrixConstants {
public class HystrixConstants {
String HYSTRIX_STREAM_DESTINATION = "springCloudHystrixStream";
public static final String HYSTRIX_STREAM_DESTINATION = "springCloudHystrixStream";
private HystrixConstants() {
throw new AssertionError("Must not instantiate constant utility class");
}
}
@@ -18,12 +18,12 @@ package org.springframework.cloud.netflix.hystrix;
import org.springframework.boot.context.properties.ConfigurationProperties;
import lombok.Data;
import java.util.Objects;
/**
* @author Venil Noronha
* @author Gregor Zurowski
*/
@Data
@ConfigurationProperties("hystrix.metrics")
public class HystrixMetricsProperties {
@@ -33,4 +33,41 @@ public class HystrixMetricsProperties {
/** Interval between subsequent polling of metrics. Defaults to 2000 ms. */
private Integer pollingIntervalMs = 2000;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Integer getPollingIntervalMs() {
return pollingIntervalMs;
}
public void setPollingIntervalMs(Integer pollingIntervalMs) {
this.pollingIntervalMs = pollingIntervalMs;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
HystrixMetricsProperties that = (HystrixMetricsProperties) o;
return enabled == that.enabled &&
Objects.equals(pollingIntervalMs, that.pollingIntervalMs);
}
@Override
public int hashCode() {
return Objects.hash(enabled, pollingIntervalMs);
}
@Override
public String toString() {
return new StringBuilder("HystrixMetricsProperties{")
.append("enabled=").append(enabled).append(", ")
.append("pollingIntervalMs=").append(pollingIntervalMs)
.append("}").toString();
}
}
@@ -0,0 +1,82 @@
/*
* Copyright 2013-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.hystrix.security;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.netflix.hystrix.security.HystrixSecurityAutoConfiguration.HystrixSecurityCondition;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.context.SecurityContext;
import com.netflix.hystrix.Hystrix;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
/**
* @author Daniel Lavoie
*/
@Configuration
@Conditional(HystrixSecurityCondition.class)
@ConditionalOnClass({ Hystrix.class, SecurityContext.class })
public class HystrixSecurityAutoConfiguration {
@Autowired(required = false)
private HystrixConcurrencyStrategy existingConcurrencyStrategy;
@PostConstruct
public void init() {
// Keeps references of existing Hystrix plugins.
HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance()
.getEventNotifier();
HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance()
.getMetricsPublisher();
HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance()
.getPropertiesStrategy();
HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance()
.getCommandExecutionHook();
HystrixPlugins.reset();
// Registers existing plugins excepts the Concurrent Strategy plugin.
HystrixPlugins.getInstance().registerConcurrencyStrategy(
new SecurityContextConcurrencyStrategy(existingConcurrencyStrategy));
HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);
}
static class HystrixSecurityCondition extends AllNestedConditions {
public HystrixSecurityCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnProperty(name = "hystrix.shareSecurityContext")
static class ShareSecurityContext {
}
}
}
@@ -0,0 +1,86 @@
/*
* Copyright 2013-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.hystrix.security;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import com.netflix.hystrix.HystrixThreadPoolProperties;
import org.springframework.security.concurrent.DelegatingSecurityContextCallable;
import com.netflix.hystrix.HystrixThreadPoolKey;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariable;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableLifecycle;
import com.netflix.hystrix.strategy.properties.HystrixProperty;
/**
* @author daniellavoie
*/
public class SecurityContextConcurrencyStrategy extends HystrixConcurrencyStrategy {
private HystrixConcurrencyStrategy existingConcurrencyStrategy;
public SecurityContextConcurrencyStrategy(
HystrixConcurrencyStrategy existingConcurrencyStrategy) {
this.existingConcurrencyStrategy = existingConcurrencyStrategy;
}
@Override
public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
return existingConcurrencyStrategy != null
? existingConcurrencyStrategy.getBlockingQueue(maxQueueSize)
: super.getBlockingQueue(maxQueueSize);
}
@Override
public <T> HystrixRequestVariable<T> getRequestVariable(
HystrixRequestVariableLifecycle<T> rv) {
return existingConcurrencyStrategy != null
? existingConcurrencyStrategy.getRequestVariable(rv)
: super.getRequestVariable(rv);
}
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
HystrixProperty<Integer> corePoolSize,
HystrixProperty<Integer> maximumPoolSize,
HystrixProperty<Integer> keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
return existingConcurrencyStrategy != null
? existingConcurrencyStrategy.getThreadPool(threadPoolKey, corePoolSize,
maximumPoolSize, keepAliveTime, unit, workQueue)
: super.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize,
keepAliveTime, unit, workQueue);
}
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties threadPoolProperties) {
return existingConcurrencyStrategy != null
? existingConcurrencyStrategy.getThreadPool(threadPoolKey, threadPoolProperties)
: super.getThreadPool(threadPoolKey, threadPoolProperties);
}
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
return existingConcurrencyStrategy != null
? existingConcurrencyStrategy
.wrapCallable(new DelegatingSecurityContextCallable<T>(callable))
: super.wrapCallable(new DelegatingSecurityContextCallable<T>(callable));
}
}
@@ -1,19 +1,23 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2017 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
* 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
* 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.
* 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.metrics;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -25,8 +29,6 @@ import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerMapping;
import com.google.common.collect.ImmutableMap;
/**
* @author Jon Schneider
*/
@@ -49,14 +51,19 @@ public class DefaultMetricsTagProvider implements MetricsTagProvider {
}
String host = request.getURI().getHost();
if( host == null ) {
host = "none";
}
String strippedUrlTemplate = urlTemplate.replaceAll("^https?://[^/]+/", "");
//@formatter:off
return ImmutableMap.of("method", request.getMethod().name(),
"uri", sanitizeUrlTemplate(strippedUrlTemplate),
"status", status, "clientName",
host != null ? host : "none");
//@formatter:on
Map<String, String> tags = new HashMap<>();
tags.put("method", request.getMethod().name());
tags.put("uri", sanitizeUrlTemplate(strippedUrlTemplate));
tags.put("status", status);
tags.put("clientName", host);
return Collections.unmodifiableMap(tags);
}
@Override
@@ -94,8 +101,11 @@ public class DefaultMetricsTagProvider implements MetricsTagProvider {
* As is, the urlTemplate is not suitable for use with Atlas, as all interactions with
* Atlas take place via query parameters
*/
private String sanitizeUrlTemplate(String urlTemplate) {
String sanitized = urlTemplate.replaceAll("/", "_").replaceAll("[{}]", "-");
protected String sanitizeUrlTemplate(String urlTemplate) {
String sanitized = urlTemplate
.replaceAll("\\{(\\w+):.+}(?=/|$)", "-$1-") // extract path variable names from regex expressions
.replaceAll("/", "_")
.replaceAll("[{}]", "-");
if (!StringUtils.hasText(sanitized)) {
sanitized = "none";
}
@@ -18,15 +18,12 @@ import java.util.Collection;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.netflix.metrics.servo.ServoMonitorCache;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import com.netflix.servo.MonitorRegistry;
import com.netflix.servo.monitor.MonitorConfig;
import com.netflix.servo.tag.SmallTagMap;
import com.netflix.servo.tag.Tags;
@@ -43,17 +40,19 @@ public class MetricsClientHttpRequestInterceptor implements ClientHttpRequestInt
* Boot (Actuator) provides a more general purpose abstraction for dimensional metrics
* systems, this can be moved there and rewritten against that abstraction.
*/
@Autowired
MonitorRegistry registry;
private final ServoMonitorCache servoMonitorCache;
@Autowired
Collection<MetricsTagProvider> tagProviders;
private final Collection<MetricsTagProvider> tagProviders;
@Autowired
ServoMonitorCache servoMonitorCache;
private final String metricName;
@Value("${netflix.metrics.restClient.metricName:restclient}")
String metricName;
public MetricsClientHttpRequestInterceptor(
Collection<MetricsTagProvider> tagProviders,
ServoMonitorCache servoMonitorCache, String metricName) {
this.tagProviders = tagProviders;
this.servoMonitorCache = servoMonitorCache;
this.metricName = metricName;
}
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
@@ -68,8 +67,8 @@ public class MetricsClientHttpRequestInterceptor implements ClientHttpRequestInt
finally {
SmallTagMap.Builder builder = SmallTagMap.builder();
for (MetricsTagProvider tagProvider : tagProviders) {
for (Map.Entry<String, String> tag : tagProvider.clientHttpRequestTags(
request, response).entrySet()) {
for (Map.Entry<String, String> tag : tagProvider
.clientHttpRequestTags(request, response).entrySet()) {
builder.add(Tags.newTag(tag.getKey(), tag.getValue()));
}
}
@@ -78,8 +77,8 @@ public class MetricsClientHttpRequestInterceptor implements ClientHttpRequestInt
.builder(metricName);
monitorConfigBuilder.withTags(builder);
servoMonitorCache.getTimer(monitorConfigBuilder.build()).record(
System.nanoTime() - startTime, TimeUnit.NANOSECONDS);
servoMonitorCache.getTimer(monitorConfigBuilder.build())
.record(System.nanoTime() - startTime, TimeUnit.NANOSECONDS);
}
}
}
@@ -13,22 +13,30 @@
package org.springframework.cloud.netflix.metrics;
import java.util.ArrayList;
import java.util.Collection;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.actuate.metrics.reader.MetricReader;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.cloud.netflix.metrics.servo.ServoMonitorCache;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import com.netflix.servo.MonitorRegistry;
import com.netflix.servo.monitor.Monitors;
/**
@@ -41,6 +49,7 @@ public class MetricsInterceptorConfiguration {
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass(WebMvcConfigurerAdapter.class)
static class MetricsWebResourceConfiguration extends WebMvcConfigurerAdapter {
@Bean
MetricsHandlerInterceptor servoMonitoringWebResourceInterceptor() {
@@ -54,9 +63,8 @@ public class MetricsInterceptorConfiguration {
}
@Configuration
@ConditionalOnClass(JoinPoint.class)
@ConditionalOnClass({ RestTemplate.class, JoinPoint.class })
@ConditionalOnProperty(value = "spring.aop.enabled", havingValue = "true", matchIfMissing = true)
@ConditionalOnBean({ RestTemplate.class })
static class MetricsRestTemplateAspectConfiguration {
@Bean
@@ -67,12 +75,18 @@ public class MetricsInterceptorConfiguration {
}
@Configuration
@ConditionalOnBean({ RestTemplate.class })
@ConditionalOnClass({ RestTemplate.class, HttpServletRequest.class }) // HttpServletRequest implicitly required by MetricsTagProvider
static class MetricsRestTemplateConfiguration {
@Value("${netflix.metrics.restClient.metricName:restclient}")
String metricName;
@Bean
MetricsClientHttpRequestInterceptor spectatorLoggingClientHttpRequestInterceptor() {
return new MetricsClientHttpRequestInterceptor();
MetricsClientHttpRequestInterceptor spectatorLoggingClientHttpRequestInterceptor(
Collection<MetricsTagProvider> tagProviders,
ServoMonitorCache servoMonitorCache) {
return new MetricsClientHttpRequestInterceptor(tagProviders,
servoMonitorCache, this.metricName);
}
@Bean
@@ -80,8 +94,8 @@ public class MetricsInterceptorConfiguration {
return new MetricsInterceptorPostProcessor();
}
private static class MetricsInterceptorPostProcessor implements
BeanPostProcessor, ApplicationContextAware {
private static class MetricsInterceptorPostProcessor
implements BeanPostProcessor, ApplicationContextAware {
private ApplicationContext context;
private MetricsClientHttpRequestInterceptor interceptor;
@@ -97,7 +111,12 @@ public class MetricsInterceptorConfiguration {
this.interceptor = this.context
.getBean(MetricsClientHttpRequestInterceptor.class);
}
((RestTemplate) bean).getInterceptors().add(interceptor);
RestTemplate restTemplate = (RestTemplate) bean;
// create a new list as the old one may be unmodifiable (ie Arrays.asList())
ArrayList<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(interceptor);
interceptors.addAll(restTemplate.getInterceptors());
restTemplate.setInterceptors(interceptors);
}
return bean;
}
@@ -26,7 +26,7 @@ import org.aspectj.lang.annotation.Aspect;
@Aspect
public class RestTemplateUrlTemplateCapturingAspect {
@Around("execution(* org.springframework.web.client.RestOperations+.*(String, ..))")
Object captureUrlTemplate(ProceedingJoinPoint joinPoint) throws Throwable {
public Object captureUrlTemplate(ProceedingJoinPoint joinPoint) throws Throwable {
try {
String urlTemplate = (String) joinPoint.getArgs()[0];
RestTemplateUrlTemplateHolder.setRestTemplateUrlTemplate(urlTemplate);
@@ -19,6 +19,8 @@ package org.springframework.cloud.netflix.metrics;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
@@ -32,11 +34,14 @@ import org.springframework.util.ClassUtils;
*/
public class ServoEnvironmentPostProcessor implements EnvironmentPostProcessor {
private static final Log log = LogFactory.getLog(ServoEnvironmentPostProcessor.class);
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
if (ClassUtils.isPresent("com.netflix.servo.monitor.Monitors", null)) {
// Make spring AOP default to target class so RestTemplates can be customized
log.debug("Setting 'spring.aop.proxyTargetClass=true' to make spring AOP default to target class so RestTemplates can be customized");
addDefaultProperty(environment, "spring.aop.proxyTargetClass", "true");
}
}
@@ -26,7 +26,9 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
@@ -36,6 +38,7 @@ import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.dataformat.smile.SmileFactory;
import com.netflix.servo.Metric;
import com.netflix.servo.annotations.DataSourceType;
import com.netflix.servo.monitor.MonitorConfig;
import com.netflix.servo.publish.MetricObserver;
import com.netflix.servo.tag.BasicTag;
import com.netflix.servo.tag.Tag;
@@ -94,7 +97,7 @@ public class AtlasMetricObserver implements MetricObserver {
return true;
}
protected static String normalizeAtlasUri(String uri) {
static String normalizeAtlasUri(String uri) {
if (uri != null) {
Matcher matcher = Pattern.compile("(.+?)(/api/v1/publish)?/?").matcher(uri);
if (matcher.matches())
@@ -117,7 +120,7 @@ public class AtlasMetricObserver implements MetricObserver {
return;
}
List<Metric> metrics = addTypeTagsAsNecessary(rawMetrics);
List<Metric> metrics = sanitizeTags(addTypeTagsAsNecessary(rawMetrics));
for (int i = 0; i < metrics.size(); i += config.getBatchSize()) {
List<Metric> batch = metrics.subList(i,
@@ -127,7 +130,11 @@ public class AtlasMetricObserver implements MetricObserver {
}
}
private void sendMetricsBatch(List<Metric> metrics) {
enum PublishMetricsBatchStatus {
NothingToDo, Success, PartialSuccess, Failure
}
PublishMetricsBatchStatus sendMetricsBatch(List<Metric> metrics) {
try {
ByteArrayOutputStream output = new ByteArrayOutputStream();
JsonGenerator gen = smileFactory.createGenerator(output, JsonEncoding.UTF8);
@@ -136,7 +143,7 @@ public class AtlasMetricObserver implements MetricObserver {
writeCommonTags(gen);
if (writeMetrics(gen, metrics) == 0)
return; // short circuit this batch if no valid/numeric metrics existed
return PublishMetricsBatchStatus.NothingToDo; // short circuit this batch if no valid/numeric metrics existed
gen.writeEndObject();
gen.flush();
@@ -145,22 +152,32 @@ public class AtlasMetricObserver implements MetricObserver {
headers.setContentType(MediaType.valueOf("application/x-jackson-smile"));
HttpEntity<byte[]> entity = new HttpEntity<>(output.toByteArray(), headers);
try {
restTemplate.exchange(uri, HttpMethod.POST, entity, Map.class);
ResponseEntity<Map> response = restTemplate.exchange(uri, HttpMethod.POST, entity, Map.class);
if(response.getStatusCode() == HttpStatus.ACCEPTED) {
// partial success processing the metrics batch
List<String> messages = (List<String>) response.getBody().get("message");
if(messages != null) {
for (String message : messages) {
logger.error("Failed to write metric to atlas: " + message);
}
}
return PublishMetricsBatchStatus.PartialSuccess;
}
}
catch (HttpClientErrorException e) {
logger.error(
"Failed to write metrics to atlas: "
+ e.getResponseBodyAsString(), e);
logger.error("Failed to write metrics to atlas: " + e.getResponseBodyAsString());
return PublishMetricsBatchStatus.Failure;
}
catch (RestClientException e) {
logger.error("Failed to write metrics to atlas", e);
return PublishMetricsBatchStatus.Failure;
}
}
catch (IOException e) {
// an IOException stemming from the generator writing to a
// ByteArrayOutputStream is impossible
throw new RuntimeException(e);
return PublishMetricsBatchStatus.Failure;
}
return PublishMetricsBatchStatus.Success;
}
private void writeCommonTags(JsonGenerator gen) throws IOException {
@@ -201,15 +218,32 @@ public class AtlasMetricObserver implements MetricObserver {
return totalMetricsInBatch;
}
protected static List<Metric> addTypeTagsAsNecessary(List<Metric> metrics) {
List<Metric> typedMetrics = new ArrayList<>();
static List<Metric> sanitizeTags(List<Metric> metrics) {
List<Metric> sanitized = new ArrayList<>(metrics.size());
for (Metric m : metrics) {
MonitorConfig.Builder config = MonitorConfig.builder(toValidCharset(m.getConfig().getName()));
for (Tag tag : m.getConfig().getTags()) {
config.withTag(toValidCharset(tag.getKey()), toValidCharset(tag.getValue()));
}
config.withPublishingPolicy(m.getConfig().getPublishingPolicy());
sanitized.add(new Metric(config.build(), m.getTimestamp(), m.getValue()));
}
return sanitized;
}
private static String toValidCharset(String name) {
return name.replaceAll("[^\\.\\-\\w]", "_");
}
static List<Metric> addTypeTagsAsNecessary(List<Metric> metrics) {
List<Metric> typedMetrics = new ArrayList<>(metrics.size());
for (Metric m : metrics) {
String value = m.getConfig().getTags().getValue(DataSourceType.KEY);
Metric transformed;
// Atlas will not normalize metrics tagged with atlas.dstype=gauge. Since
// these metric types are
// pre-normalized, we do not want Atlas to touch the value
// these metric types are pre-normalized, we do not want Atlas to touch the
// value
if (DataSourceType.GAUGE.name().equals(value)
|| DataSourceType.RATE.name().equals(value)
|| DataSourceType.NORMALIZED.name().equals(value)) {
@@ -218,10 +252,8 @@ public class AtlasMetricObserver implements MetricObserver {
}
// atlas.dstype=counter means you're sending the absolute value of the counter
// (a monotonically
// increasing value), and Atlas will keep the previous value and convert it to
// a rate per second
// when the metric is received
// (a monotonically increasing value), and Atlas will keep the previous value
// and convert it to a rate per second when the metric is received
else if (DataSourceType.COUNTER.name().equals(value)) {
transformed = new Metric(
m.getConfig().withAdditionalTag(atlasCounterTag),
@@ -16,7 +16,7 @@ package org.springframework.cloud.netflix.metrics.servo;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.springframework.util.StringUtils;
import com.netflix.servo.monitor.Monitor;
import com.netflix.servo.monitor.MonitorConfig;
@@ -33,6 +33,6 @@ public class DimensionalServoMetricNaming implements ServoMetricNaming {
for (Tag t : config.getTags()) {
tags.add(t.getKey() + "=" + t.getValue());
}
return config.getName() + "(" + StringUtils.join(tags, ",") + ")";
return config.getName() + "(" + StringUtils.collectionToCommaDelimitedString(tags) + ")";
}
}
@@ -95,8 +95,12 @@ public class ServoMetricsAutoConfiguration {
return new ServoMetricServices(monitorRegistry);
}
@Bean
public MetricsTagProvider defaultMetricsTagProvider() {
return new DefaultMetricsTagProvider();
@Configuration
@ConditionalOnClass(name = "javax.servlet.http.HttpServletRequest")
protected static class MetricsTagConfiguration {
@Bean
public MetricsTagProvider defaultMetricsTagProvider() {
return new DefaultMetricsTagProvider();
}
}
}
@@ -19,16 +19,18 @@ import java.util.Map;
import com.netflix.servo.MonitorRegistry;
import com.netflix.servo.monitor.BasicTimer;
import com.netflix.servo.monitor.MonitorConfig;
import lombok.extern.apachecommons.CommonsLog;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Servo does not provide a mechanism to retrieve an existing monitor by name + tags.
*
* @author Jon Schneider
*/
@CommonsLog
public class ServoMonitorCache {
private static final Log log = LogFactory.getLog(ServoMonitorCache.class);
private final Map<MonitorConfig, BasicTimer> timerCache = new HashMap<>();
private final MonitorRegistry monitorRegistry;
private final ServoMetricsConfigBean config;
@@ -16,19 +16,30 @@
package org.springframework.cloud.netflix.ribbon;
import java.util.Collections;
import java.util.Map;
import com.netflix.loadbalancer.Server;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* @author Spencer Gibb
*/
public class DefaultServerIntrospector implements ServerIntrospector {
private ServerIntrospectorProperties serverIntrospectorProperties = new ServerIntrospectorProperties();
@Autowired(required = false)
public void setServerIntrospectorProperties(ServerIntrospectorProperties serverIntrospectorProperties){
this.serverIntrospectorProperties = serverIntrospectorProperties;
}
@Override
public boolean isSecure(Server server) {
// Can we do better?
return (""+server.getPort()).endsWith("443");
return serverIntrospectorProperties.getSecurePorts().contains(server.getPort());
}
@Override
@@ -0,0 +1,63 @@
package org.springframework.cloud.netflix.ribbon;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.ServerList;
import com.netflix.loadbalancer.ServerListFilter;
import static org.springframework.cloud.netflix.ribbon.SpringClientFactory.NAMESPACE;
import static org.springframework.cloud.netflix.ribbon.SpringClientFactory.instantiateWithConfig;
/**
* @author Spencer Gibb
*/
public class PropertiesFactory {
@Autowired
private Environment environment;
private Map<Class, String> classToProperty = new HashMap<>();
public PropertiesFactory() {
classToProperty.put(ILoadBalancer.class, "NFLoadBalancerClassName");
classToProperty.put(IPing.class, "NFLoadBalancerPingClassName");
classToProperty.put(IRule.class, "NFLoadBalancerRuleClassName");
classToProperty.put(ServerList.class, "NIWSServerListClassName");
classToProperty.put(ServerListFilter.class, "NIWSServerListFilterClassName");
}
public boolean isSet(Class clazz, String name) {
return StringUtils.hasText(getClassName(clazz, name));
}
public String getClassName(Class clazz, String name) {
if (this.classToProperty.containsKey(clazz)) {
String classNameProperty = this.classToProperty.get(clazz);
String className = environment.getProperty(name + "." + NAMESPACE + "." + classNameProperty);
return className;
}
return null;
}
@SuppressWarnings("unchecked")
public <C> C get(Class<C> clazz, IClientConfig config, String name) {
String className = getClassName(clazz, name);
if (StringUtils.hasText(className)) {
try {
Class<?> toInstantiate = Class.forName(className);
return (C) instantiateWithConfig(toInstantiate, config);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Unknown class to load "+className+" for class " + clazz + " named " + name);
}
}
return null;
}
}
@@ -0,0 +1,64 @@
/*
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.netflix.ribbon;
import com.netflix.client.AbstractLoadBalancerAwareClient;
import com.netflix.client.RetryHandler;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.niws.client.http.RestClient;
import com.netflix.servo.monitor.Monitors;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
/**
* @author Spencer Gibb
*/
@SuppressWarnings("deprecation")
@Configuration
@RibbonAutoConfiguration.ConditionalOnRibbonRestClient
class RestClientRibbonConfiguration {
@Value("${ribbon.client.name}")
private String name = "client";
/**
* 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
* @param serverIntrospector server introspector to use by the underlying Ribbon instance
* @param retryHandler retry handler to use by the underlying Ribbon instance
* @return a {@link RestClient} instances backed by Ribbon
*/
@Bean
@Lazy
@ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
public RestClient ribbonRestClient(IClientConfig config, ILoadBalancer loadBalancer,
ServerIntrospector serverIntrospector, RetryHandler retryHandler) {
RestClient client = new RibbonClientConfiguration.OverrideRestClient(config, serverIntrospector);
client.setLoadBalancer(loadBalancer);
client.setRetryHandler(retryHandler);
Monitors.registerObject("Client_" + this.name, client);
return client;
}
}
@@ -0,0 +1,56 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.ribbon;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import java.util.List;
/**
* Responsible for eagerly creating the child application context holding the Ribbon
* related configuration
*
* @author Biju Kunjummen
*/
public class RibbonApplicationContextInitializer
implements ApplicationListener<ApplicationReadyEvent> {
private final SpringClientFactory springClientFactory;
//List of Ribbon client names
private final List<String> clientNames;
public RibbonApplicationContextInitializer(SpringClientFactory springClientFactory,
List<String> clientNames) {
this.springClientFactory = springClientFactory;
this.clientNames = clientNames;
}
private void initialize() {
if (clientNames != null) {
for (String clientName : clientNames) {
this.springClientFactory.getContext(clientName);
}
}
}
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
initialize();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,21 +16,35 @@
package org.springframework.cloud.netflix.ribbon;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration;
import org.springframework.cloud.client.loadbalancer.LoadBalancedBackOffPolicyFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryListenerFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.AsyncRestTemplate;
import org.springframework.web.client.RestTemplate;
import com.netflix.client.IClient;
@@ -42,16 +56,21 @@ import com.netflix.ribbon.Ribbon;
*
* @author Spencer Gibb
* @author Dave Syer
* @author Biju Kunjummen
*/
@Configuration
@ConditionalOnClass({ IClient.class, RestTemplate.class })
@ConditionalOnClass({ IClient.class, RestTemplate.class, AsyncRestTemplate.class, Ribbon.class})
@RibbonClients
@AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore(LoadBalancerAutoConfiguration.class)
@AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
@EnableConfigurationProperties({RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class})
public class RibbonAutoConfiguration {
@Autowired(required = false)
private List<RibbonClientSpecification> configurations = new ArrayList<>();
@Autowired
private RibbonEagerLoadProperties ribbonEagerLoadProperties;
@Bean
public HasFeatures ribbonFeature() {
@@ -71,9 +90,50 @@ public class RibbonAutoConfiguration {
return new RibbonLoadBalancerClient(springClientFactory());
}
@Bean
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
@ConditionalOnMissingBean
public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory(SpringClientFactory clientFactory) {
return new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
}
@Bean
@ConditionalOnMissingClass(value = "org.springframework.retry.support.RetryTemplate")
@ConditionalOnMissingBean
public LoadBalancedRetryPolicyFactory neverRetryPolicyFactory() {
return new LoadBalancedRetryPolicyFactory.NeverRetryFactory();
}
@Bean
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
@ConditionalOnMissingBean
public LoadBalancedBackOffPolicyFactory loadBalancedBackoffPolicyFactory() {
return new LoadBalancedBackOffPolicyFactory.NoBackOffPolicyFactory();
}
@Bean
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
@ConditionalOnMissingBean
public LoadBalancedRetryListenerFactory loadBalancedRetryListenerFactory() {
return new LoadBalancedRetryListenerFactory.DefaultRetryListenerFactory();
}
@Bean
@ConditionalOnMissingBean
public PropertiesFactory propertiesFactory() {
return new PropertiesFactory();
}
@Bean
@ConditionalOnProperty(value = "ribbon.eager-load.enabled", matchIfMissing = false)
public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {
return new RibbonApplicationContextInitializer(springClientFactory(),
ribbonEagerLoadProperties.getClients());
}
@Configuration
@ConditionalOnClass(HttpRequest.class)
@ConditionalOnProperty(value = "ribbon.http.client.enabled", matchIfMissing = false)
@ConditionalOnRibbonRestClient
protected static class RibbonClientConfig {
@Autowired
@@ -96,4 +156,24 @@ public class RibbonAutoConfiguration {
}
}
//TODO: support for autoconfiguring restemplate to use apache http client or okhttp
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnRibbonRestClientCondition.class)
@interface ConditionalOnRibbonRestClient { }
private static class OnRibbonRestClientCondition extends AnyNestedCondition {
public OnRibbonRestClientCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@Deprecated //remove in Edgware"
@ConditionalOnProperty("ribbon.http.client.enabled")
static class ZuulProperty {}
@ConditionalOnProperty("ribbon.restclient.enabled")
static class RibbonProperty {}
}
}
@@ -22,36 +22,41 @@ import javax.annotation.PostConstruct;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.CookiePolicy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.ribbon.apache.HttpClientRibbonConfiguration;
import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpRibbonConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.context.annotation.Import;
import com.netflix.client.DefaultLoadBalancerRetryHandler;
import com.netflix.client.RetryHandler;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ConfigurationBasedServerList;
import com.netflix.loadbalancer.DummyPing;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.LoadBalancerBuilder;
import com.netflix.loadbalancer.NoOpPing;
import com.netflix.loadbalancer.PollingServerListUpdater;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import com.netflix.loadbalancer.ServerListFilter;
import com.netflix.loadbalancer.ServerListUpdater;
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;
import static com.netflix.client.config.CommonClientConfigKey.DeploymentContextBasedVipAddresses;
import static org.springframework.cloud.netflix.ribbon.RibbonProperyUtils.setRibbonProperty;
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.setRibbonProperty;
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttpsIfNeeded;
/**
* @author Dave Syer
@@ -59,25 +64,39 @@ import static org.springframework.cloud.netflix.ribbon.RibbonProperyUtils.setRib
@SuppressWarnings("deprecation")
@Configuration
@EnableConfigurationProperties
//Order is important here, last should be the default, first should be optional
// see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({HttpClientConfiguration.class, OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class RibbonClientConfiguration {
public static final int DEFAULT_CONNECT_TIMEOUT = 1000;
public static final int DEFAULT_READ_TIMEOUT = 1000;
@Value("${ribbon.client.name}")
private String name = "client";
// TODO: maybe re-instate autowired load balancers: identified by name they could be
// associated with ribbon clients
@Autowired
private PropertiesFactory propertiesFactory;
@Bean
@ConditionalOnMissingBean
public IClientConfig ribbonClientConfig() {
DefaultClientConfigImpl config = new DefaultClientConfigImpl();
config.loadProperties(this.name);
config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT);
config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT);
return config;
}
@Bean
@ConditionalOnMissingBean
public IRule ribbonRule(IClientConfig config) {
if (this.propertiesFactory.isSet(IRule.class, name)) {
return this.propertiesFactory.get(IRule.class, config, name);
}
ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
rule.initWithNiwsConfig(config);
return rule;
@@ -86,56 +105,49 @@ public class RibbonClientConfiguration {
@Bean
@ConditionalOnMissingBean
public IPing ribbonPing(IClientConfig config) {
// TODO: use PingUrl
return new NoOpPing();
if (this.propertiesFactory.isSet(IPing.class, name)) {
return this.propertiesFactory.get(IPing.class, config, name);
}
return new DummyPing();
}
@Bean
@ConditionalOnMissingBean
@SuppressWarnings("unchecked")
public ServerList<Server> ribbonServerList(IClientConfig config) {
if (this.propertiesFactory.isSet(ServerList.class, name)) {
return this.propertiesFactory.get(ServerList.class, config, name);
}
ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();
serverList.initWithNiwsConfig(config);
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
* @param serverIntrospector server introspector to use by the underlying Ribbon instance
* @param retryHandler retry handler 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,
ServerIntrospector serverIntrospector, RetryHandler retryHandler) {
RestClient client = new OverrideRestClient(config, serverIntrospector);
client.setLoadBalancer(loadBalancer);
client.setRetryHandler(retryHandler);
Monitors.registerObject("Client_" + this.name, client);
return client;
public ServerListUpdater ribbonServerListUpdater(IClientConfig config) {
return new PollingServerListUpdater(config);
}
@Bean
@ConditionalOnMissingBean
public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
IRule rule, IPing ping) {
ZoneAwareLoadBalancer<Server> balancer = LoadBalancerBuilder.newBuilder()
.withClientConfig(config).withRule(rule).withPing(ping)
.withServerListFilter(serverListFilter).withDynamicServerList(serverList)
.buildDynamicServerListLoadBalancer();
return balancer;
IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
return this.propertiesFactory.get(ILoadBalancer.class, config, name);
}
return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
serverListFilter, serverListUpdater);
}
@Bean
@ConditionalOnMissingBean
@SuppressWarnings("unchecked")
public ServerListFilter<Server> ribbonServerListFilter(IClientConfig config) {
if (this.propertiesFactory.isSet(ServerListFilter.class, name)) {
return this.propertiesFactory.get(ServerListFilter.class, config, name);
}
ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
filter.initWithNiwsConfig(config);
return filter;
@@ -143,8 +155,8 @@ public class RibbonClientConfiguration {
@Bean
@ConditionalOnMissingBean
public RibbonLoadBalancerContext ribbonLoadBalancerContext(
ILoadBalancer loadBalancer, IClientConfig config, RetryHandler retryHandler) {
public RibbonLoadBalancerContext ribbonLoadBalancerContext(ILoadBalancer loadBalancer,
IClientConfig config, RetryHandler retryHandler) {
return new RibbonLoadBalancerContext(loadBalancer, config, retryHandler);
}
@@ -153,7 +165,7 @@ public class RibbonClientConfiguration {
public RetryHandler retryHandler(IClientConfig config) {
return new DefaultLoadBalancerRetryHandler(config);
}
@Bean
@ConditionalOnMissingBean
public ServerIntrospector serverIntrospector() {
@@ -167,33 +179,29 @@ public class RibbonClientConfiguration {
static class OverrideRestClient extends RestClient {
private IClientConfig config;
private ServerIntrospector serverIntrospector;
protected OverrideRestClient(IClientConfig ncc,
protected OverrideRestClient(IClientConfig config,
ServerIntrospector serverIntrospector) {
super();
this.config = config;
this.serverIntrospector = serverIntrospector;
initWithNiwsConfig(ncc);
initWithNiwsConfig(this.config);
}
@Override
public URI reconstructURIWithServer(Server server, URI original) {
String scheme = original.getScheme();
if (!"https".equals(scheme) && this.serverIntrospector.isSecure(server)) {
original = UriComponentsBuilder.fromUri(original).scheme("https").build()
.toUri();
}
return super.reconstructURIWithServer(server, original);
URI uri = updateToHttpsIfNeeded(original, this.config,
this.serverIntrospector, server);
return super.reconstructURIWithServer(server, uri);
}
@Override
protected Client apacheHttpClientSpecificInitialization() {
ApacheHttpClient4 apache = (ApacheHttpClient4) super
.apacheHttpClientSpecificInitialization();
apache.getClientHandler()
.getHttpClient()
.getParams()
.setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.IGNORE_COOKIES);
ApacheHttpClient4 apache = (ApacheHttpClient4) super.apacheHttpClientSpecificInitialization();
apache.getClientHandler().getHttpClient().getParams().setParameter(
ClientPNames.COOKIE_POLICY, CookiePolicy.IGNORE_COOKIES);
return apache;
}
@@ -18,20 +18,62 @@ package org.springframework.cloud.netflix.ribbon;
import org.springframework.cloud.context.named.NamedContextFactory;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Arrays;
import java.util.Objects;
/**
* @author Dave Syer
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RibbonClientSpecification implements NamedContextFactory.Specification {
private String name;
private Class<?>[] configuration;
public RibbonClientSpecification() {
}
public RibbonClientSpecification(String name, Class<?>[] configuration) {
this.name = name;
this.configuration = configuration;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Class<?>[] getConfiguration() {
return configuration;
}
public void setConfiguration(Class<?>[] configuration) {
this.configuration = configuration;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RibbonClientSpecification that = (RibbonClientSpecification) o;
return Arrays.equals(configuration, that.configuration) &&
Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(configuration, name);
}
@Override
public String toString() {
return new StringBuilder("RibbonClientSpecification{")
.append("name='").append(name).append("', ")
.append("configuration=").append(Arrays.toString(configuration))
.append("}").toString();
}
}
@@ -0,0 +1,49 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.ribbon;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
/*
* Configuration Properties to indicate which Ribbon configurations
* should be eagerly loaded up
*
* @author Biju Kunjummen
*/
@ConfigurationProperties(prefix = "ribbon.eager-load")
public class RibbonEagerLoadProperties {
private boolean enabled = false;
private List<String> clients;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public List<String> getClients() {
return clients;
}
public void setClients(List<String> clients) {
this.clients = clients;
}
}
@@ -108,6 +108,6 @@ public class RibbonHttpRequest extends AbstractClientHttpRequest {
}
private boolean isDynamic(String name) {
return name.equalsIgnoreCase("Content-Length") || name.equalsIgnoreCase("Transfer-Encoding");
return "Content-Length".equalsIgnoreCase(name) || "Transfer-Encoding".equalsIgnoreCase(name);
}
}
@@ -0,0 +1,121 @@
/*
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.ribbon;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.IClientConfig;
import com.netflix.client.config.IClientConfigKey;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
import org.springframework.http.HttpMethod;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
/**
* {@link LoadBalancedRetryPolicy} for Ribbon clients.
* @author Ryan Baxter
*/
public class RibbonLoadBalancedRetryPolicy implements LoadBalancedRetryPolicy {
public static final IClientConfigKey<String> RETRYABLE_STATUS_CODES = new CommonClientConfigKey<String>("retryableStatusCodes") {};
private int sameServerCount = 0;
private int nextServerCount = 0;
private String serviceId;
private RibbonLoadBalancerContext lbContext;
private ServiceInstanceChooser loadBalanceChooser;
List<Integer> retryableStatusCodes = new ArrayList<>();
public RibbonLoadBalancedRetryPolicy(String serviceId, RibbonLoadBalancerContext context, ServiceInstanceChooser loadBalanceChooser) {
this.serviceId = serviceId;
this.lbContext = context;
this.loadBalanceChooser = loadBalanceChooser;
}
public RibbonLoadBalancedRetryPolicy(String serviceId, RibbonLoadBalancerContext context, ServiceInstanceChooser loadBalanceChooser,
IClientConfig clientConfig) {
this.serviceId = serviceId;
this.lbContext = context;
this.loadBalanceChooser = loadBalanceChooser;
String retryableStatusCodesProp = clientConfig.getPropertyAsString(RETRYABLE_STATUS_CODES, "");
String[] retryableStatusCodesArray = retryableStatusCodesProp.split(",");
for(String code : retryableStatusCodesArray) {
if(!StringUtils.isEmpty(code)) {
try {
retryableStatusCodes.add(Integer.valueOf(code.trim()));
} catch (NumberFormatException e) {
//TODO log
}
}
}
}
public boolean canRetry(LoadBalancedRetryContext context) {
HttpMethod method = context.getRequest().getMethod();
return HttpMethod.GET == method || lbContext.isOkToRetryOnAllOperations();
}
@Override
public boolean canRetrySameServer(LoadBalancedRetryContext context) {
return sameServerCount < lbContext.getRetryHandler().getMaxRetriesOnSameServer() && canRetry(context);
}
@Override
public boolean canRetryNextServer(LoadBalancedRetryContext context) {
//this will be called after a failure occurs and we increment the counter
//so we check that the count is less than or equals to too make sure
//we try the next server the right number of times
return nextServerCount <= lbContext.getRetryHandler().getMaxRetriesOnNextServer() && canRetry(context);
}
@Override
public void close(LoadBalancedRetryContext context) {
}
@Override
public void registerThrowable(LoadBalancedRetryContext context, Throwable throwable) {
//Check if we need to ask the load balancer for a new server.
//Do this before we increment the counters because the first call to this method
//is not a retry it is just an initial failure.
if(!canRetrySameServer(context) && canRetryNextServer(context)) {
context.setServiceInstance(loadBalanceChooser.choose(serviceId));
}
//This method is called regardless of whether we are retrying or making the first request.
//Since we do not count the initial request in the retry count we don't reset the counter
//until we actually equal the same server count limit. This will allow us to make the initial
//request plus the right number of retries.
if(sameServerCount >= lbContext.getRetryHandler().getMaxRetriesOnSameServer() && canRetry(context)) {
//reset same server since we are moving to a new server
sameServerCount = 0;
nextServerCount++;
if(!canRetryNextServer(context)) {
context.setExhaustedOnly();
}
} else {
sameServerCount++;
}
}
@Override
public boolean retryableStatusCode(int statusCode) {
return retryableStatusCodes.contains(statusCode);
}
}
@@ -0,0 +1,40 @@
/*
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.ribbon;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
/**
* @author Ryan Baxter
*/
public class RibbonLoadBalancedRetryPolicyFactory implements LoadBalancedRetryPolicyFactory {
private SpringClientFactory clientFactory;
public RibbonLoadBalancedRetryPolicyFactory(SpringClientFactory clientFactory) {
this.clientFactory = clientFactory;
}
@Override
public LoadBalancedRetryPolicy create(String serviceId, ServiceInstanceChooser loadBalanceChooser) {
RibbonLoadBalancerContext lbContext = this.clientFactory
.getLoadBalancerContext(serviceId);
return new RibbonLoadBalancedRetryPolicy(serviceId, lbContext, loadBalanceChooser, clientFactory.getClientConfig(serviceId));
}
}
@@ -20,16 +20,12 @@ import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.Map;
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 org.springframework.web.util.UriComponentsBuilder;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
@@ -37,6 +33,7 @@ import com.netflix.loadbalancer.Server;
/**
* @author Spencer Gibb
* @author Dave Syer
* @author Ryan Baxter
*/
public class RibbonLoadBalancerClient implements LoadBalancerClient {
@@ -53,11 +50,10 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
RibbonLoadBalancerContext context = this.clientFactory
.getLoadBalancerContext(serviceId);
Server server = new Server(instance.getHost(), instance.getPort());
boolean secure = isSecure(server, serviceId);
URI uri = original;
if (secure) {
uri = UriComponentsBuilder.fromUri(uri).scheme("https").build().toUri();
}
IClientConfig clientConfig = clientFactory.getClientConfig(serviceId);
ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
URI uri = RibbonUtils.updateToHttpsIfNeeded(original, clientConfig,
serverIntrospector, server);
return context.reconstructURIWithServer(server, uri);
}
@@ -81,12 +77,25 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
serviceId), serverIntrospector(serviceId).getMetadata(server));
return execute(serviceId, ribbonServer, request);
}
@Override
public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
Server server = null;
if(serviceInstance instanceof RibbonServer) {
server = ((RibbonServer)serviceInstance).getServer();
}
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
RibbonLoadBalancerContext context = this.clientFactory
.getLoadBalancerContext(serviceId);
RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);
try {
T returnVal = request.apply(ribbonServer);
T returnVal = request.apply(serviceInstance);
statsRecorder.recordStats(returnVal);
return returnVal;
}
@@ -113,11 +122,8 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
private boolean isSecure(Server server, String serviceId) {
IClientConfig config = this.clientFactory.getClientConfig(serviceId);
if (config != null) {
return config.get(CommonClientConfigKey.IsSecure, false);
}
return serverIntrospector(serviceId).isSecure(server);
ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
return RibbonUtils.isSecure(config, serverIntrospector, server);
}
protected Server getServer(String serviceId) {
@@ -135,17 +141,17 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
return this.clientFactory.getLoadBalancer(serviceId);
}
protected static class RibbonServer implements ServiceInstance {
public static class RibbonServer implements ServiceInstance {
private final String serviceId;
private final Server server;
private final boolean secure;
private Map<String, String> metadata;
protected RibbonServer(String serviceId, Server server) {
public RibbonServer(String serviceId, Server server) {
this(serviceId, server, false, Collections.<String, String> emptyMap());
}
protected RibbonServer(String serviceId, Server server, boolean secure,
public RibbonServer(String serviceId, Server server, boolean secure,
Map<String, String> metadata) {
this.serviceId = serviceId;
this.server = server;
@@ -1,33 +0,0 @@
package org.springframework.cloud.netflix.ribbon;
import com.netflix.config.ConfigurationManager;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.config.DynamicStringProperty;
/**
* @author Spencer Gibb
*/
public class RibbonProperyUtils {
public static final String VALUE_NOT_SET = "__not__set__";
public static final String DEFAULT_NAMESPACE = "ribbon";
public static void setRibbonProperty(String serviceId, String suffix, String value) {
// how to set the namespace properly?
String key = getRibbonKey(serviceId, suffix);
DynamicStringProperty property = getProperty(key);
if (property.get().equals(VALUE_NOT_SET)) {
ConfigurationManager.getConfigInstance().setProperty(key, value);
}
}
public static String getRibbonKey(String serviceId, String suffix) {
return serviceId + "." + DEFAULT_NAMESPACE + "." + suffix;
}
public static DynamicStringProperty getProperty(String key) {
return DynamicPropertyFactory.getInstance().getStringProperty(key, VALUE_NOT_SET);
}
}
@@ -0,0 +1,86 @@
package org.springframework.cloud.netflix.ribbon;
import java.net.URI;
import org.springframework.web.util.UriComponentsBuilder;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.IClientConfig;
import com.netflix.config.ConfigurationManager;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.config.DynamicStringProperty;
import com.netflix.loadbalancer.Server;
/**
* @author Spencer Gibb
* @author Jacques-Etienne Beaudet
*/
public class RibbonUtils {
public static final String VALUE_NOT_SET = "__not__set__";
public static final String DEFAULT_NAMESPACE = "ribbon";
public static void setRibbonProperty(String serviceId, String suffix, String value) {
// how to set the namespace properly?
String key = getRibbonKey(serviceId, suffix);
DynamicStringProperty property = getProperty(key);
if (property.get().equals(VALUE_NOT_SET)) {
ConfigurationManager.getConfigInstance().setProperty(key, value);
}
}
public static String getRibbonKey(String serviceId, String suffix) {
return serviceId + "." + DEFAULT_NAMESPACE + "." + suffix;
}
public static DynamicStringProperty getProperty(String key) {
return DynamicPropertyFactory.getInstance().getStringProperty(key, VALUE_NOT_SET);
}
/**
* Determine if client is secure. If the supplied {@link IClientConfig} has the {@link CommonClientConfigKey#IsSecure}
* set, return that value. Otherwise, query the supplied {@link ServerIntrospector}.
* @param config the supplied client configuration.
* @param serverIntrospector
* @param server
* @return true if the client is secure
*/
public static boolean isSecure(IClientConfig config, ServerIntrospector serverIntrospector, Server server) {
if (config != null) {
Boolean isSecure = config.get(CommonClientConfigKey.IsSecure);
if (isSecure != null) {
return isSecure;
}
}
return serverIntrospector.isSecure(server);
}
/**
* Replace the scheme to https if needed. If the uri doesn't start with https and
* {@link #isSecure(IClientConfig, ServerIntrospector, Server)} is true, update the scheme.
* This assumes the uri is already encoded to avoid double encoding.
*
* @param uri
* @param config
* @param serverIntrospector
* @param server
* @return
*/
public static URI updateToHttpsIfNeeded(URI uri, IClientConfig config, ServerIntrospector serverIntrospector,
Server server) {
String scheme = uri.getScheme();
if (!"".equals(uri.toString()) && !"https".equals(scheme) && isSecure(config, serverIntrospector, server)) {
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUri(uri).scheme("https");
if (uri.getRawQuery() != null) {
// When building the URI, UriComponentsBuilder verify the allowed characters and does not
// support the '+' so we replace it for its equivalent '%20'.
// See issue https://jira.spring.io/browse/SPR-10172
uriComponentsBuilder.replaceQuery(uri.getRawQuery().replace("+", "%20"));
}
return uriComponentsBuilder.build(true).toUri();
}
return uri;
}
}
@@ -0,0 +1,62 @@
/*
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.ribbon;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* @author Rico Pahlisch
* @author Gregor Zurowski
*/
@ConfigurationProperties("ribbon")
public class ServerIntrospectorProperties {
private List<Integer> securePorts = Arrays.asList(443,8443);
public List<Integer> getSecurePorts() {
return securePorts;
}
public void setSecurePorts(List<Integer> securePorts) {
this.securePorts = securePorts;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ServerIntrospectorProperties that = (ServerIntrospectorProperties) o;
return Objects.equals(securePorts, that.securePorts);
}
@Override
public int hashCode() {
return Objects.hash(securePorts);
}
@Override
public String toString() {
return new StringBuilder("ServerIntrospectorProperties{")
.append("securePorts=").append(securePorts)
.append("}").toString();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
package org.springframework.cloud.netflix.ribbon;
import java.lang.reflect.Constructor;
import org.springframework.beans.BeanUtils;
import org.springframework.cloud.context.named.NamedContextFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@@ -35,8 +37,10 @@ import com.netflix.loadbalancer.ILoadBalancer;
*/
public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecification> {
static final String NAMESPACE = "ribbon";
public SpringClientFactory() {
super(RibbonClientConfiguration.class, "ribbon", "ribbon.client.name");
super(RibbonClientConfiguration.class, NAMESPACE, "ribbon.client.name");
}
/**
@@ -71,34 +75,37 @@ public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecifi
return getInstance(serviceId, RibbonLoadBalancerContext.class);
}
private <C> C instantiateWithConfig(AnnotationConfigApplicationContext context,
static <C> C instantiateWithConfig(Class<C> clazz, IClientConfig config) {
return instantiateWithConfig(null, clazz, config);
}
static <C> C instantiateWithConfig(AnnotationConfigApplicationContext context,
Class<C> clazz, IClientConfig config) {
C result = null;
if (IClientConfigAware.class.isAssignableFrom(clazz)) {
IClientConfigAware obj = (IClientConfigAware) BeanUtils.instantiate(clazz);
obj.initWithNiwsConfig(config);
@SuppressWarnings("unchecked")
C value = (C) obj;
result = value;
try {
Constructor<C> constructor = clazz.getConstructor(IClientConfig.class);
result = constructor.newInstance(config);
} catch (Throwable e) {
// Ignored
}
else {
try {
if (clazz.getConstructor(IClientConfig.class) != null) {
result = clazz.getConstructor(IClientConfig.class)
.newInstance(config);
}
else {
result = BeanUtils.instantiate(clazz);
}
if (result == null) {
result = BeanUtils.instantiate(clazz);
if (result instanceof IClientConfigAware) {
((IClientConfigAware) result).initWithNiwsConfig(config);
}
catch (Throwable ex) {
// NOPMD
if (context != null) {
context.getAutowireCapableBeanFactory().autowireBean(result);
}
}
context.getAutowireCapableBeanFactory().autowireBean(result);
return result;
}
@Override
public <C> C getInstance(String name, Class<C> type) {
C instance = super.getInstance(name, type);
if (instance != null) {
@@ -108,5 +115,10 @@ public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecifi
return instantiateWithConfig(getContext(name), type, config);
}
@Override
protected AnnotationConfigApplicationContext getContext(String name) {
return super.getContext(name);
}
}
@@ -18,9 +18,7 @@ package org.springframework.cloud.netflix.ribbon;
import java.util.ArrayList;
import java.util.List;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Objects;
import com.netflix.client.config.IClientConfig;
import com.netflix.config.ConfigurationManager;
@@ -34,8 +32,6 @@ import com.netflix.loadbalancer.ZoneAffinityServerListFilter;
*
* @author Dave Syer
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ZonePreferenceServerListFilter extends ZoneAffinityServerListFilter<Server> {
private String zone;
@@ -66,4 +62,32 @@ public class ZonePreferenceServerListFilter extends ZoneAffinityServerListFilter
return output;
}
public String getZone() {
return zone;
}
public void setZone(String zone) {
this.zone = zone;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ZonePreferenceServerListFilter that = (ZonePreferenceServerListFilter) o;
return Objects.equals(zone, that.zone);
}
@Override
public int hashCode() {
return Objects.hash(zone);
}
@Override
public String toString() {
return new StringBuilder("ZonePreferenceServerListFilter{")
.append("zone='").append(zone).append("'")
.append("}").toString();
}
}
@@ -0,0 +1,166 @@
/*
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.netflix.ribbon.apache;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import javax.annotation.PreDestroy;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.loadbalancer.LoadBalancedBackOffPolicyFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryListenerFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.netflix.client.AbstractLoadBalancerAwareClient;
import com.netflix.client.RetryHandler;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.servo.monitor.Monitors;
/**
* @author Spencer Gibb
*/
@Configuration
@ConditionalOnClass(name = "org.apache.http.client.HttpClient")
@ConditionalOnProperty(name = "ribbon.httpclient.enabled", matchIfMissing = true)
public class HttpClientRibbonConfiguration {
@Value("${ribbon.client.name}")
private String name = "client";
@Configuration
protected static class ApacheHttpClientConfiguration {
private final Timer connectionManagerTimer = new Timer(
"RibbonApacheHttpClientConfiguration.connectionManagerTimer", true);
private CloseableHttpClient httpClient;
@Autowired(required = false)
private RegistryBuilder registryBuilder;
@Bean
@ConditionalOnMissingBean(HttpClientConnectionManager.class)
public HttpClientConnectionManager httpClientConnectionManager(
IClientConfig config,
ApacheHttpClientConnectionManagerFactory connectionManagerFactory) {
Integer maxTotalConnections = config.getPropertyAsInteger(
CommonClientConfigKey.MaxTotalConnections,
DefaultClientConfigImpl.DEFAULT_MAX_TOTAL_CONNECTIONS);
Integer maxConnectionsPerHost = config.getPropertyAsInteger(
CommonClientConfigKey.MaxConnectionsPerHost,
DefaultClientConfigImpl.DEFAULT_MAX_CONNECTIONS_PER_HOST);
Integer timerRepeat = config.getPropertyAsInteger(
CommonClientConfigKey.ConnectionCleanerRepeatInterval,
DefaultClientConfigImpl.DEFAULT_CONNECTION_IDLE_TIMERTASK_REPEAT_IN_MSECS);
Object timeToLiveObj = config
.getProperty(CommonClientConfigKey.PoolKeepAliveTime);
Long timeToLive = DefaultClientConfigImpl.DEFAULT_POOL_KEEP_ALIVE_TIME;
Object ttlUnitObj = config
.getProperty(CommonClientConfigKey.PoolKeepAliveTimeUnits);
TimeUnit ttlUnit = DefaultClientConfigImpl.DEFAULT_POOL_KEEP_ALIVE_TIME_UNITS;
if (timeToLiveObj instanceof Long) {
timeToLive = (Long) timeToLiveObj;
}
if (ttlUnitObj instanceof TimeUnit) {
ttlUnit = (TimeUnit) ttlUnitObj;
}
final HttpClientConnectionManager connectionManager = connectionManagerFactory
.newConnectionManager(false, maxTotalConnections,
maxConnectionsPerHost, timeToLive, ttlUnit, registryBuilder);
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
public void run() {
connectionManager.closeExpiredConnections();
}
}, 30000, timerRepeat);
return connectionManager;
}
@Bean
@ConditionalOnMissingBean(CloseableHttpClient.class)
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
HttpClientConnectionManager connectionManager, IClientConfig config) {
Boolean followRedirects = config.getPropertyAsBoolean(
CommonClientConfigKey.FollowRedirects,
DefaultClientConfigImpl.DEFAULT_FOLLOW_REDIRECTS);
Integer connectTimeout = config.getPropertyAsInteger(
CommonClientConfigKey.ConnectTimeout,
DefaultClientConfigImpl.DEFAULT_CONNECT_TIMEOUT);
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setConnectTimeout(connectTimeout)
.setRedirectsEnabled(followRedirects).build();
this.httpClient = httpClientFactory.createBuilder().
setDefaultRequestConfig(defaultRequestConfig).
setConnectionManager(connectionManager).build();
return httpClient;
}
@PreDestroy
public void destroy() throws Exception {
connectionManagerTimer.cancel();
if(httpClient != null) {
httpClient.close();
}
}
}
@Bean
@ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
@ConditionalOnMissingClass(value = "org.springframework.retry.support.RetryTemplate")
public RibbonLoadBalancingHttpClient ribbonLoadBalancingHttpClient(
IClientConfig config, ServerIntrospector serverIntrospector,
ILoadBalancer loadBalancer, RetryHandler retryHandler, CloseableHttpClient httpClient) {
RibbonLoadBalancingHttpClient client = new RibbonLoadBalancingHttpClient(httpClient, config, serverIntrospector);
client.setLoadBalancer(loadBalancer);
client.setRetryHandler(retryHandler);
Monitors.registerObject("Client_" + this.name, client);
return client;
}
@Bean
@ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
public RetryableRibbonLoadBalancingHttpClient retryableRibbonLoadBalancingHttpClient(
IClientConfig config, ServerIntrospector serverIntrospector,
ILoadBalancer loadBalancer, RetryHandler retryHandler,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory, CloseableHttpClient httpClient,
LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory,
LoadBalancedRetryListenerFactory loadBalancedRetryListenerFactory) {
RetryableRibbonLoadBalancingHttpClient client = new RetryableRibbonLoadBalancingHttpClient(
httpClient, config, serverIntrospector, loadBalancedRetryPolicyFactory,
loadBalancedBackOffPolicyFactory, loadBalancedRetryListenerFactory);
client.setLoadBalancer(loadBalancer);
client.setRetryHandler(retryHandler);
Monitors.registerObject("Client_" + this.name, client);
return client;
}
}
@@ -0,0 +1,180 @@
/*
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.ribbon.apache;
import java.io.IOException;
import java.net.URI;
import org.apache.commons.lang.BooleanUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancedBackOffPolicyFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryListenerFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.client.loadbalancer.RetryableStatusCodeException;
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
import org.springframework.cloud.netflix.feign.ribbon.FeignRetryPolicy;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.http.HttpRequest;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;
import org.springframework.retry.backoff.BackOffPolicy;
import org.springframework.retry.backoff.NoBackOffPolicy;
import org.springframework.retry.policy.NeverRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import com.netflix.client.RequestSpecificRetryHandler;
import com.netflix.client.RetryHandler;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.Server;
/**
* An Apache HTTP client which leverages Spring Retry to retry failed requests.
* @author Ryan Baxter
* @author Gang Li
*/
public class RetryableRibbonLoadBalancingHttpClient extends RibbonLoadBalancingHttpClient
implements ServiceInstanceChooser {
private LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory = new LoadBalancedRetryPolicyFactory.NeverRetryFactory();
private LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory =
new LoadBalancedBackOffPolicyFactory.NoBackOffPolicyFactory();
private LoadBalancedRetryListenerFactory loadBalancedRetryListenerFactory =
new LoadBalancedRetryListenerFactory.DefaultRetryListenerFactory();
@Deprecated
//TODO remove in 2.0.x
public RetryableRibbonLoadBalancingHttpClient(IClientConfig config,
ServerIntrospector serverIntrospector,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
super(config, serverIntrospector);
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
}
@Deprecated
//TODO remove in 2.0.x
public RetryableRibbonLoadBalancingHttpClient(CloseableHttpClient delegate,
IClientConfig config, ServerIntrospector serverIntrospector,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
super(delegate, config, serverIntrospector);
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
}
@Deprecated
//TODO remove in 2.0.x
public RetryableRibbonLoadBalancingHttpClient(CloseableHttpClient delegate,
IClientConfig config, ServerIntrospector serverIntrospector,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory,
LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory) {
super(delegate, config, serverIntrospector);
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
this.loadBalancedBackOffPolicyFactory = loadBalancedBackOffPolicyFactory;
}
public RetryableRibbonLoadBalancingHttpClient(CloseableHttpClient delegate,
IClientConfig config, ServerIntrospector serverIntrospector,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory,
LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory,
LoadBalancedRetryListenerFactory loadBalancedRetryListenerFactory) {
super(delegate, config, serverIntrospector);
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
this.loadBalancedBackOffPolicyFactory = loadBalancedBackOffPolicyFactory;
this.loadBalancedRetryListenerFactory = loadBalancedRetryListenerFactory;
}
@Override
public RibbonApacheHttpResponse execute(final RibbonApacheHttpRequest request, final IClientConfig configOverride) throws Exception {
final RequestConfig.Builder builder = RequestConfig.custom();
IClientConfig config = configOverride != null ? configOverride : this.config;
builder.setConnectTimeout(config.get(
CommonClientConfigKey.ConnectTimeout, this.connectTimeout));
builder.setSocketTimeout(config.get(
CommonClientConfigKey.ReadTimeout, this.readTimeout));
builder.setRedirectsEnabled(config.get(
CommonClientConfigKey.FollowRedirects, this.followRedirects));
final RequestConfig requestConfig = builder.build();
final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
RetryCallback retryCallback = new RetryCallback() {
@Override
public RibbonApacheHttpResponse doWithRetry(RetryContext context) throws Exception {
//on retries the policy will choose the server and set it in the context
//extract the server and update the request being made
RibbonApacheHttpRequest newRequest = request;
if(context instanceof LoadBalancedRetryContext) {
ServiceInstance service = ((LoadBalancedRetryContext)context).getServiceInstance();
if(service != null) {
//Reconstruct the request URI using the host and port set in the retry context
newRequest = newRequest.withNewUri(new URI(service.getUri().getScheme(),
newRequest.getURI().getUserInfo(), service.getHost(), service.getPort(),
newRequest.getURI().getPath(), newRequest.getURI().getQuery(),
newRequest.getURI().getFragment()));
}
}
newRequest = getSecureRequest(request, configOverride);
HttpUriRequest httpUriRequest = newRequest.toRequest(requestConfig);
final HttpResponse httpResponse = RetryableRibbonLoadBalancingHttpClient.this.delegate.execute(httpUriRequest);
if(retryPolicy.retryableStatusCode(httpResponse.getStatusLine().getStatusCode())) {
if(CloseableHttpResponse.class.isInstance(httpResponse)) {
((CloseableHttpResponse)httpResponse).close();
}
throw new RetryableStatusCodeException(RetryableRibbonLoadBalancingHttpClient.this.clientName,
httpResponse.getStatusLine().getStatusCode());
}
return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI());
}
};
return this.executeWithRetry(request, retryPolicy, retryCallback);
}
private RibbonApacheHttpResponse executeWithRetry(RibbonApacheHttpRequest request, LoadBalancedRetryPolicy retryPolicy, RetryCallback<RibbonApacheHttpResponse, IOException> callback) throws Exception {
RetryTemplate retryTemplate = new RetryTemplate();
boolean retryable = request.getContext() == null ? true :
BooleanUtils.toBooleanDefaultIfNull(request.getContext().getRetryable(), true);
retryTemplate.setRetryPolicy(retryPolicy == null || !retryable ? new NeverRetryPolicy()
: new RetryPolicy(request, retryPolicy, this, this.getClientName()));
BackOffPolicy backOffPolicy = loadBalancedBackOffPolicyFactory.createBackOffPolicy(this.getClientName());
retryTemplate.setBackOffPolicy(backOffPolicy == null ? new NoBackOffPolicy() : backOffPolicy);
RetryListener[] retryListeners = this.loadBalancedRetryListenerFactory.createRetryListeners(this.getClientName());
if (retryListeners != null && retryListeners.length != 0) {
retryTemplate.setListeners(retryListeners);
}
return retryTemplate.execute(callback);
}
@Override
public ServiceInstance choose(String serviceId) {
Server server = this.getLoadBalancer().chooseServer(serviceId);
return new RibbonLoadBalancerClient.RibbonServer(serviceId, server);
}
@Override
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(RibbonApacheHttpRequest request, IClientConfig requestConfig) {
return new RequestSpecificRetryHandler(false, false, RetryHandler.DEFAULT, null);
}
static class RetryPolicy extends FeignRetryPolicy {
public RetryPolicy(HttpRequest request, LoadBalancedRetryPolicy policy, ServiceInstanceChooser serviceInstanceChooser, String serviceName) {
super(request, policy, serviceInstanceChooser, serviceName);
}
}
}
@@ -16,7 +16,8 @@
package org.springframework.cloud.netflix.ribbon.apache;
import java.io.InputStream;
import static org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer.Runner.customize;
import java.net.URI;
import java.util.List;
@@ -24,69 +25,58 @@ import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.entity.BasicHttpEntity;
import org.springframework.util.MultiValueMap;
import com.netflix.client.ClientRequest;
import lombok.Getter;
import org.springframework.cloud.netflix.ribbon.support.ContextAwareRequest;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
/**
* @author Christian Lohmann
*/
@Getter
public class RibbonApacheHttpRequest extends ClientRequest implements Cloneable {
public class RibbonApacheHttpRequest extends ContextAwareRequest implements Cloneable {
private final String method;
private final MultiValueMap<String, String> headers;
private final MultiValueMap<String, String> params;
private final InputStream requestEntity;
public RibbonApacheHttpRequest(final String method, final URI uri,
final Boolean retryable, final MultiValueMap<String, String> headers,
final MultiValueMap<String, String> params, final InputStream requestEntity) {
this.method = method;
this.uri = uri;
this.isRetriable = retryable;
this.headers = headers;
this.params = params;
this.requestEntity = requestEntity;
public RibbonApacheHttpRequest(RibbonCommandContext context) {
super(context);
}
public HttpUriRequest toRequest(final RequestConfig requestConfig) {
final RequestBuilder builder = RequestBuilder.create(this.method);
final RequestBuilder builder = RequestBuilder.create(this.context.getMethod());
builder.setUri(this.uri);
for (final String name : this.headers.keySet()) {
final List<String> values = this.headers.get(name);
for (final String name : this.context.getHeaders().keySet()) {
final List<String> values = this.context.getHeaders().get(name);
for (final String value : values) {
builder.addHeader(name, value);
}
}
for (final String name : this.params.keySet()) {
final List<String> values = this.params.get(name);
for (final String name : this.context.getParams().keySet()) {
final List<String> values = this.context.getParams().get(name);
for (final String value : values) {
builder.addParameter(name, value);
}
}
if (this.requestEntity != null) {
if (this.context.getRequestEntity() != null) {
final BasicHttpEntity entity;
entity = new BasicHttpEntity();
entity.setContent(this.requestEntity);
entity.setContent(this.context.getRequestEntity());
// if the entity contentLength isn't set, transfer-encoding will be set
// to chunked in org.apache.http.protocol.RequestContent. See gh-1042
Long contentLength = this.context.getContentLength();
if ("GET".equals(this.context.getMethod()) && (contentLength == null || contentLength < 0)) {
entity.setContentLength(0);
} else if (contentLength != null) {
entity.setContentLength(contentLength);
}
builder.setEntity(entity);
}
customize(this.context.getRequestCustomizers(), builder);
builder.setConfig(requestConfig);
return builder.build();
}
public RibbonApacheHttpRequest withNewUri(final URI uri) {
return new RibbonApacheHttpRequest(this.method, uri, this.isRetriable,
this.headers, this.params, this.requestEntity);
public RibbonApacheHttpRequest withNewUri(URI uri) {
return new RibbonApacheHttpRequest(newContext(uri));
}
}
@@ -16,121 +16,93 @@
package org.springframework.cloud.netflix.ribbon.apache;
import java.net.URI;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.web.util.UriComponentsBuilder;
import com.netflix.client.AbstractLoadBalancerAwareClient;
import com.netflix.client.RequestSpecificRetryHandler;
import com.netflix.client.RetryHandler;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.cloud.netflix.ribbon.support.AbstractLoadBalancingClient;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttpsIfNeeded;
/**
* @author Christian Lohmann
* @author Ryan Baxter
*/
public class RibbonLoadBalancingHttpClient
extends
AbstractLoadBalancerAwareClient<RibbonApacheHttpRequest, RibbonApacheHttpResponse> {
private final HttpClient delegate = HttpClientBuilder.create().build();
// TODO: rename (ie new class that extends this in Dalston) to ApacheHttpLoadBalancingClient
public class RibbonLoadBalancingHttpClient extends
AbstractLoadBalancingClient<RibbonApacheHttpRequest, RibbonApacheHttpResponse, CloseableHttpClient> {
private int connectTimeout;
private int readTimeout;
private boolean secure;
private boolean followRedirects;
private boolean okToRetryOnAllOperations;
public RibbonLoadBalancingHttpClient() {
super(null);
this.setRetryHandler(RetryHandler.DEFAULT);
public RibbonLoadBalancingHttpClient(IClientConfig config,
ServerIntrospector serverIntrospector) {
super(config, serverIntrospector);
}
public RibbonLoadBalancingHttpClient(final ILoadBalancer lb) {
super(lb);
this.setRetryHandler(RetryHandler.DEFAULT);
public RibbonLoadBalancingHttpClient(CloseableHttpClient delegate,
IClientConfig config, ServerIntrospector serverIntrospector) {
super(delegate, config, serverIntrospector);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
super.initWithNiwsConfig(clientConfig);
this.connectTimeout = clientConfig.getPropertyAsInteger(
CommonClientConfigKey.ConnectTimeout,
DefaultClientConfigImpl.DEFAULT_CONNECT_TIMEOUT);
this.readTimeout = clientConfig.getPropertyAsInteger(
CommonClientConfigKey.ReadTimeout,
DefaultClientConfigImpl.DEFAULT_READ_TIMEOUT);
this.secure = clientConfig.getPropertyAsBoolean(CommonClientConfigKey.IsSecure,
false);
this.followRedirects = clientConfig.getPropertyAsBoolean(
CommonClientConfigKey.FollowRedirects,
DefaultClientConfigImpl.DEFAULT_FOLLOW_REDIRECTS);
this.okToRetryOnAllOperations = clientConfig.getPropertyAsBoolean(
CommonClientConfigKey.OkToRetryOnAllOperations,
DefaultClientConfigImpl.DEFAULT_OK_TO_RETRY_ON_ALL_OPERATIONS);
}
@Override
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
final RibbonApacheHttpRequest request, final IClientConfig requestConfig) {
if (this.okToRetryOnAllOperations) {
return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
requestConfig);
}
if (!request.getMethod().equals("GET")) {
return new RequestSpecificRetryHandler(true, false, this.getRetryHandler(),
requestConfig);
}
else {
return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
requestConfig);
}
protected CloseableHttpClient createDelegate(IClientConfig config) {
return HttpClientBuilder.create()
// already defaults to 0 in builder, so resetting to 0 won't hurt
.setMaxConnTotal(config.getPropertyAsInteger(
CommonClientConfigKey.MaxTotalConnections, 0))
// already defaults to 0 in builder, so resetting to 0 won't hurt
.setMaxConnPerRoute(config.getPropertyAsInteger(
CommonClientConfigKey.MaxConnectionsPerHost, 0))
.disableCookieManagement().useSystemProperties() // for proxy
.build();
}
@Override
public RibbonApacheHttpResponse execute(RibbonApacheHttpRequest request,
final IClientConfig configOverride) throws Exception {
final RequestConfig.Builder builder = RequestConfig.custom();
if (configOverride != null) {
builder.setConnectTimeout(configOverride.get(
CommonClientConfigKey.ConnectTimeout, this.connectTimeout));
builder.setConnectionRequestTimeout(configOverride.get(
CommonClientConfigKey.ReadTimeout, this.readTimeout));
builder.setRedirectsEnabled(configOverride.get(
CommonClientConfigKey.FollowRedirects, this.followRedirects));
}
else {
builder.setConnectTimeout(this.connectTimeout);
builder.setConnectionRequestTimeout(this.readTimeout);
builder.setRedirectsEnabled(this.followRedirects);
}
IClientConfig config = configOverride != null ? configOverride : this.config;
builder.setConnectTimeout(
config.get(CommonClientConfigKey.ConnectTimeout, this.connectTimeout));
builder.setSocketTimeout(
config.get(CommonClientConfigKey.ReadTimeout, this.readTimeout));
builder.setRedirectsEnabled(
config.get(CommonClientConfigKey.FollowRedirects, this.followRedirects));
final RequestConfig requestConfig = builder.build();
if (isSecure(configOverride)) {
final URI secureUri = UriComponentsBuilder.fromUri(request.getUri())
.scheme("https").build().toUri();
request = request.withNewUri(secureUri);
}
request = getSecureRequest(request, configOverride);
final HttpUriRequest httpUriRequest = request.toRequest(requestConfig);
final HttpResponse httpResponse = this.delegate.execute(httpUriRequest);
return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI());
}
private boolean isSecure(final IClientConfig config) {
return (config != null) ? config.get(CommonClientConfigKey.IsSecure)
: this.secure;
@Override
public URI reconstructURIWithServer(Server server, URI original) {
URI uri = updateToHttpsIfNeeded(original, this.config, this.serverIntrospector,
server);
return super.reconstructURIWithServer(server, uri);
}
@Override
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
RibbonApacheHttpRequest request, IClientConfig requestConfig) {
return new RequestSpecificRetryHandler(false, false, RetryHandler.DEFAULT,
requestConfig);
}
protected RibbonApacheHttpRequest getSecureRequest(RibbonApacheHttpRequest request, IClientConfig configOverride) {
if (isSecure(configOverride)) {
final URI secureUri = UriComponentsBuilder.fromUri(request.getUri())
.scheme("https").build(true).toUri();
return request.withNewUri(secureUri);
}
return request;
}
}
@@ -0,0 +1,98 @@
/*
* 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.ribbon.okhttp;
import java.net.URI;
import java.util.concurrent.TimeUnit;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.cloud.netflix.ribbon.support.AbstractLoadBalancingClient;
import org.springframework.web.util.UriComponentsBuilder;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttpsIfNeeded;
/**
* @author Spencer Gibb
* @author Ryan Baxter
*/
public class OkHttpLoadBalancingClient
extends AbstractLoadBalancingClient<OkHttpRibbonRequest, OkHttpRibbonResponse, OkHttpClient> {
public OkHttpLoadBalancingClient(IClientConfig config,
ServerIntrospector serverIntrospector) {
super(config, serverIntrospector);
}
public OkHttpLoadBalancingClient(OkHttpClient delegate, IClientConfig config,
ServerIntrospector serverIntrospector) {
super(delegate, config, serverIntrospector);
}
@Override
protected OkHttpClient createDelegate(IClientConfig config) {
return new OkHttpClient();
}
@Override
public OkHttpRibbonResponse execute(OkHttpRibbonRequest ribbonRequest,
final IClientConfig configOverride) throws Exception {
boolean secure = isSecure(configOverride);
if (secure) {
final URI secureUri = UriComponentsBuilder.fromUri(ribbonRequest.getUri())
.scheme("https").build().toUri();
ribbonRequest = ribbonRequest.withNewUri(secureUri);
}
OkHttpClient httpClient = getOkHttpClient(configOverride, secure);
final Request request = ribbonRequest.toRequest();
Response response = httpClient.newCall(request).execute();
return new OkHttpRibbonResponse(response, ribbonRequest.getUri());
}
OkHttpClient getOkHttpClient(IClientConfig configOverride, boolean secure) {
OkHttpClient.Builder builder = this.delegate.newBuilder();
IClientConfig config = configOverride != null ? configOverride : this.config;
builder.connectTimeout(config.get(
CommonClientConfigKey.ConnectTimeout, this.connectTimeout), TimeUnit.MILLISECONDS);
builder.readTimeout(config.get(
CommonClientConfigKey.ReadTimeout, this.readTimeout), TimeUnit.MILLISECONDS);
builder.followRedirects(config.get(
CommonClientConfigKey.FollowRedirects, this.followRedirects));
if (secure) {
builder.followSslRedirects(configOverride.get(
CommonClientConfigKey.FollowRedirects, this.followRedirects));
}
return builder.build();
}
@Override
public URI reconstructURIWithServer(Server server, URI original) {
URI uri = updateToHttpsIfNeeded(original, this.config, this.serverIntrospector,
server);
return super.reconstructURIWithServer(server, uri);
}
}
@@ -0,0 +1,143 @@
/*
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.netflix.ribbon.okhttp;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import java.util.concurrent.TimeUnit;
import javax.annotation.PreDestroy;
import com.netflix.client.AbstractLoadBalancerAwareClient;
import com.netflix.client.RetryHandler;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.servo.monitor.Monitors;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.loadbalancer.LoadBalancedBackOffPolicyFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryListenerFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Spencer Gibb
*/
@Configuration
@ConditionalOnProperty("ribbon.okhttp.enabled")
@ConditionalOnClass(name = "okhttp3.OkHttpClient")
public class OkHttpRibbonConfiguration {
@Value("${ribbon.client.name}")
private String name = "client";
@Configuration
protected static class OkHttpClientConfiguration {
private OkHttpClient httpClient;
@Bean
@ConditionalOnMissingBean(ConnectionPool.class)
public ConnectionPool httpClientConnectionPool(IClientConfig config, OkHttpClientConnectionPoolFactory connectionPoolFactory) {
Integer maxTotalConnections = config.getPropertyAsInteger(
CommonClientConfigKey.MaxTotalConnections,
DefaultClientConfigImpl.DEFAULT_MAX_TOTAL_CONNECTIONS);
Object timeToLiveObj = config
.getProperty(CommonClientConfigKey.PoolKeepAliveTime);
Long timeToLive = DefaultClientConfigImpl.DEFAULT_POOL_KEEP_ALIVE_TIME;
Object ttlUnitObj = config
.getProperty(CommonClientConfigKey.PoolKeepAliveTimeUnits);
TimeUnit ttlUnit = DefaultClientConfigImpl.DEFAULT_POOL_KEEP_ALIVE_TIME_UNITS;
if (timeToLiveObj instanceof Long) {
timeToLive = (Long) timeToLiveObj;
}
if (ttlUnitObj instanceof TimeUnit) {
ttlUnit = (TimeUnit) ttlUnitObj;
}
return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
}
@Bean
@ConditionalOnMissingBean(OkHttpClient.class)
public OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool, IClientConfig config) {
Boolean followRedirects = config.getPropertyAsBoolean(
CommonClientConfigKey.FollowRedirects,
DefaultClientConfigImpl.DEFAULT_FOLLOW_REDIRECTS);
Integer connectTimeout = config.getPropertyAsInteger(
CommonClientConfigKey.ConnectTimeout,
DefaultClientConfigImpl.DEFAULT_CONNECT_TIMEOUT);
Integer readTimeout = config.getPropertyAsInteger(CommonClientConfigKey.ReadTimeout,
DefaultClientConfigImpl.DEFAULT_READ_TIMEOUT);
this.httpClient = httpClientFactory.createBuilder(false).
connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).
readTimeout(readTimeout, TimeUnit.MILLISECONDS).
followRedirects(followRedirects).
connectionPool(connectionPool).build();
return this.httpClient;
}
@PreDestroy
public void destroy() {
if(httpClient != null) {
httpClient.dispatcher().executorService().shutdown();
httpClient.connectionPool().evictAll();
}
}
}
@Bean
@ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
public RetryableOkHttpLoadBalancingClient okHttpLoadBalancingClient(
IClientConfig config,
ServerIntrospector serverIntrospector,
ILoadBalancer loadBalancer,
RetryHandler retryHandler,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory,
OkHttpClient delegate,
LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory,
LoadBalancedRetryListenerFactory loadBalancedRetryListenerFactory) {
RetryableOkHttpLoadBalancingClient client = new RetryableOkHttpLoadBalancingClient(delegate, config,
serverIntrospector, loadBalancedRetryPolicyFactory, loadBalancedBackOffPolicyFactory, loadBalancedRetryListenerFactory);
client.setLoadBalancer(loadBalancer);
client.setRetryHandler(retryHandler);
Monitors.registerObject("Client_" + this.name, client);
return client;
}
@Bean
@ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
@ConditionalOnMissingClass(value = "org.springframework.retry.support.RetryTemplate")
public OkHttpLoadBalancingClient retryableOkHttpLoadBalancingClient(
IClientConfig config,
ServerIntrospector serverIntrospector, ILoadBalancer loadBalancer,
RetryHandler retryHandler, OkHttpClient delegate) {
OkHttpLoadBalancingClient client = new OkHttpLoadBalancingClient(delegate, config,
serverIntrospector);
client.setLoadBalancer(loadBalancer);
client.setRetryHandler(retryHandler);
Monitors.registerObject("Client_" + this.name, client);
return client;
}
}
@@ -0,0 +1,131 @@
/*
* 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.ribbon.okhttp;
import static org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer.Runner.customize;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.List;
import org.springframework.cloud.netflix.ribbon.support.ContextAwareRequest;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.internal.http.HttpMethod;
import okio.BufferedSink;
import okio.Okio;
import okio.Source;
/**
* @author Spencer Gibb
*/
public class OkHttpRibbonRequest extends ContextAwareRequest implements Cloneable {
public OkHttpRibbonRequest(RibbonCommandContext context) {
super(context);
}
public Request toRequest() {
Headers.Builder headers = new Headers.Builder();
for (String name : this.context.getHeaders().keySet()) {
List<String> values = this.context.getHeaders().get(name);
for (String value : values) {
headers.add(name, value);
}
}
HttpUrl.Builder url = HttpUrl.get(this.uri).newBuilder();
for (String name : this.context.getParams().keySet()) {
List<String> values = this.context.getParams().get(name);
for (String value : values) {
url.addQueryParameter(name, value);
}
}
RequestBody requestBody = null;
if (this.context.getRequestEntity() != null && HttpMethod.permitsRequestBody(this.context.getMethod())) {
MediaType mediaType = null;
if (headers.get("Content-Type") != null) {
mediaType = MediaType.parse(headers.get("Content-Type"));
}
requestBody = new InputStreamRequestBody(this.context.getRequestEntity(), mediaType, this.context.getContentLength());
}
Request.Builder builder = new Request.Builder()
.url(url.build())
.headers(headers.build())
.method(this.context.getMethod(), requestBody);
customize(this.context.getRequestCustomizers(), builder);
return builder.build();
}
public OkHttpRibbonRequest withNewUri(final URI uri) {
return new OkHttpRibbonRequest(newContext(uri));
}
static class InputStreamRequestBody extends RequestBody {
private InputStream inputStream;
private MediaType mediaType;
private Long contentLength;
InputStreamRequestBody(InputStream inputStream, MediaType mediaType, Long contentLength) {
this.inputStream = inputStream;
this.mediaType = mediaType;
this.contentLength = contentLength;
}
@Override
public MediaType contentType() {
return mediaType;
}
@Override
public long contentLength() {
if (contentLength != null) {
return contentLength;
}
try {
return inputStream.available();
} catch (IOException e) {
return 0;
}
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
Source source = null;
try {
source = Okio.source(inputStream);
sink.writeAll(source);
} finally {
if (source != null) {
source.close();
}
}
}
}
}
@@ -0,0 +1,151 @@
/*
* 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.ribbon.okhttp;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.util.Assert;
import com.google.common.reflect.TypeToken;
import com.netflix.client.ClientException;
import com.netflix.client.http.CaseInsensitiveMultiMap;
import com.netflix.client.http.HttpHeaders;
import okhttp3.Response;
import okhttp3.ResponseBody;
/**
* @author Spencer Gibb
*/
public class OkHttpRibbonResponse implements com.netflix.client.http.HttpResponse {
private final ResponseBody body;
private URI uri;
private Response response;
public OkHttpRibbonResponse(Response response, URI uri) {
Assert.notNull(response, "response can not be null");
this.response = response;
this.body = response.body();
this.uri = uri;
}
@Override
public int getStatus() {
return this.response.code();
}
@Override
public String getStatusLine() {
return this.response.message();
}
@Override
public Object getPayload() throws ClientException {
if (!hasPayload()) {
return null;
}
return this.body.byteStream();
}
@Override
public boolean hasPayload() {
return this.body != null;
}
@Override
public boolean isSuccess() {
return this.response.isSuccessful();
}
@Override
public URI getRequestedURI() {
return this.uri;
}
@Override
public Map<String, Collection<String>> getHeaders() {
final Map<String, Collection<String>> headers = new HashMap<>();
for (Map.Entry<String,List<String>> entry : this.response.headers().toMultimap().entrySet()) {
String name = entry.getKey();
for (String value : entry.getValue()) {
if (headers.containsKey(name)) {
headers.get(name).add(value);
} else {
final List<String> values = new ArrayList<>();
values.add(value);
headers.put(name, values);
}
}
}
return headers;
}
@Override
public HttpHeaders getHttpHeaders() {
final CaseInsensitiveMultiMap headers = new CaseInsensitiveMultiMap();
for (Map.Entry<String,List<String>> entry : this.response.headers().toMultimap().entrySet()) {
for (String value : entry.getValue()) {
headers.addHeader(entry.getKey(), value);
}
}
return headers;
}
@Override
public void close() {
this.response.close();
}
@Override
public InputStream getInputStream() {
if (this.body == null) {
return null;
}
return this.body.byteStream();
}
@Override
public boolean hasEntity() {
return hasPayload();
}
@Override
public <T> T getEntity(Class<T> type) throws Exception {
return null;
}
@Override
public <T> T getEntity(Type type) throws Exception {
return null;
}
@Override
public <T> T getEntity(TypeToken<T> type) throws Exception {
return null;
}
}
@@ -0,0 +1,162 @@
/*
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.ribbon.okhttp;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.net.URI;
import org.apache.commons.lang.BooleanUtils;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancedBackOffPolicyFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryListenerFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.client.loadbalancer.RetryableStatusCodeException;
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
import org.springframework.cloud.netflix.feign.ribbon.FeignRetryPolicy;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.http.HttpRequest;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;
import org.springframework.retry.backoff.BackOffPolicy;
import org.springframework.retry.backoff.NoBackOffPolicy;
import org.springframework.retry.policy.NeverRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import com.netflix.client.RequestSpecificRetryHandler;
import com.netflix.client.RetryHandler;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.Server;
/**
* An OK HTTP client which leverages Spring Retry to retry failed request.
* @author Ryan Baxter
* @author Gang Li
*/
public class RetryableOkHttpLoadBalancingClient extends OkHttpLoadBalancingClient implements ServiceInstanceChooser {
private LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory;
private LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory =
new LoadBalancedBackOffPolicyFactory.NoBackOffPolicyFactory();
private LoadBalancedRetryListenerFactory loadBalancedRetryListenerFactory =
new LoadBalancedRetryListenerFactory.DefaultRetryListenerFactory();
@Deprecated
//TODO remove in 2.0.x
public RetryableOkHttpLoadBalancingClient(OkHttpClient delegate, IClientConfig config, ServerIntrospector serverIntrospector,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
super(delegate, config, serverIntrospector);
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
}
@Deprecated
//TODO remove in 2.0.x
public RetryableOkHttpLoadBalancingClient(OkHttpClient delegate, IClientConfig config, ServerIntrospector serverIntrospector,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory,
LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory) {
super(delegate, config, serverIntrospector);
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
this.loadBalancedBackOffPolicyFactory = loadBalancedBackOffPolicyFactory;
}
public RetryableOkHttpLoadBalancingClient(OkHttpClient delegate, IClientConfig config, ServerIntrospector serverIntrospector,
LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory,
LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory,
LoadBalancedRetryListenerFactory loadBalancedRetryListenerFactory) {
super(delegate, config, serverIntrospector);
this.loadBalancedRetryPolicyFactory = loadBalancedRetryPolicyFactory;
this.loadBalancedBackOffPolicyFactory = loadBalancedBackOffPolicyFactory;
this.loadBalancedRetryListenerFactory = loadBalancedRetryListenerFactory;
}
private OkHttpRibbonResponse executeWithRetry(OkHttpRibbonRequest request, LoadBalancedRetryPolicy retryPolicy,
RetryCallback<OkHttpRibbonResponse, Exception> callback) throws Exception {
RetryTemplate retryTemplate = new RetryTemplate();
BackOffPolicy backOffPolicy = loadBalancedBackOffPolicyFactory.createBackOffPolicy(this.getClientName());
retryTemplate.setBackOffPolicy(backOffPolicy == null ? new NoBackOffPolicy() : backOffPolicy);
RetryListener[] retryListeners = this.loadBalancedRetryListenerFactory.createRetryListeners(this.getClientName());
if (retryListeners != null && retryListeners.length != 0) {
retryTemplate.setListeners(retryListeners);
}
boolean retryable = request.getContext() == null ? true :
BooleanUtils.toBooleanDefaultIfNull(request.getContext().getRetryable(), true);
retryTemplate.setRetryPolicy(retryPolicy == null || !retryable ? new NeverRetryPolicy()
: new RetryPolicy(request, retryPolicy, this, this.getClientName()));
return retryTemplate.execute(callback);
}
@Override
public OkHttpRibbonResponse execute(final OkHttpRibbonRequest ribbonRequest,
final IClientConfig configOverride) throws Exception {
final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
RetryCallback<OkHttpRibbonResponse, Exception> retryCallback = new RetryCallback<OkHttpRibbonResponse, Exception>() {
@Override
public OkHttpRibbonResponse doWithRetry(RetryContext context) throws Exception {
//on retries the policy will choose the server and set it in the context
//extract the server and update the request being made
OkHttpRibbonRequest newRequest = ribbonRequest;
if(context instanceof LoadBalancedRetryContext) {
ServiceInstance service = ((LoadBalancedRetryContext)context).getServiceInstance();
if(service != null) {
//Reconstruct the request URI using the host and port set in the retry context
newRequest = newRequest.withNewUri(new URI(service.getUri().getScheme(),
newRequest.getURI().getUserInfo(), service.getHost(), service.getPort(),
newRequest.getURI().getPath(), newRequest.getURI().getQuery(),
newRequest.getURI().getFragment()));
}
}
if (isSecure(configOverride)) {
final URI secureUri = UriComponentsBuilder.fromUri(newRequest.getUri())
.scheme("https").build().toUri();
newRequest = newRequest.withNewUri(secureUri);
}
OkHttpClient httpClient = getOkHttpClient(configOverride, secure);
final Request request = newRequest.toRequest();
Response response = httpClient.newCall(request).execute();
if(retryPolicy.retryableStatusCode(response.code())) {
response.close();
throw new RetryableStatusCodeException(RetryableOkHttpLoadBalancingClient.this.clientName, response.code());
}
return new OkHttpRibbonResponse(response, newRequest.getUri());
}
};
return this.executeWithRetry(ribbonRequest, retryPolicy, retryCallback);
}
@Override
public ServiceInstance choose(String serviceId) {
Server server = this.getLoadBalancer().chooseServer(serviceId);
return new RibbonLoadBalancerClient.RibbonServer(serviceId,
server);
}
@Override
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(OkHttpRibbonRequest request, IClientConfig requestConfig) {
return new RequestSpecificRetryHandler(false, false, RetryHandler.DEFAULT, null);
}
static class RetryPolicy extends FeignRetryPolicy {
public RetryPolicy(HttpRequest request, LoadBalancedRetryPolicy policy, ServiceInstanceChooser serviceInstanceChooser, String serviceName) {
super(request, policy, serviceInstanceChooser, serviceName);
}
}
}
@@ -0,0 +1,151 @@
/*
* Copyright 2013-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.netflix.ribbon.support;
import com.netflix.loadbalancer.reactive.LoadBalancerCommand;
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import com.netflix.client.AbstractLoadBalancerAwareClient;
import com.netflix.client.IResponse;
import com.netflix.client.RequestSpecificRetryHandler;
import com.netflix.client.RetryHandler;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
/**
* @author Spencer Gibb
*/
public abstract class AbstractLoadBalancingClient<S extends ContextAwareRequest, T extends IResponse, D> extends
AbstractLoadBalancerAwareClient<S, T> {
protected int connectTimeout;
protected int readTimeout;
protected boolean secure;
protected boolean followRedirects;
protected boolean okToRetryOnAllOperations;
protected final D delegate;
protected final IClientConfig config;
protected final ServerIntrospector serverIntrospector;
@Deprecated
public AbstractLoadBalancingClient() {
super(null);
this.config = new DefaultClientConfigImpl();
this.delegate = createDelegate(this.config);
this.serverIntrospector = new DefaultServerIntrospector();
this.setRetryHandler(RetryHandler.DEFAULT);
initWithNiwsConfig(config);
}
@Deprecated
public AbstractLoadBalancingClient(final ILoadBalancer lb) {
super(lb);
this.config = new DefaultClientConfigImpl();
this.delegate = createDelegate(config);
this.serverIntrospector = new DefaultServerIntrospector();
this.setRetryHandler(RetryHandler.DEFAULT);
initWithNiwsConfig(config);
}
protected AbstractLoadBalancingClient(IClientConfig config, ServerIntrospector serverIntrospector) {
super(null);
this.delegate = createDelegate(config);
this.config = config;
this.serverIntrospector = serverIntrospector;
this.setRetryHandler(RetryHandler.DEFAULT);
initWithNiwsConfig(config);
}
protected AbstractLoadBalancingClient(D delegate, IClientConfig config, ServerIntrospector serverIntrospector) {
super(null);
this.delegate = delegate;
this.config = config;
this.serverIntrospector = serverIntrospector;
this.setRetryHandler(RetryHandler.DEFAULT);
initWithNiwsConfig(config);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
super.initWithNiwsConfig(clientConfig);
this.connectTimeout = clientConfig.getPropertyAsInteger(
CommonClientConfigKey.ConnectTimeout,
RibbonClientConfiguration.DEFAULT_CONNECT_TIMEOUT);
this.readTimeout = clientConfig.getPropertyAsInteger(
CommonClientConfigKey.ReadTimeout,
RibbonClientConfiguration.DEFAULT_READ_TIMEOUT);
this.secure = clientConfig.getPropertyAsBoolean(CommonClientConfigKey.IsSecure,
false);
this.followRedirects = clientConfig.getPropertyAsBoolean(
CommonClientConfigKey.FollowRedirects,
DefaultClientConfigImpl.DEFAULT_FOLLOW_REDIRECTS);
this.okToRetryOnAllOperations = clientConfig.getPropertyAsBoolean(
CommonClientConfigKey.OkToRetryOnAllOperations,
DefaultClientConfigImpl.DEFAULT_OK_TO_RETRY_ON_ALL_OPERATIONS);
}
protected abstract D createDelegate(IClientConfig config);
public D getDelegate() {
return this.delegate;
}
@Override
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
final S request, final IClientConfig requestConfig) {
if (this.okToRetryOnAllOperations) {
return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
requestConfig);
}
if (!request.getContext().getMethod().equals("GET")) {
return new RequestSpecificRetryHandler(true, false, this.getRetryHandler(),
requestConfig);
}
else {
return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
requestConfig);
}
}
protected boolean isSecure(final IClientConfig config) {
if(config != null) {
Boolean result = config.get(CommonClientConfigKey.IsSecure);
if(result != null) {
return result;
}
}
return this.secure;
}
@Override
protected void customizeLoadBalancerCommandBuilder(S request, IClientConfig config, LoadBalancerCommand.Builder<T> builder) {
if (request.getLoadBalancerKey() != null) {
builder.withServerLocator(request.getLoadBalancerKey());
}
}
}
@@ -0,0 +1,76 @@
/*
* Copyright 2013-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.netflix.ribbon.support;
import com.netflix.client.ClientRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
import org.springframework.util.MultiValueMap;
import java.net.URI;
/**
* @author Spencer Gibb
* @author Ryan Baxter
*/
public abstract class ContextAwareRequest extends ClientRequest implements HttpRequest {
protected final RibbonCommandContext context;
private HttpHeaders httpHeaders;
public ContextAwareRequest(RibbonCommandContext context) {
this.context = context;
MultiValueMap<String, String> headers = context.getHeaders();
this.httpHeaders = new HttpHeaders();
for(String key : headers.keySet()) {
this.httpHeaders.put(key, headers.get(key));
}
this.uri = context.uri();
this.isRetriable = context.getRetryable();
this.loadBalancerKey = context.getLoadBalancerKey();
}
public RibbonCommandContext getContext() {
return context;
}
@Override
public HttpMethod getMethod() {
return HttpMethod.valueOf(context.getMethod());
}
@Override
public URI getURI() {
return this.getUri();
}
@Override
public HttpHeaders getHeaders() {
return httpHeaders;
}
protected RibbonCommandContext newContext(URI uri) {
RibbonCommandContext commandContext = new RibbonCommandContext(this.context.getServiceId(),
this.context.getMethod(), uri.toString(), this.context.getRetryable(),
this.context.getHeaders(), this.context.getParams(), this.context.getRequestEntity(),
this.context.getRequestCustomizers(), this.context.getContentLength(),
this.context.getLoadBalancerKey());
return commandContext;
}
}
@@ -0,0 +1,40 @@
/*
* Copyright 2013-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.netflix.ribbon.support;
import java.util.List;
/**
* @author Spencer Gibb
*/
public interface RibbonRequestCustomizer<B> {
boolean accepts(Class builderClass);
void customize(B builder);
class Runner {
@SuppressWarnings("unchecked")
public static void customize(List<RibbonRequestCustomizer> customizers, Object builder) {
for (RibbonRequestCustomizer customizer : customizers) {
if (customizer.accepts(builder.getClass())) {
customizer.customize(builder);
}
}
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2016 the original author or authors.
* Copyright 2013-2017 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.
@@ -30,39 +30,39 @@ import rx.Subscription;
*/
class DeferredResultSubscriber<T> extends Subscriber<T> implements Runnable {
private final DeferredResult<T> deferredResult;
private final DeferredResult<T> deferredResult;
private final Subscription subscription;
private final Subscription subscription;
private boolean completed;
private boolean completed;
public DeferredResultSubscriber(Observable<T> observable, DeferredResult<T> deferredResult) {
public DeferredResultSubscriber(Observable<T> observable, DeferredResult<T> deferredResult) {
this.deferredResult = deferredResult;
this.deferredResult.onTimeout(this);
this.deferredResult.onCompletion(this);
this.subscription = observable.subscribe(this);
}
this.deferredResult = deferredResult;
this.deferredResult.onTimeout(this);
this.deferredResult.onCompletion(this);
this.subscription = observable.subscribe(this);
}
@Override
public void onNext(T value) {
if (!completed) {
deferredResult.setResult(value);
}
}
@Override
public void onNext(T value) {
if (!completed) {
deferredResult.setResult(value);
}
}
@Override
public void onError(Throwable e) {
deferredResult.setErrorResult(e);
}
@Override
public void onError(Throwable e) {
deferredResult.setErrorResult(e);
}
@Override
public void onCompleted() {
completed = true;
}
@Override
public void onCompleted() {
completed = true;
}
@Override
public void run() {
this.subscription.unsubscribe();
}
@Override
public void run() {
this.subscription.unsubscribe();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2016 the original author or authors.
* Copyright 2013-2017 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.
@@ -33,58 +33,58 @@ import rx.Subscription;
*/
class ResponseBodyEmitterSubscriber<T> extends Subscriber<T> implements Runnable {
private final MediaType mediaType;
private final MediaType mediaType;
private final Subscription subscription;
private final Subscription subscription;
private final ResponseBodyEmitter responseBodyEmitter;
private final ResponseBodyEmitter responseBodyEmitter;
private boolean completed;
private boolean completed;
/**
* Creates new instance of {@link ResponseBodyEmitterSubscriber} with response media type, observable and response
* emitter.
*
* @param mediaType the marshaled object media type
* @param observable the observable
* @param responseBodyEmitter the response emitter
*/
public ResponseBodyEmitterSubscriber(MediaType mediaType, Observable<T> observable, ResponseBodyEmitter responseBodyEmitter) {
/**
* Creates new instance of {@link ResponseBodyEmitterSubscriber} with response media type, observable and response
* emitter.
*
* @param mediaType the marshaled object media type
* @param observable the observable
* @param responseBodyEmitter the response emitter
*/
public ResponseBodyEmitterSubscriber(MediaType mediaType, Observable<T> observable, ResponseBodyEmitter responseBodyEmitter) {
this.mediaType = mediaType;
this.responseBodyEmitter = responseBodyEmitter;
this.responseBodyEmitter.onTimeout(this);
this.responseBodyEmitter.onCompletion(this);
this.subscription = observable.subscribe(this);
}
this.mediaType = mediaType;
this.responseBodyEmitter = responseBodyEmitter;
this.responseBodyEmitter.onTimeout(this);
this.responseBodyEmitter.onCompletion(this);
this.subscription = observable.subscribe(this);
}
@Override
public void onNext(T value) {
@Override
public void onNext(T value) {
try {
if(!completed) {
responseBodyEmitter.send(value, mediaType);
}
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
try {
if(!completed) {
responseBodyEmitter.send(value, mediaType);
}
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
@Override
public void onError(Throwable e) {
responseBodyEmitter.completeWithError(e);
}
@Override
public void onError(Throwable e) {
responseBodyEmitter.completeWithError(e);
}
@Override
public void onCompleted() {
if(!completed) {
completed = true;
responseBodyEmitter.complete();
}
}
@Override
public void onCompleted() {
if(!completed) {
completed = true;
responseBodyEmitter.complete();
}
}
@Override
public void run() {
subscription.unsubscribe();
}
@Override
public void run() {
subscription.unsubscribe();
}
}

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