mirror of
https://github.com/spring-cloud/spring-cloud-netflix.git
synced 2026-09-17 23:59:04 +00:00
Compare commits
1561
Commits
v1.0.0.RELEASE
...
1.4.x
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbb4a0ff9b | ||
|
|
c04f0ff427 | ||
|
|
5f686dc4eb | ||
|
|
d60ca72de6 | ||
|
|
3fdbf7cac1 | ||
|
|
dc58633628 | ||
|
|
e2d8b0c117 | ||
|
|
58335907f2 | ||
|
|
6ec4dda189 | ||
|
|
2568580731 | ||
|
|
e49e38932f | ||
|
|
71153c0c26 | ||
|
|
aeb3af891a | ||
|
|
c538e32c3e | ||
|
|
8334ff96bc | ||
|
|
4ab9897b3b | ||
|
|
2fffb2606e | ||
|
|
0c3fbc34f6 | ||
|
|
01ac98df8f | ||
|
|
3a62567ec8 | ||
|
|
3f0a43f913 | ||
|
|
edd24bc51b | ||
|
|
482deb0485 | ||
|
|
81579f522f | ||
|
|
46719c0c31 | ||
|
|
d98b3ce3d6 | ||
|
|
f19c7fbbe2 | ||
|
|
fd20f81885 | ||
|
|
cf0d54450a | ||
|
|
82fcb82d92 | ||
|
|
cb0a1d6d83 | ||
|
|
42449ee34e | ||
|
|
965fbf847b | ||
|
|
7630acffc6 | ||
|
|
f8f5a88ec6 | ||
|
|
d84b32fc92 | ||
|
|
6eff7e25a3 | ||
|
|
a57c719649 | ||
|
|
c181163a75 | ||
|
|
8af3ae8af8 | ||
|
|
ab7b0fb3a8 | ||
|
|
7b75fe934e | ||
|
|
8c7116b2d8 | ||
|
|
2df81038f9 | ||
|
|
381a097045 | ||
|
|
0970a3ef62 | ||
|
|
84084fc36a | ||
|
|
d95a01b9b5 | ||
|
|
b9bfd4fbb2 | ||
|
|
f14d94fe3e | ||
|
|
61a920e0a5 | ||
|
|
0c4a46bea3 | ||
|
|
038106de17 | ||
|
|
82b51b7a36 | ||
|
|
d62c788cd7 | ||
|
|
21ff95e97b | ||
|
|
4e155f4c3b | ||
|
|
520d12631b | ||
|
|
d8582f7814 | ||
|
|
3ebda28fed | ||
|
|
3a0ff1f94d | ||
|
|
50a02e3ba4 | ||
|
|
bf99c6236e | ||
|
|
722a626e55 | ||
|
|
90a514d935 | ||
|
|
b5a2291ec6 | ||
|
|
d2d2dede3f | ||
|
|
3a5e12b895 | ||
|
|
dadd86056e | ||
|
|
dd4b967fbf | ||
|
|
b99faba3cd | ||
|
|
57a6693de7 | ||
|
|
a8b92a2211 | ||
|
|
7e433bf901 | ||
|
|
16155ed852 | ||
|
|
c84d20cdbe | ||
|
|
276b2ada0c | ||
|
|
2c4ae35127 | ||
|
|
021dc55898 | ||
|
|
c35796af7e | ||
|
|
6eb497770b | ||
|
|
ba17eb6a31 | ||
|
|
31c974c461 | ||
|
|
d4e7fdef7e | ||
|
|
70ea5ed597 | ||
|
|
dd9e3b60db | ||
|
|
6b023df258 | ||
|
|
d70c12e2a4 | ||
|
|
8f0c15e966 | ||
|
|
fdf88bfdd7 | ||
|
|
24e31f997c | ||
|
|
c53a4a0ce9 | ||
|
|
33aae30c92 | ||
|
|
d76e1ffc8f | ||
|
|
e6943b8f78 | ||
|
|
ba87fef3b2 | ||
|
|
a9aca1b8ed | ||
|
|
6975781a0b | ||
|
|
665fe67aa6 | ||
|
|
5b149c7ff2 | ||
|
|
35477d9c06 | ||
|
|
576baf6fff | ||
|
|
ca80c57797 | ||
|
|
7d2a4b155d | ||
|
|
ff68e0f66e | ||
|
|
d51112e6af | ||
|
|
3c9792545b | ||
|
|
1c79bd2646 | ||
|
|
c79ac8ba19 | ||
|
|
a399cca7ce | ||
|
|
3c0c054d62 | ||
|
|
3a07faf23a | ||
|
|
0bfa706a39 | ||
|
|
837267fb0a | ||
|
|
8d18dfbc3d | ||
|
|
d2155dc561 | ||
|
|
882219f962 | ||
|
|
8dfcdbed76 | ||
|
|
8017995ba3 | ||
|
|
ca399ffd0b | ||
|
|
a4c995a394 | ||
|
|
9deab33dc1 | ||
|
|
7c20c274d7 | ||
|
|
7339021684 | ||
|
|
579fb1bfa7 | ||
|
|
94358489ef | ||
|
|
504720e366 | ||
|
|
07de257371 | ||
|
|
0401ad96e5 | ||
|
|
efb91874f8 | ||
|
|
ebdcca4fa8 | ||
|
|
2287e0caf2 | ||
|
|
dde7ff4c45 | ||
|
|
6c52269dc5 | ||
|
|
736a3e4610 | ||
|
|
1e87b50998 | ||
|
|
f59a228daf | ||
|
|
db3a412c8a | ||
|
|
202f96876e | ||
|
|
2489d5e234 | ||
|
|
64ad2ba20c | ||
|
|
60c4e1b007 | ||
|
|
6e3b4fdd7b | ||
|
|
62a8f72b98 | ||
|
|
89441ac126 | ||
|
|
d9f7842aec | ||
|
|
65762b1dce | ||
|
|
ba24811356 | ||
|
|
573d52119a | ||
|
|
3cafacc3e9 | ||
|
|
a805b53d86 | ||
|
|
c0c59320a5 | ||
|
|
271bfcfb31 | ||
|
|
798a4bb2b9 | ||
|
|
df7f31098c | ||
|
|
fa9524f9c9 | ||
|
|
1368f059bb | ||
|
|
8701060355 | ||
|
|
ee067ed3f2 | ||
|
|
b57dedb74a | ||
|
|
d7c13adac0 | ||
|
|
bd2473a7bf | ||
|
|
de16cb71f1 | ||
|
|
ffc431753b | ||
|
|
28de82e63c | ||
|
|
6c65266f05 | ||
|
|
6ffe2f1af2 | ||
|
|
68ce0cf25f | ||
|
|
7cff8ddbd3 | ||
|
|
9d1eed6a82 | ||
|
|
eeadc829b9 | ||
|
|
efe040549c | ||
|
|
5bc9e65cdd | ||
|
|
d6a604051f | ||
|
|
51a03b30f2 | ||
|
|
561f3b2fbf | ||
|
|
3a7af2c8fe | ||
|
|
26c7c362d2 | ||
|
|
0642ea52c9 | ||
|
|
957e33207e | ||
|
|
a39fe345b5 | ||
|
|
40e72ddb8c | ||
|
|
195c3fd75d | ||
|
|
c626bccad3 | ||
|
|
e295188075 | ||
|
|
7804e1bd59 | ||
|
|
6b6889b8b4 | ||
|
|
f809c119bd | ||
|
|
a806dc920f | ||
|
|
74fc02b2cc | ||
|
|
c1fc987d96 | ||
|
|
7f97a61ed5 | ||
|
|
05a3db3cc0 | ||
|
|
145b7ce69a | ||
|
|
c6fa162728 | ||
|
|
527dc2cc48 | ||
|
|
7672ee3666 | ||
|
|
7ca4f33dcf | ||
|
|
78e4cb4410 | ||
|
|
baa50c136a | ||
|
|
24d0bedf7d | ||
|
|
38b8786ede | ||
|
|
146513aeb0 | ||
|
|
4dc5088fb2 | ||
|
|
4c53fd2bf6 | ||
|
|
885425e2a3 | ||
|
|
b74c77b74f | ||
|
|
1bc8e8fd3a | ||
|
|
6dbdb1d8ed | ||
|
|
ea5585e381 | ||
|
|
ace95a3355 | ||
|
|
019615f376 | ||
|
|
a8d40afd58 | ||
|
|
ebe3800c75 | ||
|
|
30b98bea85 | ||
|
|
d9738a7ee5 | ||
|
|
d778b938e7 | ||
|
|
5e9fa908b4 | ||
|
|
91808c5fd9 | ||
|
|
0784b1ee19 | ||
|
|
b3cdd72754 | ||
|
|
4a8913f7ea | ||
|
|
8fbd73af1e | ||
|
|
1be2b2c295 | ||
|
|
02778fd670 | ||
|
|
985f014f40 | ||
|
|
2a63d07146 | ||
|
|
947d30c5c4 | ||
|
|
e71ce3395d | ||
|
|
f5e7be25dd | ||
|
|
c8d024c25f | ||
|
|
b519ad8d18 | ||
|
|
fcb1f5dc54 | ||
|
|
9b7a2f93ad | ||
|
|
9ce253b644 | ||
|
|
24cbbb9a11 | ||
|
|
ed57c8f0fa | ||
|
|
07b99704ee | ||
|
|
9440e4d153 | ||
|
|
1840647458 | ||
|
|
c3114e65bf | ||
|
|
c2aa60f292 | ||
|
|
e4e29e1316 | ||
|
|
63f975ad43 | ||
|
|
a01fc5b758 | ||
|
|
481e31220b | ||
|
|
040ce4db81 | ||
|
|
8fb89b25d9 | ||
|
|
099c9024bb | ||
|
|
bd7e75f4b3 | ||
|
|
b3d61a6de7 | ||
|
|
e5b14e4177 | ||
|
|
0de86d3faf | ||
|
|
aa2159b7e5 | ||
|
|
1c30c24639 | ||
|
|
cd2ed8eeee | ||
|
|
112a3e27a6 | ||
|
|
bbac5a730f | ||
|
|
348c96bf13 | ||
|
|
fd783734ac | ||
|
|
fc4f375a65 | ||
|
|
1d6b47ceef | ||
|
|
cda1c876b2 | ||
|
|
6112ea2558 | ||
|
|
37129671b6 | ||
|
|
97f1655ff7 | ||
|
|
1cf509c365 | ||
|
|
6b530b0269 | ||
|
|
e525ab7d64 | ||
|
|
31d19fd51a | ||
|
|
74540e9f4d | ||
|
|
afc8a99d7e | ||
|
|
859a79ad35 | ||
|
|
6ab27328cc | ||
|
|
72c31ca524 | ||
|
|
4d69941593 | ||
|
|
0d19a55ddd | ||
|
|
ceef16f0c8 | ||
|
|
187f34dfee | ||
|
|
96b1e40c0a | ||
|
|
63d77a8b81 | ||
|
|
8022342e81 | ||
|
|
6379e15ce1 | ||
|
|
5b075623ff | ||
|
|
46cbaa053c | ||
|
|
ffc33b4c69 | ||
|
|
1d3773ab5e | ||
|
|
a968b4bec8 | ||
|
|
65902d11e4 | ||
|
|
050dc31c97 | ||
|
|
aae96faa76 | ||
|
|
0dea44acf4 | ||
|
|
b158f6f2b7 | ||
|
|
11da3d4e3c | ||
|
|
5f4fc247a4 | ||
|
|
78ba3c5394 | ||
|
|
781fa3ea0c | ||
|
|
a08b661af6 | ||
|
|
d0009ba05a | ||
|
|
ab9b5ba3ec | ||
|
|
70d39aae7b | ||
|
|
56ccc46f5f | ||
|
|
ff33905704 | ||
|
|
69739420fa | ||
|
|
2b593bc742 | ||
|
|
eb1b7baf1c | ||
|
|
05eef0e8f5 | ||
|
|
c2c634707e | ||
|
|
0579b326be | ||
|
|
70a26fec26 | ||
|
|
02ec9bb98e | ||
|
|
b2f608b884 | ||
|
|
a496dc8170 | ||
|
|
8ad15399d6 | ||
|
|
8e11514e7c | ||
|
|
89cb5573d9 | ||
|
|
5856d76e65 | ||
|
|
86b1f6ede3 | ||
|
|
572305d7c8 | ||
|
|
2aa93abc0f | ||
|
|
e2954d0117 | ||
|
|
497b95ec64 | ||
|
|
02e7594de5 | ||
|
|
7efa68e853 | ||
|
|
003fefa61d | ||
|
|
5c035768b0 | ||
|
|
9a878b0fbd | ||
|
|
779b026e6b | ||
|
|
ccae4e4ce2 | ||
|
|
d39a22945b | ||
|
|
aac7799e3e | ||
|
|
310f558e9c | ||
|
|
1a0428da97 | ||
|
|
4783b07407 | ||
|
|
0334c5a289 | ||
|
|
12a9a2e485 | ||
|
|
f9424231a9 | ||
|
|
d4ba70db5c | ||
|
|
a59f5e1c4f | ||
|
|
02316d4ee0 | ||
|
|
4f148de74d | ||
|
|
9f10d5207f | ||
|
|
484bde10b3 | ||
|
|
8281757da8 | ||
|
|
4e25e2eb81 | ||
|
|
f552426304 | ||
|
|
15295dd294 | ||
|
|
1405a1894b | ||
|
|
a1c8cfe967 | ||
|
|
f0b424c84f | ||
|
|
582746587b | ||
|
|
a69fe71406 | ||
|
|
13155ce64c | ||
|
|
c5d95ca805 | ||
|
|
0b2fc81be4 | ||
|
|
9e180b8ba7 | ||
|
|
e08a6e8a48 | ||
|
|
7d06c140fa | ||
|
|
d914406c84 | ||
|
|
31a3ff61e8 | ||
|
|
3773047a06 | ||
|
|
a1d089038d | ||
|
|
562e604cee | ||
|
|
71c906b00b | ||
|
|
6d22a407cc | ||
|
|
bdfbe3a87e | ||
|
|
cb3613db71 | ||
|
|
7dbfe15d1a | ||
|
|
ccf8f86c8d | ||
|
|
7425f974cb | ||
|
|
58e62ebd06 | ||
|
|
0de72d2dad | ||
|
|
7898ad772b | ||
|
|
25a1881127 | ||
|
|
34350cb7ec | ||
|
|
e130ec03ad | ||
|
|
0cd544b4ce | ||
|
|
6362be8196 | ||
|
|
aa4d9d62bc | ||
|
|
cd44a52084 | ||
|
|
98fd3c7379 | ||
|
|
73b5609e63 | ||
|
|
fe06370aa2 | ||
|
|
a1b1a7d702 | ||
|
|
ccdf0d9f95 | ||
|
|
287ce9c0ff | ||
|
|
b8ed846247 | ||
|
|
2a9a62d0f6 | ||
|
|
377bccf12a | ||
|
|
030a8e7111 | ||
|
|
189d926c6a | ||
|
|
d993fe217c | ||
|
|
8983c9bce1 | ||
|
|
ced1f36d19 | ||
|
|
e698fa664b | ||
|
|
ab2cf59bf6 | ||
|
|
a315ac9e54 | ||
|
|
c6347ec9c8 | ||
|
|
ac8379af29 | ||
|
|
69622e909b | ||
|
|
910167de09 | ||
|
|
29fb58de84 | ||
|
|
dbf17dea18 | ||
|
|
7fcbc0fd5a | ||
|
|
32c79b7a91 | ||
|
|
1647fc3017 | ||
|
|
17f43fd138 | ||
|
|
a708ceefe8 | ||
|
|
1f12902f78 | ||
|
|
cebbcf592e | ||
|
|
0c7994c502 | ||
|
|
d412df5883 | ||
|
|
cca3744396 | ||
|
|
c98c50291c | ||
|
|
e9035ab3e0 | ||
|
|
d8eb4432b9 | ||
|
|
8dcf544079 | ||
|
|
98cbf2f47f | ||
|
|
e041d87b5a | ||
|
|
5440aca212 | ||
|
|
834980eae7 | ||
|
|
a61822f9c9 | ||
|
|
6290859f81 | ||
|
|
62408af5ea | ||
|
|
711a8f0aae | ||
|
|
382988a06a | ||
|
|
88431bdbea | ||
|
|
62cb0ae28f | ||
|
|
31f5a2bb73 | ||
|
|
cb90cd9929 | ||
|
|
d402587ab2 | ||
|
|
02226f938e | ||
|
|
37309218ec | ||
|
|
03740f1369 | ||
|
|
d3314447a8 | ||
|
|
a8b170f51c | ||
|
|
a669a309f6 | ||
|
|
a628ba1b8f | ||
|
|
5bf87c4da1 | ||
|
|
b7c5b24d43 | ||
|
|
44bb4f2cc3 | ||
|
|
55a58eaf30 | ||
|
|
5d3ca843b6 | ||
|
|
ecf12aa0e7 | ||
|
|
427770a6d0 | ||
|
|
2d9604918a | ||
|
|
567a6c4a9f | ||
|
|
31b617b10f | ||
|
|
a6fe648a41 | ||
|
|
66163bee2b | ||
|
|
db7d352c97 | ||
|
|
cda92f4ba3 | ||
|
|
133613aeb1 | ||
|
|
15e01dfe5d | ||
|
|
6df7190472 | ||
|
|
5212d138f8 | ||
|
|
465582d20c | ||
|
|
fae60e9fae | ||
|
|
c89645e069 | ||
|
|
6465cf4f3e | ||
|
|
54c052c5eb | ||
|
|
465b8273eb | ||
|
|
129e0ce4b5 | ||
|
|
309b280d0d | ||
|
|
99d9b17a2d | ||
|
|
208a741823 | ||
|
|
c953d59b5a | ||
|
|
c0fefc4d1a | ||
|
|
0fcc979fe1 | ||
|
|
cb9d9bd8a1 | ||
|
|
1d5a68c9e7 | ||
|
|
8e3f45b899 | ||
|
|
ea87b9bfc2 | ||
|
|
df91ff7735 | ||
|
|
652fe97a1b | ||
|
|
06565765b8 | ||
|
|
fc9c7dcdfc | ||
|
|
72f1ce9096 | ||
|
|
0abf69dc1d | ||
|
|
a4f30ebf1d | ||
|
|
ed137a93ce | ||
|
|
e73ee5f690 | ||
|
|
de886d6c3d | ||
|
|
050d5f898f | ||
|
|
e3341a19cd | ||
|
|
8f5e3ad7c0 | ||
|
|
384c9b9725 | ||
|
|
981fa5db9a | ||
|
|
c417a9a55e | ||
|
|
52ab4d3ecd | ||
|
|
62bc2758eb | ||
|
|
731c8bf0fd | ||
|
|
d78da7b994 | ||
|
|
c795bd16a7 | ||
|
|
7237275f5d | ||
|
|
3e7b196950 | ||
|
|
919c8fa72a | ||
|
|
bcb97bce1c | ||
|
|
64d8883210 | ||
|
|
0af99769ad | ||
|
|
c8fded80d5 | ||
|
|
c6a3157fd8 | ||
|
|
8afb02cf03 | ||
|
|
675e4c1031 | ||
|
|
d21d03b738 | ||
|
|
ff70c08fb9 | ||
|
|
506a9a44af | ||
|
|
10e8634d38 | ||
|
|
a1577127b7 | ||
|
|
5a1f1d2c64 | ||
|
|
a915651797 | ||
|
|
133d054fc8 | ||
|
|
c87f6ca983 | ||
|
|
a2e1836e24 | ||
|
|
53649f6020 | ||
|
|
601e220411 | ||
|
|
ed6c4b1925 | ||
|
|
46ee0ce161 | ||
|
|
71b3110acf | ||
|
|
e6f2db70ef | ||
|
|
65829c437c | ||
|
|
f27defbdf4 | ||
|
|
41bf8eadfd | ||
|
|
d76bd84466 | ||
|
|
ca244c5dbb | ||
|
|
ff0ac568f4 | ||
|
|
5d0b2fae86 | ||
|
|
f8a8b3cf31 | ||
|
|
88396411f7 | ||
|
|
42931f9300 | ||
|
|
a5bd17c0c0 | ||
|
|
a5e5f81d88 | ||
|
|
66da2d8f48 | ||
|
|
b619ee00bf | ||
|
|
899d8bc24d | ||
|
|
bf586ba2cb | ||
|
|
22248dd9e9 | ||
|
|
6dfc7a815b | ||
|
|
b27ba0434a | ||
|
|
3b41b5e15f | ||
|
|
8029deb1e4 | ||
|
|
5ee9990d93 | ||
|
|
8a9c5fe4a1 | ||
|
|
45d769b05b | ||
|
|
af032476a8 | ||
|
|
c52309ad59 | ||
|
|
8526371d25 | ||
|
|
7f4a6fd94c | ||
|
|
6fc5d1aea3 | ||
|
|
1860b26491 | ||
|
|
fccbe1aff4 | ||
|
|
fa45a9f3a4 | ||
|
|
98cd592f3e | ||
|
|
64b81ff5a4 | ||
|
|
f3deb04521 | ||
|
|
ab454f1e1b | ||
|
|
bd6d732fc7 | ||
|
|
4eac8a5a47 | ||
|
|
82dd2ebd05 | ||
|
|
1cd194a1e1 | ||
|
|
f26a5554d5 | ||
|
|
c380039b3e | ||
|
|
9125f54897 | ||
|
|
08fe5a89e9 | ||
|
|
2e46bdf9d2 | ||
|
|
18b9c46a8b | ||
|
|
a99e23f9fc | ||
|
|
8b089222bd | ||
|
|
5b25c9ad1a | ||
|
|
ec80797694 | ||
|
|
8b7d6848b3 | ||
|
|
edb14e2a6b | ||
|
|
6a82644153 | ||
|
|
ac3401bc4f | ||
|
|
9da71b1655 | ||
|
|
ed31004aa8 | ||
|
|
fe50674c8e | ||
|
|
1fe1ea58f6 | ||
|
|
b3f1a30027 | ||
|
|
111a95d262 | ||
|
|
35528a7199 | ||
|
|
927b753262 | ||
|
|
8359016981 | ||
|
|
5372fe9d6e | ||
|
|
8814da3b78 | ||
|
|
2d748bf63c | ||
|
|
eff1f0d77a | ||
|
|
aec8ec43ef | ||
|
|
93cd1e7a4a | ||
|
|
ea4e5d76e0 | ||
|
|
e6298f8107 | ||
|
|
704a874e84 | ||
|
|
428ed6c88c | ||
|
|
dd62879b7b | ||
|
|
14a31cd5f8 | ||
|
|
8a7df36d88 | ||
|
|
fe56d96295 | ||
|
|
fabf90bed4 | ||
|
|
c3a84f1b73 | ||
|
|
5a5a1aaa7f | ||
|
|
aef753a3a0 | ||
|
|
08028db982 | ||
|
|
85b2fe37e2 | ||
|
|
81959b5ef3 | ||
|
|
657d9bb8e8 | ||
|
|
2dfee97563 | ||
|
|
7f075d11ee | ||
|
|
a38b7b71ac | ||
|
|
139ab95438 | ||
|
|
71ac67f9f1 | ||
|
|
0e1b856fc5 | ||
|
|
0235d77932 | ||
|
|
6e1ca54edc | ||
|
|
274213bc83 | ||
|
|
5a42cb1b2b | ||
|
|
ee99efba36 | ||
|
|
b16a95d0f2 | ||
|
|
2c8bac5666 | ||
|
|
8e6b643829 | ||
|
|
cc24d00fde | ||
|
|
ddc1069e86 | ||
|
|
c9704d3c97 | ||
|
|
34775eb8db | ||
|
|
90d6490fea | ||
|
|
acb9e5ebdf | ||
|
|
12fff7c97f | ||
|
|
acb12ea7d4 | ||
|
|
e94c35d331 | ||
|
|
a685a72660 | ||
|
|
4aaa7d0555 | ||
|
|
2f09aa291b | ||
|
|
bdf2f25259 | ||
|
|
5a0af27115 | ||
|
|
18c4bc060b | ||
|
|
d23aa67f7b | ||
|
|
5a127d0700 | ||
|
|
aa160940df | ||
|
|
c8829f2aa4 | ||
|
|
0985c8f092 | ||
|
|
279edf0665 | ||
|
|
50fac81a30 | ||
|
|
99b86e7a29 | ||
|
|
151e021ef9 | ||
|
|
dd97b6a4b7 | ||
|
|
f30e8b7b46 | ||
|
|
ba3923d706 | ||
|
|
622cc83204 | ||
|
|
8e18c3d3b6 | ||
|
|
0adbd5662f | ||
|
|
f00d1159c8 | ||
|
|
4c90ce437e | ||
|
|
e109093b83 | ||
|
|
4ba3411292 | ||
|
|
8e1be54759 | ||
|
|
be6f2634b5 | ||
|
|
7191e820a2 | ||
|
|
9eed249028 | ||
|
|
bbff2b24d0 | ||
|
|
b91fa734d2 | ||
|
|
302eae6aeb | ||
|
|
088afa6d71 | ||
|
|
5f96c415e3 | ||
|
|
6c9fa68d5a | ||
|
|
bc4f5a3788 | ||
|
|
f094ecfd88 | ||
|
|
13268f7c63 | ||
|
|
aa820ce347 | ||
|
|
6310059531 | ||
|
|
6f92859608 | ||
|
|
758ff920fc | ||
|
|
7ba8063940 | ||
|
|
61de118130 | ||
|
|
4bf64f7005 | ||
|
|
85eab87dce | ||
|
|
5ba0cc5860 | ||
|
|
08aae5be3b | ||
|
|
071bad218a | ||
|
|
027e7fc5c6 | ||
|
|
498e8a1486 | ||
|
|
09c6bc1d12 | ||
|
|
62521f38e8 | ||
|
|
a6785afa5a | ||
|
|
7ebb46a092 | ||
|
|
a9fcc0a002 | ||
|
|
08ed3c7fc4 | ||
|
|
3c1a23e7b1 | ||
|
|
c7403361fd | ||
|
|
d9efb0a84d | ||
|
|
4f81255970 | ||
|
|
e556ef84a4 | ||
|
|
9587003d44 | ||
|
|
8f7ea41fe8 | ||
|
|
b4284d18d5 | ||
|
|
048d401309 | ||
|
|
85a1f3ec20 | ||
|
|
959b007269 | ||
|
|
9b550f7293 | ||
|
|
dc26b1efa8 | ||
|
|
d356e57134 | ||
|
|
c5b0ffe310 | ||
|
|
1ffa7825ed | ||
|
|
d95f3cd1c5 | ||
|
|
6ba64d97c8 | ||
|
|
fa508a2c7e | ||
|
|
d04df564f0 | ||
|
|
5f192dd6ce | ||
|
|
6819922d26 | ||
|
|
ec726e863c | ||
|
|
610754981b | ||
|
|
485034dec0 | ||
|
|
9316124733 | ||
|
|
1b9d91dc1e | ||
|
|
15550e1855 | ||
|
|
43adc84dfd | ||
|
|
8d6aeebb61 | ||
|
|
1c8c0cfbf0 | ||
|
|
047e25f7f7 | ||
|
|
258e53a402 | ||
|
|
c8c4dfba6e | ||
|
|
0bed544188 | ||
|
|
3cc07d36a2 | ||
|
|
78b53a5215 | ||
|
|
886382f568 | ||
|
|
a9dd1b4568 | ||
|
|
323243f1ec | ||
|
|
25366c2a37 | ||
|
|
b98c9ebc1d | ||
|
|
cb44cd2e33 | ||
|
|
1fefd410be | ||
|
|
f5bb5ff9c1 | ||
|
|
e2e7c490db | ||
|
|
68cf346025 | ||
|
|
7927372b75 | ||
|
|
ca2c9018c6 | ||
|
|
9ed5621a78 | ||
|
|
58cf528463 | ||
|
|
4602fc1105 | ||
|
|
b9e2a1b21e | ||
|
|
f76f294f7f | ||
|
|
64fff7edd2 | ||
|
|
beb84dbe07 | ||
|
|
d9768b5378 | ||
|
|
34befdf1d2 | ||
|
|
21df6e43a3 | ||
|
|
bc7b8367a9 | ||
|
|
c7a6720593 | ||
|
|
cf0c3bda92 | ||
|
|
2db2a033ab | ||
|
|
cf72184883 | ||
|
|
62344dccda | ||
|
|
03caa1b8ce | ||
|
|
97c8f4c026 | ||
|
|
674e45f473 | ||
|
|
36fec07a17 | ||
|
|
0c694a018c | ||
|
|
f37b513f21 | ||
|
|
56c7466e64 | ||
|
|
519be4db06 | ||
|
|
4255c5578b | ||
|
|
5e698278a8 | ||
|
|
b09b0bcdbd | ||
|
|
909a51000f | ||
|
|
306b43624b | ||
|
|
ec22c23bc5 | ||
|
|
a4ba11eabb | ||
|
|
58559aff16 | ||
|
|
3e3511ac44 | ||
|
|
36ae029c18 | ||
|
|
4dc016f3e5 | ||
|
|
d90e99216b | ||
|
|
67838807e8 | ||
|
|
dc2cdd55c0 | ||
|
|
5b0f6013e8 | ||
|
|
52c956831e | ||
|
|
4f93897b71 | ||
|
|
5676c8f753 | ||
|
|
eed72600c5 | ||
|
|
35ca9691d2 | ||
|
|
072f7760a4 | ||
|
|
3c1599a1f4 | ||
|
|
90808124f5 | ||
|
|
3c96fcc888 | ||
|
|
d21a7ef8e2 | ||
|
|
b1ec12af43 | ||
|
|
7929dfec0f | ||
|
|
8bb223f6e3 | ||
|
|
8b911e6074 | ||
|
|
10a3d1df69 | ||
|
|
8cf581d496 | ||
|
|
0095ba8984 | ||
|
|
6f32aa95a7 | ||
|
|
afb43c5883 | ||
|
|
85ef54a9a1 | ||
|
|
2f581a1181 | ||
|
|
110f872779 | ||
|
|
54931afa8b | ||
|
|
2bdd2dab78 | ||
|
|
2908323702 | ||
|
|
5d37bfe2fa | ||
|
|
0b0bdb17aa | ||
|
|
e5b2ae967b | ||
|
|
c03ae0ab47 | ||
|
|
5d664cd00d | ||
|
|
00ed67f1b7 | ||
|
|
277ded519d | ||
|
|
7835c4047b | ||
|
|
ab0af4554c | ||
|
|
926414a014 | ||
|
|
3991c8c5f7 | ||
|
|
cb4d60ca8d | ||
|
|
7ebe1977d8 | ||
|
|
5e9c71e0e3 | ||
|
|
723287b305 | ||
|
|
46b2e45fd5 | ||
|
|
390999e042 | ||
|
|
8a7e165214 | ||
|
|
5742f5e811 | ||
|
|
52b6578fcf | ||
|
|
2f205f20c5 | ||
|
|
e1df7c3b4d | ||
|
|
7c2f806745 | ||
|
|
6877f0898f | ||
|
|
74ac6eae8f | ||
|
|
75610e92a7 | ||
|
|
7dc8b86001 | ||
|
|
dfbfcfecc7 | ||
|
|
7ac81f8fd5 | ||
|
|
fd36a0cc72 | ||
|
|
398c5aa009 | ||
|
|
7a90c0cd88 | ||
|
|
cfcca5f881 | ||
|
|
ca67265c79 | ||
|
|
b422fe297c | ||
|
|
856c47138a | ||
|
|
83cdf647c9 | ||
|
|
ff46a7dc50 | ||
|
|
9e479481cc | ||
|
|
532627fad2 | ||
|
|
aa23660d0c | ||
|
|
20db868592 | ||
|
|
02948657a4 | ||
|
|
050c66250c | ||
|
|
88ce4fe81b | ||
|
|
e819dd39ca | ||
|
|
cfb13ac59f | ||
|
|
00aee4be00 | ||
|
|
a669975fb0 | ||
|
|
161d719dba | ||
|
|
ccc75f2978 | ||
|
|
8b0598abee | ||
|
|
31598b4329 | ||
|
|
dd220458f8 | ||
|
|
d4163dde8e | ||
|
|
3bd563e495 | ||
|
|
525c5852fb | ||
|
|
69501bd110 | ||
|
|
95e086a543 | ||
|
|
f6cd2d279d | ||
|
|
9cab69a968 | ||
|
|
6906c7ba55 | ||
|
|
243c13b7ee | ||
|
|
09cc1f2db0 | ||
|
|
7d295bdc8b | ||
|
|
b6807fc264 | ||
|
|
b3ef770819 | ||
|
|
726f355e9c | ||
|
|
ad7e341b78 | ||
|
|
53a3076e1c | ||
|
|
02d935dbaa | ||
|
|
4507d1d46d | ||
|
|
2566707256 | ||
|
|
6b1113e355 | ||
|
|
de45269ec3 | ||
|
|
479a976459 | ||
|
|
332b5605d6 | ||
|
|
c3d16ddb30 | ||
|
|
b6fa67c49f | ||
|
|
1c7c96a245 | ||
|
|
0da6f27aac | ||
|
|
b5f8c73060 | ||
|
|
5e2db65383 | ||
|
|
fc08b55eac | ||
|
|
8229eb657e | ||
|
|
1c532d9637 | ||
|
|
c743ba893c | ||
|
|
a5eb3b6012 | ||
|
|
2d6306644c | ||
|
|
711f095ebc | ||
|
|
0705c9f540 | ||
|
|
1e52fe2749 | ||
|
|
65e7650b16 | ||
|
|
cc16a7a741 | ||
|
|
9d3f2d0cfd | ||
|
|
0cc3fe6ab4 | ||
|
|
9e03342690 | ||
|
|
8501969643 | ||
|
|
0f5d8ec2a6 | ||
|
|
7c084f855f | ||
|
|
af26f0f41e | ||
|
|
1c0e44cc8e | ||
|
|
e0ae584ea7 | ||
|
|
ab056f3190 | ||
|
|
16e827f3ba | ||
|
|
4fd36592dc | ||
|
|
4a1e637b83 | ||
|
|
d19a6f2615 | ||
|
|
c3f1210619 | ||
|
|
56fddcc5e2 | ||
|
|
2567e60834 | ||
|
|
315e3487a9 | ||
|
|
f040726478 | ||
|
|
27bb2cf826 | ||
|
|
2ff2ce4de4 | ||
|
|
69fd8e7eed | ||
|
|
66387a15b7 | ||
|
|
a0c1b4c05d | ||
|
|
d5252375e1 | ||
|
|
880efd1843 | ||
|
|
9823d07817 | ||
|
|
2b58487ad7 | ||
|
|
2c502e3997 | ||
|
|
32d481df26 | ||
|
|
b084eb03ff | ||
|
|
043a965db3 | ||
|
|
65b4b0310b | ||
|
|
5d50a773e7 | ||
|
|
c399f1117b | ||
|
|
d9327a02a2 | ||
|
|
617ecf11ec | ||
|
|
83caebf384 | ||
|
|
d3cba4d80e | ||
|
|
654ea7094c | ||
|
|
52a3974a78 | ||
|
|
cbd398d7f6 | ||
|
|
12f3edd4ab | ||
|
|
76f292a6ba | ||
|
|
9ce9d670c9 | ||
|
|
4888299b28 | ||
|
|
e4f3428344 | ||
|
|
c4e879ea96 | ||
|
|
83235b13c1 | ||
|
|
5de52b96f4 | ||
|
|
2aedd9b27b | ||
|
|
ea1a4c5add | ||
|
|
88864153eb | ||
|
|
9ad3d21b75 | ||
|
|
1c9d11de23 | ||
|
|
860d7b8446 | ||
|
|
5613e48141 | ||
|
|
3a251e2a38 | ||
|
|
c7c191c01e | ||
|
|
e0bc9d11f1 | ||
|
|
6087bc755a | ||
|
|
8952cff24d | ||
|
|
1c3de94c42 | ||
|
|
9bd3a66b16 | ||
|
|
314d229ccf | ||
|
|
5c2937b05c | ||
|
|
d1a31a9a2b | ||
|
|
5acf4e1a2e | ||
|
|
2b40952429 | ||
|
|
8b306df132 | ||
|
|
8a4fb82b98 | ||
|
|
e0c67b30f8 | ||
|
|
33efc60de7 | ||
|
|
5fc76aaa72 | ||
|
|
24579f67e2 | ||
|
|
d99196aeb1 | ||
|
|
7c531eb411 | ||
|
|
df5303912f | ||
|
|
4479a2b2ee | ||
|
|
05351b7809 | ||
|
|
a3ab3259e3 | ||
|
|
e87d9432c7 | ||
|
|
d0841f792e | ||
|
|
cef3777313 | ||
|
|
0e2d8c1a11 | ||
|
|
c055bd5327 | ||
|
|
3f8c4834a3 | ||
|
|
593549551a | ||
|
|
c0d767351a | ||
|
|
0b9ea25d86 | ||
|
|
fcd0feb61e | ||
|
|
1c4db22c28 | ||
|
|
bdc5b915e8 | ||
|
|
983221a710 | ||
|
|
1cf7ac9260 | ||
|
|
b3a8b1df38 | ||
|
|
cd42566daf | ||
|
|
5d2065b3e7 | ||
|
|
c57a758e91 | ||
|
|
892eea752b | ||
|
|
3f9e76b876 | ||
|
|
29988b5f58 | ||
|
|
951758ef30 | ||
|
|
d8da9af29d | ||
|
|
d774847d00 | ||
|
|
5e38ae1234 | ||
|
|
67b6eafbf0 | ||
|
|
08f7076f6e | ||
|
|
b9a6876d00 | ||
|
|
f76675a561 | ||
|
|
cae1379440 | ||
|
|
a7e34d706d | ||
|
|
2bca3504cd | ||
|
|
bca86191b3 | ||
|
|
0f61521c2d | ||
|
|
37357d4d80 | ||
|
|
38644689c5 | ||
|
|
615d860f82 | ||
|
|
074b2e6ecb | ||
|
|
a71e13ef07 | ||
|
|
b8f11e79c6 | ||
|
|
4f31596e4f | ||
|
|
a1857556ed | ||
|
|
802482fff3 | ||
|
|
371edbf827 | ||
|
|
f8df337893 | ||
|
|
3c8a0d0edb | ||
|
|
7f9d453afe | ||
|
|
69a705731b | ||
|
|
4aa156773c | ||
|
|
61e9d0723c | ||
|
|
2670890845 | ||
|
|
48c9d48789 | ||
|
|
a913369dd0 | ||
|
|
eed5a6f10b | ||
|
|
0634c4bc37 | ||
|
|
8375e3b444 | ||
|
|
790ac76a48 | ||
|
|
1d1ad75afb | ||
|
|
5bd18e4567 | ||
|
|
bda11adfd5 | ||
|
|
000fee96aa | ||
|
|
718cafcd04 | ||
|
|
908dcc0ec8 | ||
|
|
4f6b2bceae | ||
|
|
472b9d1757 | ||
|
|
8a2a608af0 | ||
|
|
60f4a5bc13 | ||
|
|
a5d839fe44 | ||
|
|
9ea64308cb | ||
|
|
b8cbf4e637 | ||
|
|
2808162037 | ||
|
|
9e0d8cae17 | ||
|
|
b6fef04761 | ||
|
|
b1bcf483c9 | ||
|
|
d2e004dc68 | ||
|
|
40361f1b8e | ||
|
|
17eb2f8d91 | ||
|
|
727ea4d473 | ||
|
|
46fa2110da | ||
|
|
ca81f5671a | ||
|
|
bc0eef9ae2 | ||
|
|
093354e231 | ||
|
|
d5f6dbe118 | ||
|
|
41c364002f | ||
|
|
3f34ccc8fb | ||
|
|
05305568ff | ||
|
|
c3766720fa | ||
|
|
c3efcbabb8 | ||
|
|
79b7480f4f | ||
|
|
1a96e594f0 | ||
|
|
32b3ca4159 | ||
|
|
aa2d0eb442 | ||
|
|
65bf3bb6ee | ||
|
|
bfd4efcd94 | ||
|
|
c1704235b4 | ||
|
|
572220ae1f | ||
|
|
48913cbb64 | ||
|
|
86ac0127a5 | ||
|
|
104361608c | ||
|
|
c291358858 | ||
|
|
86d515282e | ||
|
|
67258446f9 | ||
|
|
5c2919074f | ||
|
|
68682ce48a | ||
|
|
81dd95457f | ||
|
|
90e7fd8d74 | ||
|
|
d66cae9c49 | ||
|
|
2323512304 | ||
|
|
213df388f3 | ||
|
|
47747f3256 | ||
|
|
834dfe22dd | ||
|
|
1b190a32bd | ||
|
|
57ebc6344c | ||
|
|
443406f75f | ||
|
|
61ef6dd511 | ||
|
|
e660cbd6ee | ||
|
|
8fa249bab8 | ||
|
|
3524f414ff | ||
|
|
ad7921e32c | ||
|
|
90d0725fe0 | ||
|
|
c784ff1bc3 | ||
|
|
d5277925bc | ||
|
|
5b86b57771 | ||
|
|
c2483cbe4e | ||
|
|
9c3010074d | ||
|
|
199c740c17 | ||
|
|
63896485b0 | ||
|
|
01fc27cb49 | ||
|
|
3611edd1a0 | ||
|
|
63b0c8c67d | ||
|
|
32c0f90999 | ||
|
|
82d8a11021 | ||
|
|
2e9ff4c7d7 | ||
|
|
c95e2f450d | ||
|
|
cb13b9dd77 | ||
|
|
7747674b5c | ||
|
|
29e691a4da | ||
|
|
22b08f1a7f | ||
|
|
65ec773066 | ||
|
|
2f905b81ed | ||
|
|
1534ca41f7 | ||
|
|
7bdab0f23b | ||
|
|
82991a7fc2 | ||
|
|
3c5126c740 | ||
|
|
dbb6059558 | ||
|
|
92a35ba77f | ||
|
|
ddd7979a25 | ||
|
|
3fb26f69e5 | ||
|
|
0b9fc290d2 | ||
|
|
e2f6bd0e2e | ||
|
|
a6e47a0b47 | ||
|
|
be0f030ccf | ||
|
|
b46dab3099 | ||
|
|
c97f69517d | ||
|
|
053296de93 | ||
|
|
249f524ad9 | ||
|
|
62d340bcfe | ||
|
|
9287e66cfc | ||
|
|
98e1ac7375 | ||
|
|
f9fc8ba8aa | ||
|
|
2585798ab5 | ||
|
|
1d85260ccd | ||
|
|
e4f81fb62e | ||
|
|
1e582dea0a | ||
|
|
f00f761996 | ||
|
|
390f0016e6 | ||
|
|
3ecd9cb0cf | ||
|
|
9161c54c43 | ||
|
|
b4cf375044 | ||
|
|
b894f8dbb3 | ||
|
|
af88007cc3 | ||
|
|
c1e567afc7 | ||
|
|
1ee1a924ba | ||
|
|
704fa7a1be | ||
|
|
682b71565d | ||
|
|
3f742d9675 | ||
|
|
0e8ce13c07 | ||
|
|
e4a1b50edb | ||
|
|
832bd41f5d | ||
|
|
af5c2360ea | ||
|
|
86e16f58b9 | ||
|
|
13aa5be9b9 | ||
|
|
1e13740bda | ||
|
|
7e738dedc5 | ||
|
|
d809ac9999 | ||
|
|
952be93124 | ||
|
|
096f452cfa | ||
|
|
7b8a02ede8 | ||
|
|
94c73da943 | ||
|
|
03f6d12e50 | ||
|
|
8938aa4621 | ||
|
|
721fdc5918 | ||
|
|
ad2716c361 | ||
|
|
fae2c5d1b5 | ||
|
|
de1021e20c | ||
|
|
e5a58ef7c1 | ||
|
|
d36fff1772 | ||
|
|
8d259b9d8f | ||
|
|
7c7513ee25 | ||
|
|
4ffeff200e | ||
|
|
66c047e6d5 | ||
|
|
a00fd2c348 | ||
|
|
f707e5f053 | ||
|
|
a61c18c69c | ||
|
|
7234a99756 | ||
|
|
33d651847d | ||
|
|
2bb6befbca | ||
|
|
2f0d425b66 | ||
|
|
bad20d3fcc | ||
|
|
493caec5df | ||
|
|
7d78a3c347 | ||
|
|
3f45391d29 | ||
|
|
502f955091 | ||
|
|
4720b78db5 | ||
|
|
4a52a7f889 | ||
|
|
9f9948707a | ||
|
|
1cd363f33f | ||
|
|
4325b7e363 | ||
|
|
e994971bec | ||
|
|
0ddfb42d4d | ||
|
|
15f940054d | ||
|
|
b108de33ed | ||
|
|
982288ec04 | ||
|
|
804abb6d6d | ||
|
|
8f6cd86e56 | ||
|
|
abd32fbfd0 | ||
|
|
2acd353c7d | ||
|
|
456f2c0a99 | ||
|
|
ad6e61dd47 | ||
|
|
810b17ae06 | ||
|
|
a43d9d86df | ||
|
|
57206c639d | ||
|
|
1ede1458b9 | ||
|
|
f38e43a4b6 | ||
|
|
cb8b9363f6 | ||
|
|
8957a40328 | ||
|
|
1129872061 | ||
|
|
f2fee74880 | ||
|
|
1090f2817a | ||
|
|
d23a57d13b | ||
|
|
c93a19c0d1 | ||
|
|
940921ac15 | ||
|
|
452f494c21 | ||
|
|
03a005033d | ||
|
|
f46e8e8e1f | ||
|
|
18dff8527d | ||
|
|
81b430dbb4 | ||
|
|
3994a3e439 | ||
|
|
94d4bdfcc3 | ||
|
|
4721bee8f2 | ||
|
|
415cd1d83c | ||
|
|
bd12fa47d7 | ||
|
|
a7d425deba | ||
|
|
06a4e0996b | ||
|
|
06b50c9fe0 | ||
|
|
05f75e70da | ||
|
|
db2e87f1b6 | ||
|
|
db4c82c39c | ||
|
|
713e6b8461 | ||
|
|
be14074817 | ||
|
|
024d9946dc | ||
|
|
36e4c7cf93 | ||
|
|
ef5785bfb7 | ||
|
|
dc1f28a74e | ||
|
|
cff20c5a6d | ||
|
|
b001fea325 | ||
|
|
3dfb8b5dc1 | ||
|
|
31e82a7597 | ||
|
|
38055b586d | ||
|
|
1e716ff2c2 | ||
|
|
5566749742 | ||
|
|
7bf6a85bd3 | ||
|
|
679e659cbc | ||
|
|
4d7f40f6e8 | ||
|
|
dfec99780a | ||
|
|
b117030a47 | ||
|
|
c61a48735d | ||
|
|
e4a4448497 | ||
|
|
98d76dfe8c | ||
|
|
88cbeb015a | ||
|
|
2cf4a38398 | ||
|
|
7eb5855815 | ||
|
|
d954a5c525 | ||
|
|
509c8e9f13 | ||
|
|
4fdb85e678 | ||
|
|
31d2f65528 | ||
|
|
50efa8536d | ||
|
|
5d3a0da50e | ||
|
|
966e03ba64 | ||
|
|
22f4b82efa | ||
|
|
eb475769ca | ||
|
|
d3000a23e9 | ||
|
|
1fe28af3f0 | ||
|
|
6d5591020d | ||
|
|
9e4cad750a | ||
|
|
248732e6ff | ||
|
|
cd30bea0c8 | ||
|
|
43cab5f9b5 | ||
|
|
fa75ddf452 | ||
|
|
ce4076621a | ||
|
|
914fee9ae4 | ||
|
|
a3a7c60925 | ||
|
|
8103bdbff8 | ||
|
|
fc20cc7135 | ||
|
|
7fcc559edb | ||
|
|
f878cfcf67 | ||
|
|
3e0e47612c | ||
|
|
a96666b902 | ||
|
|
5b3a9f8f31 | ||
|
|
ef67fa328c | ||
|
|
8058ced90d | ||
|
|
5f41de87d2 | ||
|
|
c051cb83d2 | ||
|
|
568bc47b3e | ||
|
|
fe989530e3 | ||
|
|
51ea8afb24 | ||
|
|
5065e47565 | ||
|
|
0cbfc20293 | ||
|
|
2d595e86f3 | ||
|
|
82b21f34f5 | ||
|
|
68fa6afa17 | ||
|
|
c5950df6e4 | ||
|
|
d433096779 | ||
|
|
e4b63a5b51 | ||
|
|
8147b7de51 | ||
|
|
4510f657b9 | ||
|
|
810502c1f3 | ||
|
|
0df6f0c0ae | ||
|
|
a7abfd4242 | ||
|
|
5f84bcddd6 | ||
|
|
0c2c564be5 | ||
|
|
8bdfaf2c3d | ||
|
|
3ef58cdc39 | ||
|
|
17dc4f6f55 | ||
|
|
65bf36ecf9 | ||
|
|
187af71535 | ||
|
|
4877de4916 | ||
|
|
c6fda88fed | ||
|
|
422bc42cf6 | ||
|
|
f1f239d774 | ||
|
|
0141a13c92 | ||
|
|
aa8626739e | ||
|
|
3ed982d235 | ||
|
|
a38ba34049 | ||
|
|
9c144220b1 | ||
|
|
b10bf9c305 | ||
|
|
7cf0fa4824 | ||
|
|
0a2d1703f7 | ||
|
|
befcada46d | ||
|
|
d29839ea3f | ||
|
|
60b437e2c5 | ||
|
|
d1cb42946b | ||
|
|
3697ce7fd9 | ||
|
|
0204978b36 | ||
|
|
da04c53726 | ||
|
|
8f9acab3bd | ||
|
|
ded968ad58 | ||
|
|
581e9bbd58 | ||
|
|
cf3ae735b2 | ||
|
|
797544e668 | ||
|
|
eb8a902056 | ||
|
|
61294659bf | ||
|
|
0d92cb0803 | ||
|
|
639a87bfa1 | ||
|
|
9e817a7dce | ||
|
|
1888a4496a | ||
|
|
741908155a | ||
|
|
c15fb33fc2 | ||
|
|
4037f4ca80 | ||
|
|
d72c9f0dc4 | ||
|
|
bea48c5150 | ||
|
|
cbc7aa37f4 | ||
|
|
46860e5c2c | ||
|
|
ee5b9c6382 | ||
|
|
a11a7709bf | ||
|
|
c25e950914 | ||
|
|
12b8e52f70 | ||
|
|
f60cc6fb42 | ||
|
|
67f0035885 | ||
|
|
a92307b060 | ||
|
|
b55832e5ef | ||
|
|
304e836980 | ||
|
|
c9008ea277 | ||
|
|
654ccf2dea | ||
|
|
72c89c51e6 | ||
|
|
e31f41c2a9 | ||
|
|
aef6d2a326 | ||
|
|
62129c72c2 | ||
|
|
80f2735ea5 | ||
|
|
7fc9811975 | ||
|
|
b3ca7430b0 | ||
|
|
00cc64c6c5 | ||
|
|
c905822005 | ||
|
|
56fae9330f | ||
|
|
c1cb0025c1 | ||
|
|
89ebf1fa3f | ||
|
|
ddd1b35b71 | ||
|
|
ad86d48c6a | ||
|
|
e8b0e613fc | ||
|
|
b1dffe00e7 | ||
|
|
b494e1d2cc | ||
|
|
ec22bf21eb | ||
|
|
56d0570674 | ||
|
|
913091a716 | ||
|
|
b943a94b33 | ||
|
|
691771a13d | ||
|
|
a82a1734ce | ||
|
|
54607135b6 | ||
|
|
67748d6626 | ||
|
|
cb5a8c107d | ||
|
|
d64cfa5cad | ||
|
|
b7bcdddacd | ||
|
|
d24bff02f3 | ||
|
|
c883495f8a | ||
|
|
ddb09c499b | ||
|
|
539ca47bab | ||
|
|
9e40714e3c | ||
|
|
512d687615 | ||
|
|
b9d1d5e697 | ||
|
|
191a1ccc35 | ||
|
|
c39efb96bd | ||
|
|
246830c9d3 | ||
|
|
e7a767e042 | ||
|
|
5fb3040a80 | ||
|
|
69c3723aac | ||
|
|
5ac5f198aa | ||
|
|
a2270dc27a | ||
|
|
e5e677c0ad | ||
|
|
79f939bec5 | ||
|
|
798a19e468 | ||
|
|
371f8b3bf2 | ||
|
|
b0b4916d3c | ||
|
|
cb732763b5 | ||
|
|
42f6e4d1f0 | ||
|
|
59534a2447 | ||
|
|
558c78353c | ||
|
|
2cf2de3465 | ||
|
|
5bb778811f | ||
|
|
2c688062e7 | ||
|
|
a6b94a38fe | ||
|
|
22ed881805 | ||
|
|
dd1c5bb495 | ||
|
|
335481563f | ||
|
|
0a34ad88f5 | ||
|
|
9149319bd7 | ||
|
|
aa63b072ae | ||
|
|
ffa2f27878 | ||
|
|
69543470fa | ||
|
|
3a22a5e702 | ||
|
|
06bb00172e | ||
|
|
8c2b186b67 | ||
|
|
e90824bfab | ||
|
|
f682c42001 | ||
|
|
f4a69e43eb | ||
|
|
206a5a029d | ||
|
|
57cefb2591 | ||
|
|
ee97ef626f | ||
|
|
5c18b02cb8 | ||
|
|
d0a82c1421 | ||
|
|
91d288d781 | ||
|
|
d046791b3e | ||
|
|
9a45beec1d | ||
|
|
3519ecf224 | ||
|
|
72cecacea7 | ||
|
|
e17766e916 | ||
|
|
29841f9f57 | ||
|
|
06d4d1e158 | ||
|
|
a903b5d58c | ||
|
|
25651f4835 | ||
|
|
bab74edf89 | ||
|
|
652e4b96b3 | ||
|
|
34eb36323a | ||
|
|
12bee7772f | ||
|
|
283c0f7600 | ||
|
|
0b925e5afa | ||
|
|
80206e6b38 | ||
|
|
58284a4508 | ||
|
|
2ba5c4b9ec | ||
|
|
69eaa27ccc | ||
|
|
440779bcf0 | ||
|
|
0d468b3930 | ||
|
|
0f2fbf8e1c | ||
|
|
b746ad5504 | ||
|
|
a63b825690 | ||
|
|
ce96c83401 | ||
|
|
aa002b56c4 | ||
|
|
fa84108ebf | ||
|
|
a23131f094 | ||
|
|
a0d72b3cbc | ||
|
|
23cde30063 | ||
|
|
899a577980 | ||
|
|
682ceec402 | ||
|
|
68c731d668 | ||
|
|
1a6602149d | ||
|
|
dbef27ac6b | ||
|
|
28f890989d | ||
|
|
bd808a76fa | ||
|
|
2559b76747 | ||
|
|
4255179354 | ||
|
|
bd5d00c082 | ||
|
|
8bb578ad97 | ||
|
|
3bb1562b7f | ||
|
|
68b0c91fa7 | ||
|
|
8fa9b71579 | ||
|
|
532cfd9bda | ||
|
|
94081f82ce | ||
|
|
ed692c3836 | ||
|
|
e3b42e79b6 | ||
|
|
688b577d63 | ||
|
|
85b7ccf618 | ||
|
|
eed9b996fb | ||
|
|
3809bcbc90 | ||
|
|
0587df4ae2 | ||
|
|
53c0e0a246 | ||
|
|
bad44ec8ab | ||
|
|
fde4741333 | ||
|
|
c77322348c | ||
|
|
c3f5678854 | ||
|
|
5166994f7c | ||
|
|
ce6d286d70 | ||
|
|
df8c637a57 | ||
|
|
e2fa45f960 | ||
|
|
b28fd07cdb | ||
|
|
22c261c8ce | ||
|
|
3e98c78c12 | ||
|
|
6dce075e99 | ||
|
|
3d9e998a01 | ||
|
|
55a8195a67 | ||
|
|
936a3b334e | ||
|
|
1799809562 | ||
|
|
38e25f390f | ||
|
|
a765839771 | ||
|
|
20da63c238 | ||
|
|
408910e893 | ||
|
|
45449cfb84 | ||
|
|
611c0b5c1c | ||
|
|
9699664e09 | ||
|
|
3279ae601f | ||
|
|
e1b6ddf563 | ||
|
|
242419726d | ||
|
|
3794213aaf | ||
|
|
6a0737dacf | ||
|
|
873854810a | ||
|
|
555b1f1c06 | ||
|
|
5961931caf | ||
|
|
814f7e9283 | ||
|
|
20bb1af655 | ||
|
|
370ce804e3 | ||
|
|
2d18706219 | ||
|
|
8bfef044d2 | ||
|
|
28db47c72a | ||
|
|
55d46eb6c7 | ||
|
|
5f9c3e6073 | ||
|
|
3714cee51d | ||
|
|
d0021f0415 | ||
|
|
f5b6c58f30 | ||
|
|
f0d8c86b51 | ||
|
|
76d0c018ee | ||
|
|
e60a0fc4fb | ||
|
|
9dc2821bc9 | ||
|
|
924363ca1c | ||
|
|
3515112cb2 | ||
|
|
cdc5681694 | ||
|
|
3ba7cfbff2 | ||
|
|
1c54e6b937 | ||
|
|
869e3ec92f | ||
|
|
edbb0acc7c | ||
|
|
98ac81bda2 | ||
|
|
444f3c8010 | ||
|
|
b03b2f5629 | ||
|
|
72a182c914 | ||
|
|
da24fa6d20 | ||
|
|
369a00cb71 | ||
|
|
3445d093c7 | ||
|
|
f3ced9e0ed | ||
|
|
cc2159f77b | ||
|
|
9fe69cc80a | ||
|
|
a2b0b70b55 | ||
|
|
9357bdd9db | ||
|
|
0391cfff65 | ||
|
|
da4f5d3454 | ||
|
|
e2583f5380 | ||
|
|
df7abd1bc9 | ||
|
|
164c9f8755 | ||
|
|
384fad6c40 | ||
|
|
4bd7c3bdcf | ||
|
|
4e795ea783 | ||
|
|
e3a20d5d6b |
@@ -0,0 +1,42 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: springcloud/pipeline-base
|
||||
user: appuser
|
||||
environment:
|
||||
_JAVA_OPTIONS: "-Xms1024m -Xmx2048m"
|
||||
TERM: dumb
|
||||
branches:
|
||||
ignore:
|
||||
- gh-pages # list of branches to ignore
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: sc-netflix-{{ .Branch }}
|
||||
- run:
|
||||
name: "Download dependencies"
|
||||
command: |
|
||||
cd spring-cloud-netflix-hystrix-contract && ../mvnw clean install && \
|
||||
./mvnw -s .settings.xml -U --fail-never dependency:go-offline || true
|
||||
- save_cache:
|
||||
key: sc-netflix-{{ .Branch }}
|
||||
paths:
|
||||
- ~/.m2
|
||||
- run:
|
||||
name: "Running build"
|
||||
command: ./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
|
||||
- run:
|
||||
name: "Aggregate test results"
|
||||
when: always
|
||||
command: |
|
||||
mkdir -p ~/junit/
|
||||
find . -type f -regex ".*/target/.*-reports/.*" -exec cp {} ~/junit/ \;
|
||||
bash <(curl -s https://codecov.io/bash)
|
||||
- store_artifacts:
|
||||
path: ~/junit/
|
||||
destination: artifacts
|
||||
- store_test_results:
|
||||
path: ~/junit/
|
||||
destination: testartifacts
|
||||
@@ -7,6 +7,14 @@
|
||||
.settings/
|
||||
.springBeans
|
||||
target/
|
||||
bin/
|
||||
_site/
|
||||
.idea
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.factorypath
|
||||
*.log
|
||||
.shelf
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
-Xmx1024m -XX:CICompilerCount=1 -XX:TieredStopAtLevel=1 -Djava.security.egd=file:/dev/./urandom
|
||||
@@ -0,0 +1 @@
|
||||
-DaltSnapshotDeploymentRepository=repo.spring.io::default::https://repo.spring.io/libs-snapshot-local -P spring
|
||||
Vendored
BIN
Binary file not shown.
+1
@@ -0,0 +1 @@
|
||||
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip
|
||||
+11
-5
@@ -9,13 +9,19 @@
|
||||
</servers>
|
||||
<profiles>
|
||||
<profile>
|
||||
<!--
|
||||
N.B. this profile is only here to support users and IDEs that do not use Maven 3.3.
|
||||
It isn't needed on the command line if you use the wrapper script (mvnw) or if you use
|
||||
a native Maven with the right version. Eclipse users should points their Maven tooling to
|
||||
this settings file, or copy the profile into their ~/.m2/settings.xml.
|
||||
-->
|
||||
<id>spring</id>
|
||||
<activation><activeByDefault>true</activeByDefault></activation>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>http://repo.spring.io/libs-snapshot-local</url>
|
||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
@@ -23,7 +29,7 @@
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>http://repo.spring.io/libs-milestone-local</url>
|
||||
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
@@ -31,7 +37,7 @@
|
||||
<repository>
|
||||
<id>spring-releases</id>
|
||||
<name>Spring Releases</name>
|
||||
<url>http://repo.spring.io/release</url>
|
||||
<url>https://repo.spring.io/release</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
@@ -41,7 +47,7 @@
|
||||
<pluginRepository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>http://repo.spring.io/libs-snapshot-local</url>
|
||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
@@ -49,7 +55,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>
|
||||
|
||||
-27
@@ -1,27 +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:
|
||||
- mvn --settings .settings.xml install -P docs -q -U -DskipTests=true -Dmaven.test.redirectTestOutputToFile=true
|
||||
- ./docs/src/main/asciidoc/ghpages.sh
|
||||
script:
|
||||
- '[ "${TRAVIS_PULL_REQUEST}" != "false" ] || mvn --settings .settings.xml deploy -P release -nsu -Dmaven.test.redirectTestOutputToFile=true'
|
||||
- '[ "${TRAVIS_PULL_REQUEST}" = "false" ] || mvn --settings .settings.xml install -nsu -Dmaven.test.redirectTestOutputToFile=true'
|
||||
env:
|
||||
global:
|
||||
- GIT_NAME="Dave Syer"
|
||||
- GIT_EMAIL=dsyer@pivotal.io
|
||||
- CI_DEPLOY_USERNAME=buildmaster
|
||||
- secure: aeLXRC5oFSddwnZt1/7G2/OHr7jDbxz0ET7sej3I+eSbe3N5vbzQ6FC08es4l89l54ciXd90I1g2BMw7DTYKOO373FP78XPdAEbifJTU4DGd6fCELmoTtUPhjunBIk7E49hisPbv82892IYYA7qi/hzG548cPyZ1IgiJjq0NCsc=
|
||||
@@ -1,10 +1,10 @@
|
||||
require 'asciidoctor'
|
||||
require 'erb'
|
||||
|
||||
options = {:mkdirs => true, :safe => :unsafe, :attributes => 'linkcss'}
|
||||
options = {:mkdirs => true, :safe => :unsafe, :attributes => ['linkcss', 'allow-uri-read']}
|
||||
|
||||
guard 'shell' do
|
||||
watch(/^docs\/[A-Za-z].*\.adoc$/) {|m|
|
||||
watch(/^docs\/[A-Z-a-z][^#]*\.adoc$/) {|m|
|
||||
Asciidoctor.load_file('docs/src/main/asciidoc/README.adoc', :to_file => './README.adoc', safe: :safe, parse: false, attributes: 'allow-uri-read')
|
||||
Asciidoctor.render_file('docs/src/main/asciidoc/spring-cloud-netflix.adoc', options.merge(:to_dir => 'target/generated-docs'))
|
||||
}
|
||||
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
https://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+195
-91
@@ -1,6 +1,9 @@
|
||||
// Do not edit this file (e.g. go instead to src/main/asciidoc)
|
||||
|
||||
image::https://travis-ci.org/spring-cloud/spring-cloud-netflix.svg?branch=master[Build Status, link=https://travis-ci.org/spring-cloud/spring-cloud-netflix]
|
||||
image::https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/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"]
|
||||
|
||||
|
||||
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
|
||||
@@ -18,42 +21,54 @@ Intelligent Routing (Zuul) and Client Side Load Balancing (Ribbon).
|
||||
* Circuit Breaker: embedded Hystrix dashboard with declarative Java configuration
|
||||
* Declarative REST Client: Feign creates a dynamic implementation of an interface decorated with JAX-RS or Spring MVC annotations
|
||||
* Client Side Load Balancer: Ribbon
|
||||
* External Configuration: a bridge from the Sprnig Environment to Archaius (enables native configuration of Netflix components using Spring Boot conventions)
|
||||
* Router and Filter: automatic regsitration of Zuul filters, and a simple convention over configuration approach to reverse proxy creation
|
||||
* External Configuration: a bridge from the Spring Environment to Archaius (enables native configuration of Netflix components using Spring Boot conventions)
|
||||
* Router and Filter: automatic registration of Zuul filters, and a simple convention over configuration approach to reverse proxy creation
|
||||
|
||||
== Building
|
||||
|
||||
:jdkversion: 1.8
|
||||
|
||||
=== Basic Compile and Test
|
||||
|
||||
To build the source you will need to install
|
||||
http://maven.apache.org/run-maven/index.html[Apache Maven] v3.0.6 or above and JDK 1.7.
|
||||
To build the source you will need to install JDK {jdkversion}.
|
||||
|
||||
Spring Cloud uses Maven for most build-related activities, and you
|
||||
should be able to get off the ground quite quickly by cloning the
|
||||
project you are interested in and typing
|
||||
|
||||
----
|
||||
$ mvn install -s .settings.xml
|
||||
$ ./mvnw install
|
||||
----
|
||||
|
||||
NOTE: You may need to increase the amount of memory available to Maven by setting
|
||||
a `MAVEN_OPTS` environment variable with the value `-Xmx512m -XX:MaxPermSize=128m`
|
||||
NOTE: You can also install Maven (>=3.3.3) yourself and run the `mvn` command
|
||||
in place of `./mvnw` in the examples below. If you do that you also
|
||||
might need to add `-P spring` if your local Maven settings do not
|
||||
contain repository declarations for spring pre-release artifacts.
|
||||
|
||||
The `.settings.xml` is only required the first time (or after updates
|
||||
to dependencies). It is there to provide repository declarations so
|
||||
that those do not need to be hard coded in the project poms.
|
||||
NOTE: Be aware that you might need to increase the amount of memory
|
||||
available to Maven by setting a `MAVEN_OPTS` environment variable with
|
||||
a value like `-Xmx512m -XX:MaxPermSize=128m`. We try to cover this in
|
||||
the `.mvn` configuration, so if you find you have to do it to make a
|
||||
build succeed, please raise a ticket to get the settings added to
|
||||
source control.
|
||||
|
||||
For hints on how to build the project look in `.travis.yml` if there
|
||||
is one. There should be a "script" and maybe "install" command. Also
|
||||
look at the "services" section to see if any services need to be
|
||||
running locally (e.g. mongo or rabbit). Ignore the git-related bits
|
||||
that you might find in "before_install" since they will be able git
|
||||
that you might find in "before_install" since they're related to setting git
|
||||
credentials and you already have those.
|
||||
|
||||
If you need mongo, rabbit or redis, see the README in the https://github.com/spring-cloud-samples/scripts[scripts
|
||||
demo repository] for
|
||||
instructions. For example consider using the "fig.yml" with
|
||||
http://www.fig.sh/[Fig] to run them in Docker containers.
|
||||
The projects that require middleware generally include a
|
||||
`docker-compose.yml`, so consider using
|
||||
https://docs.docker.com/compose/[Docker Compose] to run the middeware servers
|
||||
in Docker containers. See the README in the
|
||||
https://github.com/spring-cloud-samples/scripts[scripts demo
|
||||
repository] for specific instructions about the common cases of mongo,
|
||||
rabbit and redis.
|
||||
|
||||
NOTE: If all else fails, build with the command from `.travis.yml` (usually
|
||||
`./mvnw install`).
|
||||
|
||||
=== Documentation
|
||||
|
||||
@@ -66,107 +81,64 @@ parsing or rendering it, just copying it to `${main.basedir}`
|
||||
any changes in the README it will then show up after a Maven build as
|
||||
a modified file in the correct place. Just commit it and push the change.
|
||||
|
||||
=== Pull Requests
|
||||
|
||||
Spring Cloud is released under the non-restrictive Apache 2.0 license,
|
||||
and follows a very standard Github development process, using Github
|
||||
tracker for issues and merging pull requests into master. If you want
|
||||
to contribute even something trivial please do not hesitate, but
|
||||
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]. 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 given
|
||||
the ability to merge pull requests.
|
||||
|
||||
==== Code Conventions and Housekeeping
|
||||
None of these is essential for a pull request, but they will all help. They can also be
|
||||
added after the original pull request but before a merge.
|
||||
|
||||
* Use the Spring Framework code format conventions. If you use Eclipse and you follow
|
||||
the ``Importing into eclipse'' instructions below you should get project specific
|
||||
formatting automatically. You can also import formatter settings using the
|
||||
`eclipse-code-formatter.xml` file from the `eclipse` folder. If using IntelliJ, you can
|
||||
use the http://plugins.jetbrains.com/plugin/6546[Eclipse Code Formatter Plugin]
|
||||
to import the same file.
|
||||
* Make sure all new `.java` files to have a simple Javadoc class comment with at least an
|
||||
`@author` tag identifying you, and preferably at least a paragraph on what the class is
|
||||
for.
|
||||
* Add the ASF license header comment to all new `.java` files (copy from existing files
|
||||
in the project)
|
||||
* Add yourself as an `@author` to the .java files that you modify substantially (more
|
||||
than cosmetic changes).
|
||||
* Add some Javadocs and, if you change the namespace, some XSD doc elements.
|
||||
* A few unit tests would help a lot as well -- someone has to do it.
|
||||
* If no-one else is using your branch, please rebase it against the current master (or
|
||||
other target branch in the main project).
|
||||
|
||||
=== Working with the code
|
||||
If you don't have an IDE preference we would recommend that you use
|
||||
http://www.springsource.com/developer/sts[Spring Tools Suite] or
|
||||
http://eclipse.org[Eclipse] when working with the code. We use the
|
||||
http://eclipse.org/m2e/[m2eclipe] eclipse plugin for maven support. Other IDEs and tools
|
||||
should also work without issue.
|
||||
https://www.springsource.com/developer/sts[Spring Tools Suite] or
|
||||
https://eclipse.org[Eclipse] when working with the code. We use the
|
||||
https://eclipse.org/m2e/[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 https://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".
|
||||
|
||||
NOTE: Older versions of m2e do not support Maven 3.3, so once the
|
||||
projects are imported into Eclipse you will also need to tell
|
||||
m2eclipse to use the right profile for the projects. If you
|
||||
see many different errors related to the POMs in the projects, check
|
||||
that you have an up to date installation. If you can't upgrade m2e,
|
||||
add the "spring" profile to your `settings.xml`. Alternatively you can
|
||||
copy the repository settings from the "spring" profile of the parent
|
||||
pom into your `settings.xml`.
|
||||
|
||||
==== Importing into eclipse without m2eclipse
|
||||
If you prefer not to use m2eclipse you can generate eclipse project metadata using the
|
||||
following command:
|
||||
|
||||
[indent=0]
|
||||
----
|
||||
$ mvn eclipse:eclipse
|
||||
$ ./mvnw eclipse:eclipse
|
||||
----
|
||||
|
||||
The generated eclipse projects can be imported by selecting `import existing projects`
|
||||
from the `file` menu.
|
||||
|
||||
==== Adding Project Lombok Agent
|
||||
|
||||
Spring Cloud uses [Project
|
||||
Lombok](http://projectlombok.org/features/index.html) to generate
|
||||
getters and setters etc. Compiling from the command line this
|
||||
shouldn't cause any problems, but in an IDE you need to add an agent
|
||||
to the JVM. Full instructions can be found in the Lombok website. The
|
||||
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 other IDEs
|
||||
Maven is well supported by most Java IDEs. Refer to you vendor documentation.
|
||||
|
||||
== Contributing
|
||||
|
||||
Spring Cloud is released under the non-restrictive Apache 2.0 license. If you would like
|
||||
to contribute something, or simply want to hack on the code this document should help
|
||||
you get started.
|
||||
:spring-cloud-build-branch: master
|
||||
|
||||
Spring Cloud is released under the non-restrictive Apache 2.0 license,
|
||||
and follows a very standard Github development process, using Github
|
||||
tracker for issues and merging pull requests into master. If you want
|
||||
to contribute even something trivial please do not hesitate, but
|
||||
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
|
||||
given the ability to merge pull requests.
|
||||
|
||||
=== Code of Conduct
|
||||
This project adheres to the Contributor Covenant https://github.com/spring-cloud/spring-cloud-build/blob/master/docs/src/main/asciidoc/code-of-conduct.adoc[code of
|
||||
conduct]. By participating, you are expected to uphold this code. Please report
|
||||
unacceptable behavior to spring-code-of-conduct@pivotal.io.
|
||||
|
||||
=== Code Conventions and Housekeeping
|
||||
None of these is essential for a pull request, but they will all help. They can also be
|
||||
added after the original pull request but before a merge.
|
||||
@@ -174,9 +146,9 @@ added after the original pull request but before a merge.
|
||||
* Use the Spring Framework code format conventions. If you use Eclipse
|
||||
you can import formatter settings using the
|
||||
`eclipse-code-formatter.xml` file from the
|
||||
https://github.com/spring-cloud/build/tree/master/eclipse-coding-conventions.xml[Spring
|
||||
https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-dependencies-parent/eclipse-code-formatter.xml[Spring
|
||||
Cloud Build] project. If using IntelliJ, you can use the
|
||||
http://plugins.jetbrains.com/plugin/6546[Eclipse Code Formatter
|
||||
https://plugins.jetbrains.com/plugin/6546[Eclipse Code Formatter
|
||||
Plugin] to import the same file.
|
||||
* Make sure all new `.java` files to have a simple Javadoc class comment with at least an
|
||||
`@author` tag identifying you, and preferably at least a paragraph on what the class is
|
||||
@@ -189,6 +161,138 @@ added after the original pull request but before a merge.
|
||||
* A few unit tests would help a lot as well -- someone has to do it.
|
||||
* If no-one else is using your branch, please rebase it against the current master (or
|
||||
other target branch in the main project).
|
||||
* When writing a commit message please follow http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html[these conventions],
|
||||
* When writing a commit message please follow https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html[these conventions],
|
||||
if you are fixing an existing issue please add `Fixes gh-XXXX` at the end of the commit
|
||||
message (where XXXX is the issue number).
|
||||
message (where XXXX is the issue number).
|
||||
|
||||
=== Checkstyle
|
||||
|
||||
Spring Cloud Build comes with a set of checkstyle rules. You can find them in the `spring-cloud-build-tools` module. The most notable files under the module are:
|
||||
|
||||
.spring-cloud-build-tools/
|
||||
----
|
||||
└── src
|
||||
├── checkstyle
|
||||
│ └── checkstyle-suppressions.xml <3>
|
||||
└── main
|
||||
└── resources
|
||||
├── checkstyle-header.txt <2>
|
||||
└── checkstyle.xml <1>
|
||||
----
|
||||
<1> Default Checkstyle rules
|
||||
<2> File header setup
|
||||
<3> Default suppression rules
|
||||
|
||||
==== Checkstyle configuration
|
||||
|
||||
Checkstyle rules are *disabled by default*. To add checkstyle to your project just define the following properties and plugins.
|
||||
|
||||
.pom.xml
|
||||
----
|
||||
<properties>
|
||||
<maven-checkstyle-plugin.failsOnError>true</maven-checkstyle-plugin.failsOnError> <1>
|
||||
<maven-checkstyle-plugin.failsOnViolation>true
|
||||
</maven-checkstyle-plugin.failsOnViolation> <2>
|
||||
<maven-checkstyle-plugin.includeTestSourceDirectory>true
|
||||
</maven-checkstyle-plugin.includeTestSourceDirectory> <3>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin> <4>
|
||||
<groupId>io.spring.javaformat</groupId>
|
||||
<artifactId>spring-javaformat-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin> <5>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin> <5>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
</build>
|
||||
----
|
||||
<1> Fails the build upon Checkstyle errors
|
||||
<2> Fails the build upon Checkstyle violations
|
||||
<3> Checkstyle analyzes also the test sources
|
||||
<4> Add the Spring Java Format plugin that will reformat your code to pass most of the Checkstyle formatting rules
|
||||
<5> Add checkstyle plugin to your build and reporting phases
|
||||
|
||||
If you need to suppress some rules (e.g. line length needs to be longer), then it's enough for you to define a file under `${project.root}/src/checkstyle/checkstyle-suppressions.xml` with your suppressions. Example:
|
||||
|
||||
.projectRoot/src/checkstyle/checkstyle-suppresions.xml
|
||||
----
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE suppressions PUBLIC
|
||||
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
|
||||
"https://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
|
||||
<suppressions>
|
||||
<suppress files=".*ConfigServerApplication\.java" checks="HideUtilityClassConstructor"/>
|
||||
<suppress files=".*ConfigClientWatch\.java" checks="LineLengthCheck"/>
|
||||
</suppressions>
|
||||
----
|
||||
|
||||
It's advisable to copy the `${spring-cloud-build.rootFolder}/.editorconfig` and `${spring-cloud-build.rootFolder}/.springformat` to your project. That way, some default formatting rules will be applied. You can do so by running this script:
|
||||
|
||||
```bash
|
||||
$ curl https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/.editorconfig -o .editorconfig
|
||||
$ touch .springformat
|
||||
```
|
||||
|
||||
=== IDE setup
|
||||
|
||||
==== Intellij IDEA
|
||||
|
||||
In order to setup Intellij you should import our coding conventions, inspection profiles and set up the checkstyle plugin.
|
||||
|
||||
.spring-cloud-build-tools/
|
||||
----
|
||||
└── src
|
||||
├── checkstyle
|
||||
│ └── checkstyle-suppressions.xml <3>
|
||||
└── main
|
||||
└── resources
|
||||
├── checkstyle-header.txt <2>
|
||||
├── checkstyle.xml <1>
|
||||
└── intellij
|
||||
├── Intellij_Project_Defaults.xml <4>
|
||||
└── Intellij_Spring_Boot_Java_Conventions.xml <5>
|
||||
----
|
||||
<1> Default Checkstyle rules
|
||||
<2> File header setup
|
||||
<3> Default suppression rules
|
||||
<4> Project defaults for Intellij that apply most of Checkstyle rules
|
||||
<5> Project style conventions for Intellij that apply most of Checkstyle rules
|
||||
|
||||
.Code style
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/{spring-cloud-build-branch}/docs/src/main/asciidoc/images/intellij-code-style.png[Code style]
|
||||
|
||||
Go to `File` -> `Settings` -> `Editor` -> `Code style`. There click on the icon next to the `Scheme` section. There, click on the `Import Scheme` value and pick the `Intellij IDEA code style XML` option. Import the `spring-cloud-build-tools/src/main/resources/intellij/Intellij_Spring_Boot_Java_Conventions.xml` file.
|
||||
|
||||
.Inspection profiles
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/{spring-cloud-build-branch}/docs/src/main/asciidoc/images/intellij-inspections.png[Code style]
|
||||
|
||||
Go to `File` -> `Settings` -> `Editor` -> `Inspections`. There click on the icon next to the `Profile` section. There, click on the `Import Profile` and import the `spring-cloud-build-tools/src/main/resources/intellij/Intellij_Project_Defaults.xml` file.
|
||||
|
||||
.Checkstyle
|
||||
|
||||
To have Intellij work with Checkstyle, you have to install the `Checkstyle` plugin. It's advisable to also install the `Assertions2Assertj` to automatically convert the JUnit assertions
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/{spring-cloud-build-branch}/docs/src/main/asciidoc/images/intellij-checkstyle.png[Checkstyle]
|
||||
|
||||
Go to `File` -> `Settings` -> `Other settings` -> `Checkstyle`. There click on the `+` icon in the `Configuration file` section. There, you'll have to define where the checkstyle rules should be picked from. In the image above, we've picked the rules from the cloned Spring Cloud Build repository. However, you can point to the Spring Cloud Build's GitHub repository (e.g. for the `checkstyle.xml` : `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/main/resources/checkstyle.xml`). We need to provide the following variables:
|
||||
|
||||
- `checkstyle.header.file` - please point it to the Spring Cloud Build's, `spring-cloud-build-tools/src/main/resources/checkstyle/checkstyle-header.txt` file either in your cloned repo or via the `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/main/resources/checkstyle-header.txt` URL.
|
||||
- `checkstyle.suppressions.file` - default suppressions. Please point it to the Spring Cloud Build's, `spring-cloud-build-tools/src/checkstyle/checkstyle-suppressions.xml` file either in your cloned repo or via the `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/checkstyle/checkstyle-suppressions.xml` URL.
|
||||
- `checkstyle.additional.suppressions.file` - this variable corresponds to suppressions in your local project. E.g. you're working on `spring-cloud-contract`. Then point to the `project-root/src/checkstyle/checkstyle-suppressions.xml` folder. Example for `spring-cloud-contract` would be: `/home/username/spring-cloud-contract/src/checkstyle/checkstyle-suppressions.xml`.
|
||||
|
||||
IMPORTANT: Remember to set the `Scan Scope` to `All sources` since we apply checkstyle rules for production and test sources.
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
# Running Spring Platform components
|
||||
|
||||
## Config Server
|
||||
|
||||
`spring-platform-config$ java -Dspring.platform.config.server.uri=https://github.com/spencergibb/config-repo -jar spring-platform-config-server/target/spring-platform-config-server-1.0.0.BUILD-SNAPSHOT-exec.jar`
|
||||
|
||||
## Netflix Eureka
|
||||
|
||||
`spring-boot-eureka$ mvn spring-boot:run`
|
||||
|
||||
http://localhost:8080/eureka/jsp/status.jsp
|
||||
|
||||
Error running executable war:
|
||||
|
||||
2014-07-14 18:14:25,713 ERROR org.apache.juli.logging.DirectJDKLog:185 [localhost-startStop-1] [log] Exception starting filter servletContainer
|
||||
com.sun.jersey.core.spi.scanning.ScannerException: IO error when scanning jar jar:file:/Users/sgibb/workspace/spring/spring-boot-eureka/target/eureka-server-1.0.0.BUILD-SNAPSHOT.war!/WEB-INF/lib/jersey-apache-client4-1.11.jar!/com/sun/jersey
|
||||
at com.sun.jersey.core.spi.scanning.uri.JarZipSchemeScanner.scan(JarZipSchemeScanner.java:82)
|
||||
at com.sun.jersey.core.spi.scanning.PackageNamesScanner.scan(PackageNamesScanner.java:223)
|
||||
at com.sun.jersey.core.spi.scanning.PackageNamesScanner.scan(PackageNamesScanner.java:139)
|
||||
at com.sun.jersey.api.core.ScanningResourceConfig.init(ScanningResourceConfig.java:80)
|
||||
at com.sun.jersey.api.core.PackagesResourceConfig.init(PackagesResourceConfig.java:104)
|
||||
at com.sun.jersey.api.core.PackagesResourceConfig.<init>(PackagesResourceConfig.java:78)
|
||||
at com.sun.jersey.api.core.PackagesResourceConfig.<init>(PackagesResourceConfig.java:89)
|
||||
at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:700)
|
||||
at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:678)
|
||||
at com.sun.jersey.spi.container.servlet.WebComponent.init(WebComponent.java:203)
|
||||
at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:374)
|
||||
at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:727)
|
||||
at org.apache.catalina.core.ApplicationFilterConfig.initFilter(ApplicationFilterConfig.java:279)
|
||||
at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:109)
|
||||
at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4809)
|
||||
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5485)
|
||||
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
|
||||
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
|
||||
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
|
||||
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
|
||||
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
|
||||
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
|
||||
at java.lang.Thread.run(Thread.java:744)
|
||||
Caused by: java.io.FileNotFoundException: /Users/sgibb/workspace/spring/spring-boot-eureka/target/eureka-server-1.0.0.BUILD-SNAPSHOT.war!/WEB-INF/lib/jersey-apache-client4-1.11.jar (No such file or directory)
|
||||
at java.io.FileInputStream.open(Native Method)
|
||||
at java.io.FileInputStream.<init>(FileInputStream.java:146)
|
||||
at java.io.FileInputStream.<init>(FileInputStream.java:101)
|
||||
at sun.net.www.protocol.file.FileURLConnection.connect(FileURLConnection.java:90)
|
||||
at sun.net.www.protocol.file.FileURLConnection.getInputStream(FileURLConnection.java:188)
|
||||
at java.net.URL.openStream(URL.java:1037)
|
||||
at com.sun.jersey.core.spi.scanning.uri.JarZipSchemeScanner.closing(JarZipSchemeScanner.java:123)
|
||||
at com.sun.jersey.core.spi.scanning.uri.JarZipSchemeScanner.scan(JarZipSchemeScanner.java:75)
|
||||
... 22 more
|
||||
|
||||
|
||||
## Netflix Zuul
|
||||
|
||||
`spring-platform-netflix-zuul$ java -Dzuul.filterRoot=src/main/resources/filters -jar target/spring-platform-netflix-zuul-1.0.0.BUILD-SNAPSHOT.jar`
|
||||
|
||||
http://localhost:6080/ (routes to Sample Frontend)
|
||||
|
||||
## Netflix Hystrix Dashboard
|
||||
|
||||
`spring-platform-netflix-hystrix$ java -jar target/spring-platform-netflix-hystrix-1.0.0.BUILD-SNAPSHOT.war`
|
||||
|
||||
## Netflix Turbine
|
||||
|
||||
`spring-platform-netflix-turbine$ java -jar target/spring-platform-netflix-turbine-1.0.0.BUILD-SNAPSHOT.war --InstanceDiscovery.impl=io.spring.platform.netflix.turbine.EurekaInstanceDiscovery --turbine.appConfig=samplefrontendservice --turbine.aggregator.clusterConfig=SAMPLEAPPS --turbine.instanceUrlSuffix=/hystrix.stream --turbine.instanceInsertPort=true`
|
||||
|
||||
## Sandbox Sample Backend
|
||||
|
||||
`spring-platform-sandbox-sample-backend$ java -jar target/spring-platform-sandbox-sample-backend-1.0.0.BUILD-SNAPSHOT.jar`
|
||||
|
||||
## Sandbox Sample Frontend
|
||||
|
||||
`spring-platform-sandbox-sample-frontend$ java -jar target/spring-platform-sandbox-sample-frontend-1.0.0.BUILD-SNAPSHOT.jar`
|
||||
|
||||
http://localhost:9080/
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* Asciidoctor default stylesheet | MIT License | http://asciidoctor.org */
|
||||
/* Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */
|
||||
/* Remove the comments around the @import statement below when using this as a custom stylesheet */
|
||||
/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic|Noto+Serif:400,400italic,700,700italic|Droid+Sans+Mono:400";*/
|
||||
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
releaser:
|
||||
maven:
|
||||
buildCommand: ./scripts/build.sh {{systemProps}}
|
||||
@@ -1,22 +0,0 @@
|
||||
// Do not edit this file (e.g. go instead to src/main/asciidoc)
|
||||
|
||||
|
||||
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
|
||||
simple annotations you can quickly enable and configure the common patterns inside your
|
||||
application and build large distributed systems with battle-tested Netflix components. The
|
||||
patterns provided include Service Discovery (Eureka), Circuit Breaker (Hystrix),
|
||||
Intelligent Routing (Zuul) and Client Side Load Balancing (Ribbon).
|
||||
|
||||
|
||||
== Features
|
||||
|
||||
* Service Discovery: Eureka instances can be registered and clients can discover the instances using Spring-managed beans
|
||||
* Service Discovery: an embedded Eureka server can be created with declarative Java configuration
|
||||
* Circuit Breaker: Hystrix clients can be built with a simple annotation-driven method decorator
|
||||
* Circuit Breaker: embedded Hystrix dashboard with declarative Java configuration
|
||||
* Declarative REST Client: Feign creates a dynamic implementation of an interface decorated with JAX-RS or Spring MVC annotations
|
||||
* Client Side Load Balancer: Ribbon
|
||||
* External Configuration: a bridge from the Sprnig Environment to Archaius (enabls native configuration of Netflix components using Spring Boot conventions)
|
||||
* Router and Filter: automatic regsitration of Zuul filters,a nd a simple convention over configuration approach to reverse proxy creation
|
||||
|
||||
+22
-4
@@ -1,12 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-build</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath />
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.4.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-docs</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
@@ -15,17 +14,36 @@
|
||||
<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>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<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>
|
||||
|
||||
@@ -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[]
|
||||
|
||||
@@ -10,12 +13,12 @@ include::intro.adoc[]
|
||||
* Circuit Breaker: embedded Hystrix dashboard with declarative Java configuration
|
||||
* Declarative REST Client: Feign creates a dynamic implementation of an interface decorated with JAX-RS or Spring MVC annotations
|
||||
* Client Side Load Balancer: Ribbon
|
||||
* External Configuration: a bridge from the Sprnig Environment to Archaius (enables native configuration of Netflix components using Spring Boot conventions)
|
||||
* Router and Filter: automatic regsitration of Zuul filters, and a simple convention over configuration approach to reverse proxy creation
|
||||
* External Configuration: a bridge from the Spring Environment to Archaius (enables native configuration of Netflix components using Spring Boot conventions)
|
||||
* Router and Filter: automatic registration of Zuul filters, and a simple convention over configuration approach to reverse proxy creation
|
||||
|
||||
== Building
|
||||
|
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/building.adoc[]
|
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/building-jdk8.adoc[]
|
||||
|
||||
== Contributing
|
||||
|
||||
|
||||
@@ -1,46 +1,330 @@
|
||||
#!/bin/bash -x
|
||||
|
||||
git remote set-url --push origin `git config remote.origin.url | sed -e 's/^git:/https:/'`
|
||||
set -e
|
||||
|
||||
if ! (git remote set-branches --add origin gh-pages && git fetch -q); then
|
||||
echo "No gh-pages, so not syncing"
|
||||
exit 0
|
||||
fi
|
||||
# Set default props like MAVEN_PATH, ROOT_FOLDER etc.
|
||||
function set_default_props() {
|
||||
# The script should be executed from the root folder
|
||||
ROOT_FOLDER=`pwd`
|
||||
echo "Current folder is ${ROOT_FOLDER}"
|
||||
|
||||
if ! [ -d docs/target/generated-docs ]; then
|
||||
echo "No gh-pages sources in docs/target/generated-docs, so not syncing"
|
||||
exit 0
|
||||
fi
|
||||
if [[ ! -e "${ROOT_FOLDER}/.git" ]]; then
|
||||
echo "You're not in the root folder of the project!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Prop that will let commit the changes
|
||||
COMMIT_CHANGES="no"
|
||||
MAVEN_PATH=${MAVEN_PATH:-}
|
||||
echo "Path to Maven is [${MAVEN_PATH}]"
|
||||
REPO_NAME=${PWD##*/}
|
||||
echo "Repo name is [${REPO_NAME}]"
|
||||
SPRING_CLOUD_STATIC_REPO=${SPRING_CLOUD_STATIC_REPO:-git@github.com:spring-cloud/spring-cloud-static.git}
|
||||
echo "Spring Cloud Static repo is [${SPRING_CLOUD_STATIC_REPO}"
|
||||
}
|
||||
|
||||
# Check if gh-pages exists and docs have been built
|
||||
function check_if_anything_to_sync() {
|
||||
git remote set-url --push origin `git config remote.origin.url | sed -e 's/^git:/https:/'`
|
||||
|
||||
if ! (git remote set-branches --add origin gh-pages && git fetch -q); then
|
||||
echo "No gh-pages, so not syncing"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! [ -d docs/target/generated-docs ] && ! [ "${BUILD}" == "yes" ]; then
|
||||
echo "No gh-pages sources in docs/target/generated-docs, so not syncing"
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
function retrieve_current_branch() {
|
||||
# Code getting the name of the current branch. For master we want to publish as we did until now
|
||||
# https://stackoverflow.com/questions/1593051/how-to-programmatically-determine-the-current-checked-out-git-branch
|
||||
# If there is a branch already passed will reuse it - otherwise will try to find it
|
||||
CURRENT_BRANCH=${BRANCH}
|
||||
if [[ -z "${CURRENT_BRANCH}" ]] ; then
|
||||
CURRENT_BRANCH=$(git symbolic-ref -q HEAD)
|
||||
CURRENT_BRANCH=${CURRENT_BRANCH##refs/heads/}
|
||||
CURRENT_BRANCH=${CURRENT_BRANCH:-HEAD}
|
||||
fi
|
||||
echo "Current branch is [${CURRENT_BRANCH}]"
|
||||
git checkout ${CURRENT_BRANCH} || echo "Failed to check the branch... continuing with the script"
|
||||
}
|
||||
|
||||
# Switches to the provided value of the release version. We always prefix it with `v`
|
||||
function switch_to_tag() {
|
||||
git checkout v${VERSION}
|
||||
}
|
||||
|
||||
# Build the docs if switch is on
|
||||
function build_docs_if_applicable() {
|
||||
if [[ "${BUILD}" == "yes" ]] ; then
|
||||
./mvnw clean install -P docs -pl docs -DskipTests
|
||||
fi
|
||||
}
|
||||
|
||||
# Get the name of the `docs.main` property
|
||||
# Get whitelisted branches - assumes that a `docs` module is available under `docs` profile
|
||||
function retrieve_doc_properties() {
|
||||
MAIN_ADOC_VALUE=$("${MAVEN_PATH}"mvn -q \
|
||||
-Dexec.executable="echo" \
|
||||
-Dexec.args='${docs.main}' \
|
||||
--non-recursive \
|
||||
org.codehaus.mojo:exec-maven-plugin:1.3.1:exec)
|
||||
echo "Extracted 'main.adoc' from Maven build [${MAIN_ADOC_VALUE}]"
|
||||
|
||||
|
||||
WHITELIST_PROPERTY=${WHITELIST_PROPERTY:-"docs.whitelisted.branches"}
|
||||
WHITELISTED_BRANCHES_VALUE=$("${MAVEN_PATH}"mvn -q \
|
||||
-Dexec.executable="echo" \
|
||||
-Dexec.args="\${${WHITELIST_PROPERTY}}" \
|
||||
org.codehaus.mojo:exec-maven-plugin:1.3.1:exec \
|
||||
-P docs \
|
||||
-pl docs)
|
||||
echo "Extracted '${WHITELIST_PROPERTY}' from Maven build [${WHITELISTED_BRANCHES_VALUE}]"
|
||||
}
|
||||
|
||||
# Stash any outstanding changes
|
||||
###################################################################
|
||||
git diff-index --quiet HEAD
|
||||
dirty=$?
|
||||
if [ "$dirty" != "0" ]; then git stash; fi
|
||||
function stash_changes() {
|
||||
git diff-index --quiet HEAD && dirty=$? || (echo "Failed to check if the current repo is dirty. Assuming that it is." && dirty="1")
|
||||
if [ "$dirty" != "0" ]; then git stash; fi
|
||||
}
|
||||
|
||||
# Switch to gh-pages branch to sync it with master
|
||||
###################################################################
|
||||
git checkout gh-pages
|
||||
|
||||
for f in docs/target/generated-docs/*; do
|
||||
file=${f#docs/target/generated-docs/*}
|
||||
if ! git ls-files -i -o --exclude-standard --directory | grep -q ^$file$; then
|
||||
# Not ignored...
|
||||
cp -rf $f .
|
||||
git add -A $file
|
||||
# Switch to gh-pages branch to sync it with current branch
|
||||
function add_docs_from_target() {
|
||||
local DESTINATION_REPO_FOLDER
|
||||
if [[ -z "${DESTINATION}" && -z "${CLONE}" ]] ; then
|
||||
DESTINATION_REPO_FOLDER=${ROOT_FOLDER}
|
||||
elif [[ "${CLONE}" == "yes" ]]; then
|
||||
mkdir -p ${ROOT_FOLDER}/target
|
||||
local clonedStatic=${ROOT_FOLDER}/target/spring-cloud-static
|
||||
if [[ ! -e "${clonedStatic}/.git" ]]; then
|
||||
echo "Cloning Spring Cloud Static to target"
|
||||
git clone ${SPRING_CLOUD_STATIC_REPO} ${clonedStatic} && git checkout gh-pages
|
||||
else
|
||||
echo "Spring Cloud Static already cloned - will pull changes"
|
||||
cd ${clonedStatic} && git checkout gh-pages && git pull origin gh-pages
|
||||
fi
|
||||
DESTINATION_REPO_FOLDER=${clonedStatic}/${REPO_NAME}
|
||||
mkdir -p ${DESTINATION_REPO_FOLDER}
|
||||
else
|
||||
if [[ ! -e "${DESTINATION}/.git" ]]; then
|
||||
echo "[${DESTINATION}] is not a git repository"
|
||||
exit 1
|
||||
fi
|
||||
DESTINATION_REPO_FOLDER=${DESTINATION}/${REPO_NAME}
|
||||
mkdir -p ${DESTINATION_REPO_FOLDER}
|
||||
echo "Destination was provided [${DESTINATION}]"
|
||||
fi
|
||||
cd ${DESTINATION_REPO_FOLDER}
|
||||
git checkout gh-pages
|
||||
git pull origin gh-pages
|
||||
|
||||
# Add git branches
|
||||
###################################################################
|
||||
if [[ -z "${VERSION}" ]] ; then
|
||||
copy_docs_for_current_version
|
||||
else
|
||||
copy_docs_for_provided_version
|
||||
fi
|
||||
commit_changes_if_applicable
|
||||
}
|
||||
|
||||
|
||||
# Copies the docs by using the retrieved properties from Maven build
|
||||
function copy_docs_for_current_version() {
|
||||
if [[ "${CURRENT_BRANCH}" == "master" ]] ; then
|
||||
echo -e "Current branch is master - will copy the current docs only to the root folder"
|
||||
for f in docs/target/generated-docs/*; do
|
||||
file=${f#docs/target/generated-docs/*}
|
||||
if ! git ls-files -i -o --exclude-standard --directory | grep -q ^$file$; then
|
||||
# Not ignored...
|
||||
cp -rf $f ${ROOT_FOLDER}/
|
||||
git add -A ${ROOT_FOLDER}/$file
|
||||
fi
|
||||
done
|
||||
COMMIT_CHANGES="yes"
|
||||
else
|
||||
echo -e "Current branch is [${CURRENT_BRANCH}]"
|
||||
# https://stackoverflow.com/questions/29300806/a-bash-script-to-check-if-a-string-is-present-in-a-comma-separated-list-of-strin
|
||||
if [[ ",${WHITELISTED_BRANCHES_VALUE}," = *",${CURRENT_BRANCH},"* ]] ; then
|
||||
mkdir -p ${ROOT_FOLDER}/${CURRENT_BRANCH}
|
||||
echo -e "Branch [${CURRENT_BRANCH}] is whitelisted! Will copy the current docs to the [${CURRENT_BRANCH}] folder"
|
||||
for f in docs/target/generated-docs/*; do
|
||||
file=${f#docs/target/generated-docs/*}
|
||||
if ! git ls-files -i -o --exclude-standard --directory | grep -q ^$file$; then
|
||||
# Not ignored...
|
||||
# We want users to access 1.0.0.RELEASE/ instead of 1.0.0.RELEASE/spring-cloud.sleuth.html
|
||||
if [[ "${file}" == "${MAIN_ADOC_VALUE}.html" ]] ; then
|
||||
# We don't want to copy the spring-cloud-sleuth.html
|
||||
# we want it to be converted to index.html
|
||||
cp -rf $f ${ROOT_FOLDER}/${CURRENT_BRANCH}/index.html
|
||||
git add -A ${ROOT_FOLDER}/${CURRENT_BRANCH}/index.html
|
||||
else
|
||||
cp -rf $f ${ROOT_FOLDER}/${CURRENT_BRANCH}
|
||||
git add -A ${ROOT_FOLDER}/${CURRENT_BRANCH}/$file
|
||||
fi
|
||||
fi
|
||||
done
|
||||
COMMIT_CHANGES="yes"
|
||||
else
|
||||
echo -e "Branch [${CURRENT_BRANCH}] is not on the white list! Check out the Maven [${WHITELIST_PROPERTY}] property in
|
||||
[docs] module available under [docs] profile. Won't commit any changes to gh-pages for this branch."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Copies the docs by using the explicitly provided version
|
||||
function copy_docs_for_provided_version() {
|
||||
local FOLDER=${DESTINATION_REPO_FOLDER}/${VERSION}
|
||||
mkdir -p ${FOLDER}
|
||||
echo -e "Current tag is [v${VERSION}] Will copy the current docs to the [${FOLDER}] folder"
|
||||
for f in ${ROOT_FOLDER}/docs/target/generated-docs/*; do
|
||||
file=${f#${ROOT_FOLDER}/docs/target/generated-docs/*}
|
||||
copy_docs_for_branch ${file} ${FOLDER}
|
||||
done
|
||||
COMMIT_CHANGES="yes"
|
||||
CURRENT_BRANCH="v${VERSION}"
|
||||
}
|
||||
|
||||
# Copies the docs from target to the provided destination
|
||||
# Params:
|
||||
# $1 - file from target
|
||||
# $2 - destination to which copy the files
|
||||
function copy_docs_for_branch() {
|
||||
local file=$1
|
||||
local destination=$2
|
||||
if ! git ls-files -i -o --exclude-standard --directory | grep -q ^${file}$; then
|
||||
# Not ignored...
|
||||
# We want users to access 1.0.0.RELEASE/ instead of 1.0.0.RELEASE/spring-cloud.sleuth.html
|
||||
if [[ ("${file}" == "${MAIN_ADOC_VALUE}.html") || ("${file}" == "${REPO_NAME}.html") ]] ; then
|
||||
# We don't want to copy the spring-cloud-sleuth.html
|
||||
# we want it to be converted to index.html
|
||||
cp -rf $f ${destination}/index.html
|
||||
git add -A ${destination}/index.html
|
||||
else
|
||||
cp -rf $f ${destination}
|
||||
git add -A ${destination}/$file
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function commit_changes_if_applicable() {
|
||||
if [[ "${COMMIT_CHANGES}" == "yes" ]] ; then
|
||||
COMMIT_SUCCESSFUL="no"
|
||||
git commit -a -m "Sync docs from ${CURRENT_BRANCH} to gh-pages" && COMMIT_SUCCESSFUL="yes" || echo "Failed to commit changes"
|
||||
|
||||
# Uncomment the following push if you want to auto push to
|
||||
# the gh-pages branch whenever you commit to master locally.
|
||||
# This is a little extreme. Use with care!
|
||||
###################################################################
|
||||
if [[ "${COMMIT_SUCCESSFUL}" == "yes" ]] ; then
|
||||
git push origin gh-pages
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Switch back to the previous branch and exit block
|
||||
function checkout_previous_branch() {
|
||||
# If -version was provided we need to come back to root project
|
||||
cd ${ROOT_FOLDER}
|
||||
git checkout ${CURRENT_BRANCH} || echo "Failed to check the branch... continuing with the script"
|
||||
if [ "$dirty" != "0" ]; then git stash pop; fi
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Assert if properties have been properly passed
|
||||
function assert_properties() {
|
||||
echo "VERSION [${VERSION}], DESTINATION [${DESTINATION}], CLONE [${CLONE}]"
|
||||
if [[ "${VERSION}" != "" && (-z "${DESTINATION}" && -z "${CLONE}") ]] ; then echo "Version was set but destination / clone was not!"; exit 1;fi
|
||||
if [[ ("${DESTINATION}" != "" && "${CLONE}" != "") && -z "${VERSION}" ]] ; then echo "Destination / clone was set but version was not!"; exit 1;fi
|
||||
if [[ "${DESTINATION}" != "" && "${CLONE}" == "yes" ]] ; then echo "Destination and clone was set. Pick one!"; exit 1;fi
|
||||
}
|
||||
|
||||
# Prints the usage
|
||||
function print_usage() {
|
||||
cat <<EOF
|
||||
The idea of this script is to update gh-pages branch with the generated docs. Without any options
|
||||
the script will work in the following manner:
|
||||
|
||||
- if there's no gh-pages / target for docs module then the script ends
|
||||
- for master branch the generated docs are copied to the root of gh-pages branch
|
||||
- for any other branch (if that branch is whitelisted) a subfolder with branch name is created
|
||||
and docs are copied there
|
||||
- if the version switch is passed (-v) then a tag with (v) prefix will be retrieved and a folder
|
||||
with that version number will be created in the gh-pages branch. WARNING! No whitelist verification will take place
|
||||
- if the destination switch is passed (-d) then the script will check if the provided dir is a git repo and then will
|
||||
switch to gh-pages of that repo and copy the generated docs to `docs/<project-name>/<version>`
|
||||
- if the destination switch is passed (-d) then the script will check if the provided dir is a git repo and then will
|
||||
switch to gh-pages of that repo and copy the generated docs to `docs/<project-name>/<version>`
|
||||
|
||||
USAGE:
|
||||
|
||||
You can use the following options:
|
||||
|
||||
-v|--version - the script will apply the whole procedure for a particular library version
|
||||
-d|--destination - the root of destination folder where the docs should be copied. You have to use the full path.
|
||||
E.g. point to spring-cloud-static folder. Can't be used with (-c)
|
||||
-b|--build - will run the standard build process after checking out the branch
|
||||
-c|--clone - will automatically clone the spring-cloud-static repo instead of providing the destination.
|
||||
Obviously can't be used with (-d)
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
# ==========================================
|
||||
# ____ ____ _____ _____ _____ _______
|
||||
# / ____|/ ____| __ \|_ _| __ \__ __|
|
||||
# | (___ | | | |__) | | | | |__) | | |
|
||||
# \___ \| | | _ / | | | ___/ | |
|
||||
# ____) | |____| | \ \ _| |_| | | |
|
||||
# |_____/ \_____|_| \_\_____|_| |_|
|
||||
#
|
||||
# ==========================================
|
||||
|
||||
while [[ $# > 0 ]]
|
||||
do
|
||||
key="$1"
|
||||
case ${key} in
|
||||
-v|--version)
|
||||
VERSION="$2"
|
||||
shift # past argument
|
||||
;;
|
||||
-d|--destination)
|
||||
DESTINATION="$2"
|
||||
shift # past argument
|
||||
;;
|
||||
-b|--build)
|
||||
BUILD="yes"
|
||||
;;
|
||||
-c|--clone)
|
||||
CLONE="yes"
|
||||
;;
|
||||
-h|--help)
|
||||
print_usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Invalid option: [$1]"
|
||||
print_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift # past argument or value
|
||||
done
|
||||
|
||||
git commit -a -m "Sync docs from master to gh-pages"
|
||||
|
||||
# Uncomment the following push if you want to auto push to
|
||||
# the gh-pages branch whenever you commit to master locally.
|
||||
# This is a little extreme. Use with care!
|
||||
###################################################################
|
||||
git push origin gh-pages
|
||||
|
||||
# Finally, switch back to the master branch and exit block
|
||||
git checkout master
|
||||
if [ "$dirty" != "0" ]; then git stash pop; fi
|
||||
|
||||
exit 0
|
||||
assert_properties
|
||||
set_default_props
|
||||
check_if_anything_to_sync
|
||||
if [[ -z "${VERSION}" ]] ; then
|
||||
retrieve_current_branch
|
||||
else
|
||||
switch_to_tag
|
||||
fi
|
||||
build_docs_if_applicable
|
||||
retrieve_doc_properties
|
||||
stash_changes
|
||||
add_docs_from_target
|
||||
checkout_previous_branch
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
File diff suppressed because it is too large
Load Diff
@@ -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 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
|
||||
@@ -0,0 +1,253 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Maven2 Start Up Batch script
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
# JAVA_HOME - location of a JDK home dir
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# M2_HOME - location of maven2's installed home dir
|
||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
# e.g. to debug Maven itself, use
|
||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||
|
||||
if [ -f /etc/mavenrc ] ; then
|
||||
. /etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.mavenrc" ] ; then
|
||||
. "$HOME/.mavenrc"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# OS specific support. $var _must_ be set to either true or false.
|
||||
cygwin=false;
|
||||
darwin=false;
|
||||
mingw=false
|
||||
case "`uname`" in
|
||||
CYGWIN*) cygwin=true ;;
|
||||
MINGW*) mingw=true;;
|
||||
Darwin*) darwin=true
|
||||
#
|
||||
# Look for the Apple JDKs first to preserve the existing behaviour, and then look
|
||||
# for the new JDKs provided by Oracle.
|
||||
#
|
||||
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
|
||||
#
|
||||
# Apple JDKs
|
||||
#
|
||||
export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
|
||||
#
|
||||
# Apple JDKs
|
||||
#
|
||||
export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
|
||||
#
|
||||
# Oracle JDKs
|
||||
#
|
||||
export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
|
||||
#
|
||||
# Apple JDKs
|
||||
#
|
||||
export JAVA_HOME=`/usr/libexec/java_home`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
if [ -r /etc/gentoo-release ] ; then
|
||||
JAVA_HOME=`java-config --jre-home`
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$M2_HOME" ] ; then
|
||||
## resolve links - $0 may be a link to maven's home
|
||||
PRG="$0"
|
||||
|
||||
# need this for relative symlinks
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG="`dirname "$PRG"`/$link"
|
||||
fi
|
||||
done
|
||||
|
||||
saveddir=`pwd`
|
||||
|
||||
M2_HOME=`dirname "$PRG"`/..
|
||||
|
||||
# make it fully qualified
|
||||
M2_HOME=`cd "$M2_HOME" && pwd`
|
||||
|
||||
cd "$saveddir"
|
||||
# echo Using m2 at $M2_HOME
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --unix "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
|
||||
fi
|
||||
|
||||
# For Migwn, ensure paths are in UNIX format before anything is touched
|
||||
if $mingw ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME="`(cd "$M2_HOME"; pwd)`"
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
|
||||
# TODO classpath?
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
javaExecutable="`which javac`"
|
||||
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
|
||||
# readlink(1) is not available as standard on Solaris 10.
|
||||
readLink=`which readlink`
|
||||
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
|
||||
if $darwin ; then
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
|
||||
else
|
||||
javaExecutable="`readlink -f \"$javaExecutable\"`"
|
||||
fi
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
|
||||
JAVA_HOME="$javaHome"
|
||||
export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$JAVACMD" ] ; then
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
else
|
||||
JAVACMD="`which java`"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||
echo " We cannot execute $JAVACMD" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
echo "Warning: JAVA_HOME environment variable is not set."
|
||||
fi
|
||||
|
||||
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --path --windows "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
|
||||
fi
|
||||
|
||||
# traverses directory structure from process work directory to filesystem root
|
||||
# first directory with .mvn subdirectory is considered project base directory
|
||||
find_maven_basedir() {
|
||||
local basedir=$(pwd)
|
||||
local wdir=$(pwd)
|
||||
while [ "$wdir" != '/' ] ; do
|
||||
if [ -d "$wdir"/.mvn ] ; then
|
||||
basedir=$wdir
|
||||
break
|
||||
fi
|
||||
wdir=$(cd "$wdir/.."; pwd)
|
||||
done
|
||||
echo "${basedir}"
|
||||
}
|
||||
|
||||
# concatenates all lines of a file
|
||||
concat_lines() {
|
||||
if [ -f "$1" ]; then
|
||||
echo "$(tr -s '\n' ' ' < "$1")"
|
||||
fi
|
||||
}
|
||||
|
||||
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)}
|
||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||
|
||||
# Provide a "standardized" way to retrieve the CLI args that will
|
||||
# work with both Windows and non-Windows executions.
|
||||
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
|
||||
export MAVEN_CMD_LINE_ARGS
|
||||
|
||||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
echo "Running version check"
|
||||
VERSION=$( sed '\!<parent!,\!</parent!d' `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} "$@"
|
||||
@@ -0,0 +1,145 @@
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM https://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Maven2 Start Up Batch script
|
||||
@REM
|
||||
@REM Required ENV vars:
|
||||
@REM JAVA_HOME - location of a JDK home dir
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM M2_HOME - location of maven2's installed home dir
|
||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
|
||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
@REM e.g. to debug Maven itself, use
|
||||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||
@echo off
|
||||
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
|
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||
|
||||
@REM set %HOME% to equivalent of $HOME
|
||||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||
|
||||
@REM Execute a user defined script before this one
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
|
||||
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
|
||||
:skipRcPre
|
||||
|
||||
@setlocal
|
||||
|
||||
set ERROR_CODE=0
|
||||
|
||||
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||
@setlocal
|
||||
|
||||
@REM ==== START VALIDATION ====
|
||||
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME not found in your environment. >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
:OkJHome
|
||||
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
@REM ==== END VALIDATION ====
|
||||
|
||||
:init
|
||||
|
||||
set MAVEN_CMD_LINE_ARGS=%*
|
||||
|
||||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||
@REM Fallback to current working directory if not found.
|
||||
|
||||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||
|
||||
set EXEC_DIR=%CD%
|
||||
set WDIR=%EXEC_DIR%
|
||||
:findBaseDir
|
||||
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||
cd ..
|
||||
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||
set WDIR=%CD%
|
||||
goto findBaseDir
|
||||
|
||||
:baseDirFound
|
||||
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||
cd "%EXEC_DIR%"
|
||||
goto endDetectBaseDir
|
||||
|
||||
:baseDirNotFound
|
||||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||
cd "%EXEC_DIR%"
|
||||
|
||||
:endDetectBaseDir
|
||||
|
||||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||
|
||||
@setlocal EnableExtensions EnableDelayedExpansion
|
||||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||
|
||||
:endReadAdditionalConfig
|
||||
|
||||
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||
|
||||
set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar""
|
||||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
|
||||
if ERRORLEVEL 1 goto error
|
||||
goto end
|
||||
|
||||
:error
|
||||
set ERROR_CODE=1
|
||||
|
||||
:end
|
||||
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
|
||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
|
||||
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
|
||||
:skipRcPost
|
||||
|
||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||
if "%MAVEN_BATCH_PAUSE%" == "on" pause
|
||||
|
||||
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
|
||||
|
||||
exit /B %ERROR_CODE%
|
||||
@@ -1,18 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-build</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<version>1.4.8.BUILD-SNAPSHOT</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.13.BUILD-SNAPSHOT</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>
|
||||
@@ -20,23 +20,32 @@
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
<properties>
|
||||
<bintray.package>netflix</bintray.package>
|
||||
<main.basedir>${basedir}</main.basedir>
|
||||
<archaius.version>0.6.5</archaius.version>
|
||||
<eureka.version>1.1.147</eureka.version>
|
||||
<feign.version>7.1.0</feign.version>
|
||||
<hystrix.version>1.4.0-RC6</hystrix.version>
|
||||
<ribbon.version>2.0-RC13</ribbon.version>
|
||||
<zuul.version>1.0.28</zuul.version>
|
||||
<netflix.rxjava.version>0.20.7</netflix.rxjava.version>
|
||||
<java.version>1.7</java.version>
|
||||
<spring-integration-dsl.version>1.0.0.RELEASE</spring-integration-dsl.version>
|
||||
<cloud-connectors.version>1.1.1.BUILD-SNAPSHOT</cloud-connectors.version>
|
||||
<netty.version>4.0.27.Final</netty.version>
|
||||
<jackson.version>2.7.3</jackson.version>
|
||||
<spring-cloud-commons.version>1.3.6.BUILD-SNAPSHOT</spring-cloud-commons.version>
|
||||
<spring-cloud-config.version>1.4.7.BUILD-SNAPSHOT</spring-cloud-config.version>
|
||||
<spring-cloud-stream.version>Ditmars.BUILD-SNAPSHOT</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>
|
||||
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
|
||||
<sonar.jacoco.reportPath>${project.basedir}/../target/jacoco.exec</sonar.jacoco.reportPath>
|
||||
<sonar.language>java</sonar.language>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<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,261 +72,194 @@
|
||||
</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>
|
||||
<version>${project.version}</version>
|
||||
<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>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
<version>${spring-cloud-commons.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-config-client</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<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-config-server</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<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-netflix-core</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<artifactId>spring-cloud-contract-dependencies</artifactId>
|
||||
<version>${donotreplacespring-cloud-contract.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-eureka-server</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-hystrix-dashboard</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-hystrix-amqp</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-turbine</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-turbine-amqp</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-zuul-server</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.archaius</groupId>
|
||||
<artifactId>archaius-core</artifactId>
|
||||
<version>${archaius.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<groupId>commons-logging</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.eureka</groupId>
|
||||
<artifactId>eureka-client</artifactId>
|
||||
<version>${eureka.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.eureka</groupId>
|
||||
<artifactId>eureka-core</artifactId>
|
||||
<version>${eureka.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<groupId>commons-logging</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>log4j</artifactId>
|
||||
<groupId>log4j</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>com.netflix.blitz4j</artifactId>
|
||||
<groupId>blitz4j</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- Eureka core dep that is now optional -->
|
||||
<dependency>
|
||||
<groupId>com.thoughtworks.xstream</groupId>
|
||||
<artifactId>xstream</artifactId>
|
||||
<version>1.4.2</version>
|
||||
</dependency>
|
||||
<!-- Eureka server dep that is now optional -->
|
||||
<dependency>
|
||||
<groupId>com.netflix.blitz4j</groupId>
|
||||
<artifactId>blitz4j</artifactId>
|
||||
<version>1.36.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.feign</groupId>
|
||||
<artifactId>feign-core</artifactId>
|
||||
<version>${feign.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.feign</groupId>
|
||||
<artifactId>feign-ribbon</artifactId>
|
||||
<version>${feign.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-loadbalancer</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- TODO: remove with feign 8.0.0 -->
|
||||
<dependency>
|
||||
<groupId>com.squareup.dagger</groupId>
|
||||
<artifactId>dagger</artifactId>
|
||||
<version>1.2.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.feign</groupId>
|
||||
<artifactId>feign-slf4j</artifactId>
|
||||
<version>${feign.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-core</artifactId>
|
||||
<version>${hystrix.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-metrics-event-stream</artifactId>
|
||||
<version>${hystrix.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-javanica</artifactId>
|
||||
<version>${hystrix.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon</artifactId>
|
||||
<version>${ribbon.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-core</artifactId>
|
||||
<version>${ribbon.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-httpclient</artifactId>
|
||||
<version>${ribbon.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-eureka</artifactId>
|
||||
<version>${ribbon.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.rxjava</groupId>
|
||||
<artifactId>rxjava-core</artifactId>
|
||||
<version>${netflix.rxjava.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.zuul</groupId>
|
||||
<artifactId>zuul-core</artifactId>
|
||||
<version>${zuul.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>groovy-all</artifactId>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<groupId>org.mockito</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-java-dsl</artifactId>
|
||||
<version>${spring-integration-dsl.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>18.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.jersey</groupId>
|
||||
<artifactId>jersey-servlet</artifactId>
|
||||
<version>1.13</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.jersey</groupId>
|
||||
<artifactId>jersey-core</artifactId>
|
||||
<version>1.13</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.jersey</groupId>
|
||||
<artifactId>jersey-client</artifactId>
|
||||
<version>1.13</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.jersey</groupId>
|
||||
<artifactId>jersey-server</artifactId>
|
||||
<version>1.13</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>d3js</artifactId>
|
||||
<version>3.4.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>jquery</artifactId>
|
||||
<version>2.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>bootstrap</artifactId>
|
||||
<version>3.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.inject</groupId>
|
||||
<artifactId>javax.inject</artifactId>
|
||||
<version>1</version>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-smile</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</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>
|
||||
<module>spring-cloud-netflix-hystrix-stream</module>
|
||||
<module>spring-cloud-netflix-eureka-client</module>
|
||||
<module>spring-cloud-netflix-eureka-server</module>
|
||||
<module>spring-cloud-netflix-spectator</module>
|
||||
<module>spring-cloud-netflix-turbine</module>
|
||||
<module>spring-cloud-netflix-turbine-amqp</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>
|
||||
<module>spring-cloud-starter-eureka-server</module>
|
||||
<module>spring-cloud-starter-feign</module>
|
||||
<module>spring-cloud-starter-hystrix</module>
|
||||
<module>spring-cloud-starter-hystrix-dashboard</module>
|
||||
<module>spring-cloud-starter-ribbon</module>
|
||||
<module>spring-cloud-starter-spectator</module>
|
||||
<module>spring-cloud-starter-turbine</module>
|
||||
<module>spring-cloud-starter-turbine-amqp</module>
|
||||
<module>spring-cloud-starter-turbine-stream</module>
|
||||
<module>spring-cloud-starter-zuul</module>
|
||||
<module>docs</module>
|
||||
</modules>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>spring</id>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-releases</id>
|
||||
<name>Spring Releases</name>
|
||||
<url>https://repo.spring.io/release</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>spring-releases</id>
|
||||
<name>Spring Releases</name>
|
||||
<url>https://repo.spring.io/libs-release-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>sonar</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<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>
|
||||
<configuration>
|
||||
<!-- Sets the VM argument line used when unit tests are run. -->
|
||||
<argLine>${surefireArgLine}</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
(cd spring-cloud-netflix-hystrix-contract && ../mvnw clean install -B -Pdocs ${@})
|
||||
./mvnw clean install -B -Pdocs ${@}
|
||||
Executable
+27
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
|
||||
mkdir -p target
|
||||
|
||||
SCRIPT_URL="https://raw.githubusercontent.com/spring-cloud-samples/brewery/master/runAcceptanceTests.sh"
|
||||
AT_WHAT_TO_TEST="EUREKA"
|
||||
|
||||
cd target
|
||||
|
||||
curl "${SCRIPT_URL}" --output runAcceptanceTests.sh
|
||||
|
||||
chmod +x runAcceptanceTests.sh
|
||||
|
||||
echo "Killing all running apps"
|
||||
./runAcceptanceTests.sh -t "${AT_WHAT_TO_TEST}" --killnow --branch edgware
|
||||
|
||||
./runAcceptanceTests.sh -t "${AT_WHAT_TO_TEST}" --killattheend --branch edgware
|
||||
|
||||
SCRIPT_URL="https://raw.githubusercontent.com/spring-cloud-samples/tests/master/scripts/runTests.sh"
|
||||
|
||||
curl "${SCRIPT_URL}" --output runIntegrationTests.sh
|
||||
|
||||
chmod +x runIntegrationTests.sh
|
||||
|
||||
./runIntegrationTests.sh
|
||||
@@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
<version>1.4.8.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
@@ -16,6 +16,11 @@
|
||||
<main.basedir>${basedir}/..</main.basedir>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot</artifactId>
|
||||
@@ -24,19 +29,38 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<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>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-config-client</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-config-server</artifactId>
|
||||
<artifactId>spring-cloud-context</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -44,46 +68,56 @@
|
||||
<artifactId>archaius-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- archaius dependencies that are now runtime -->
|
||||
<dependency>
|
||||
<groupId>com.netflix.eureka</groupId>
|
||||
<artifactId>eureka-client</artifactId>
|
||||
<groupId>commons-configuration</groupId>
|
||||
<artifactId>commons-configuration</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- end archaius deps -->
|
||||
<dependency>
|
||||
<groupId>com.netflix.servo</groupId>
|
||||
<artifactId>servo-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.eureka</groupId>
|
||||
<artifactId>eureka-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- Eureka core deps that are now optional -->
|
||||
<dependency>
|
||||
<groupId>com.thoughtworks.xstream</groupId>
|
||||
<artifactId>xstream</artifactId>
|
||||
<groupId>com.netflix.netflix-commons</groupId>
|
||||
<artifactId>netflix-commons-util</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.java.dev.rome</groupId>
|
||||
<artifactId>rome</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<groupId>javax.inject</groupId>
|
||||
<artifactId>javax.inject</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.feign</groupId>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-loadbalancer</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<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>
|
||||
<artifactId>feign-ribbon</artifactId>
|
||||
<groupId>io.github.openfeign</groupId>
|
||||
<artifactId>feign-httpclient</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.dagger</groupId>
|
||||
<artifactId>dagger</artifactId>
|
||||
<groupId>io.github.openfeign</groupId>
|
||||
<artifactId>feign-hystrix</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.openfeign</groupId>
|
||||
<artifactId>feign-okhttp</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -111,11 +145,6 @@
|
||||
<artifactId>ribbon-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-eureka</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-httpclient</artifactId>
|
||||
@@ -130,18 +159,25 @@
|
||||
<artifactId>groovy-all</artifactId>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<groupId>org.mockito</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.rxjava</groupId>
|
||||
<artifactId>rxjava-core</artifactId>
|
||||
<groupId>io.reactivex</groupId>
|
||||
<artifactId>rxjava</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<!-- Only needed at compile time -->
|
||||
<scope>compile</scope>
|
||||
<groupId>com.sun.jersey.contribs</groupId>
|
||||
<artifactId>jersey-apache-client4</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -149,5 +185,59 @@
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-smile</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.googlecode.protobuf-java-format</groupId>
|
||||
<artifactId>protobuf-java-format</artifactId>
|
||||
<version>1.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
<version>3.4.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>java8plus</id>
|
||||
<activation>
|
||||
<jdk>[1.8,2.0)</jdk>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<compilerArgs>
|
||||
<arg>-parameters</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
||||
+73
-32
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -17,35 +17,44 @@
|
||||
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 lombok.extern.apachecommons.CommonsLog;
|
||||
import com.netflix.config.AggregatedConfiguration;
|
||||
import com.netflix.config.ConcurrentCompositeConfiguration;
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.config.DeploymentContext;
|
||||
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 com.netflix.config.ConcurrentCompositeConfiguration;
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.config.DynamicPropertyFactory;
|
||||
import com.netflix.config.DynamicURLConfiguration;
|
||||
|
||||
import static com.netflix.config.ConfigurationBasedDeploymentContext.DEPLOYMENT_APPLICATION_ID_PROPERTY;
|
||||
import static com.netflix.config.ConfigurationManager.APPLICATION_PROPERTIES;
|
||||
import static com.netflix.config.ConfigurationManager.DISABLE_DEFAULT_ENV_CONFIG;
|
||||
import static com.netflix.config.ConfigurationManager.DISABLE_DEFAULT_SYS_CONFIG;
|
||||
@@ -57,21 +66,33 @@ import static com.netflix.config.ConfigurationManager.URL_CONFIG_NAME;
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ ConcurrentCompositeConfiguration.class, ConfigurationBuilder.class })
|
||||
@CommonsLog
|
||||
@ConditionalOnClass({ ConcurrentCompositeConfiguration.class,
|
||||
ConfigurationBuilder.class })
|
||||
@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(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);
|
||||
setStatic(DynamicPropertyFactory.class, "initializedWithDefaultConfig", false);
|
||||
setStatic(DynamicProperty.class, "dynamicPropertySupportImpl", null);
|
||||
initialized.compareAndSet(true, false);
|
||||
}
|
||||
|
||||
@@ -85,7 +106,8 @@ public class ArchaiusAutoConfiguration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(Endpoint.class)
|
||||
protected static class ArchaiusEndpointConfuguration {
|
||||
@ConditionalOnEnabledEndpoint("archaius")
|
||||
protected static class ArchaiusEndpointConfiguration {
|
||||
@Bean
|
||||
protected ArchaiusEndpoint archaiusEndpoint() {
|
||||
return new ArchaiusEndpoint();
|
||||
@@ -95,7 +117,8 @@ public class ArchaiusAutoConfiguration {
|
||||
@Configuration
|
||||
@ConditionalOnProperty(value = "archaius.propagate.environmentChangedEvent", matchIfMissing = true)
|
||||
@ConditionalOnClass(EnvironmentChangeEvent.class)
|
||||
protected static class PropagateEventsConfiguration implements ApplicationListener<EnvironmentChangeEvent> {
|
||||
protected static class PropagateEventsConfiguration
|
||||
implements ApplicationListener<EnvironmentChangeEvent> {
|
||||
@Autowired
|
||||
private Environment env;
|
||||
|
||||
@@ -103,20 +126,20 @@ public class ArchaiusAutoConfiguration {
|
||||
public void onApplicationEvent(EnvironmentChangeEvent event) {
|
||||
AbstractConfiguration manager = ConfigurationManager.getConfigInstance();
|
||||
for (String key : event.getKeys()) {
|
||||
for (ConfigurationListener listener : manager.getConfigurationListeners()) {
|
||||
for (ConfigurationListener listener : manager
|
||||
.getConfigurationListeners()) {
|
||||
Object source = event.getSource();
|
||||
// TODO: Handle add vs set vs delete?
|
||||
int type = AbstractConfiguration.EVENT_SET_PROPERTY;
|
||||
String value = env.getProperty(key);
|
||||
String value = this.env.getProperty(key);
|
||||
boolean beforeUpdate = false;
|
||||
listener.configurationChanged(new ConfigurationEvent(source,
|
||||
type, key, value, beforeUpdate));
|
||||
listener.configurationChanged(new ConfigurationEvent(source, type,
|
||||
key, value, beforeUpdate));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void configureArchaius(ConfigurableEnvironmentConfiguration envConfig) {
|
||||
if (initialized.compareAndSet(false, true)) {
|
||||
String appName = this.env.getProperty("spring.application.name");
|
||||
@@ -124,25 +147,21 @@ public class ArchaiusAutoConfiguration {
|
||||
appName = "application";
|
||||
log.warn("No spring.application.name found, defaulting to 'application'");
|
||||
}
|
||||
// this is deprecated, but currently it seams the only way to set it initially
|
||||
System.setProperty(DEPLOYMENT_APPLICATION_ID_PROPERTY, appName);
|
||||
|
||||
// TODO: support for other DeploymentContexts
|
||||
System.setProperty(DeploymentContext.ContextKey.appId.getKey(), appName);
|
||||
|
||||
ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration();
|
||||
|
||||
// support to add other Configurations (Jdbc, DynamoDb, Zookeeper, jclouds,
|
||||
// etc...)
|
||||
/*
|
||||
* if (factories != null && !factories.isEmpty()) { for
|
||||
* (PropertiesSourceFactory factory: factories) {
|
||||
* config.addConfiguration(factory.getConfiguration(), factory.getName()); } }
|
||||
*/
|
||||
if (this.externalConfigurations != null) {
|
||||
for (AbstractConfiguration externalConfig : this.externalConfigurations) {
|
||||
config.addConfiguration(externalConfig);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
@@ -162,14 +181,36 @@ public class ArchaiusAutoConfiguration {
|
||||
|
||||
ConcurrentCompositeConfiguration appOverrideConfig = new ConcurrentCompositeConfiguration();
|
||||
config.addConfiguration(appOverrideConfig, APPLICATION_PROPERTIES);
|
||||
config.setContainerConfigurationIndex(config
|
||||
.getIndexOfConfiguration(appOverrideConfig));
|
||||
config.setContainerConfigurationIndex(
|
||||
config.getIndexOfConfiguration(appOverrideConfig));
|
||||
|
||||
ConfigurationManager.install(config);
|
||||
addArchaiusConfiguration(config);
|
||||
}
|
||||
else {
|
||||
// TODO: reinstall ConfigurationManager
|
||||
log.warn("Netflix ConfigurationManager has already been installed, unable to re-install");
|
||||
log.warn(
|
||||
"Netflix ConfigurationManager has already been installed, unable to re-install");
|
||||
}
|
||||
}
|
||||
|
||||
private void addArchaiusConfiguration(ConcurrentCompositeConfiguration config) {
|
||||
if (ConfigurationManager.isConfigurationInstalled()) {
|
||||
AbstractConfiguration installedConfiguration = ConfigurationManager
|
||||
.getConfigInstance();
|
||||
if (installedConfiguration instanceof ConcurrentCompositeConfiguration) {
|
||||
ConcurrentCompositeConfiguration configInstance = (ConcurrentCompositeConfiguration) installedConfiguration;
|
||||
configInstance.addConfiguration(config);
|
||||
}
|
||||
else {
|
||||
installedConfiguration.append(config);
|
||||
if (!(installedConfiguration instanceof AggregatedConfiguration)) {
|
||||
log.warn(
|
||||
"Appending a configuration to an existing non-aggregated installed configuration will have no effect");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
ConfigurationManager.install(config);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
-117
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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.config;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
|
||||
import org.springframework.cloud.client.discovery.event.HeartbeatMonitor;
|
||||
import org.springframework.cloud.config.client.ConfigClientProperties;
|
||||
import org.springframework.cloud.config.client.ConfigServicePropertySourceLocator;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.event.SmartApplicationListener;
|
||||
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.discovery.DiscoveryClient;
|
||||
|
||||
/**
|
||||
* Bootstrap configuration for a config client that wants to lookup the config server via
|
||||
* discovery.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@ConditionalOnClass({ DiscoveryClient.class, ConfigServicePropertySourceLocator.class })
|
||||
@ConditionalOnExpression("${spring.cloud.config.discovery.enabled:false}")
|
||||
@Configuration
|
||||
@EnableDiscoveryClient
|
||||
@Import(EurekaClientAutoConfiguration.class)
|
||||
@CommonsLog
|
||||
public class DiscoveryClientConfigServiceBootstrapConfiguration implements
|
||||
SmartApplicationListener {
|
||||
|
||||
private HeartbeatMonitor monitor = new HeartbeatMonitor();
|
||||
|
||||
@Autowired
|
||||
private DiscoveryClient client;
|
||||
|
||||
@Autowired
|
||||
private ConfigClientProperties config;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
if (event instanceof ContextRefreshedEvent) {
|
||||
refresh();
|
||||
}
|
||||
else if (event instanceof HeartbeatEvent) {
|
||||
if (this.monitor.update(((HeartbeatEvent) event).getValue())) {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
|
||||
return ContextRefreshedEvent.class.isAssignableFrom(eventType)
|
||||
|| HeartbeatEvent.class.isAssignableFrom(eventType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSourceType(Class<?> sourceType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
try {
|
||||
log.info("Locating configserver via discovery");
|
||||
InstanceInfo server = this.client.getNextServerFromEureka(this.config
|
||||
.getDiscovery().getServiceId(), false);
|
||||
String url = server.getHomePageUrl();
|
||||
if (server.getMetadata().containsKey("password")) {
|
||||
String user = server.getMetadata().get("user");
|
||||
user = user == null ? "user" : user;
|
||||
this.config.setUsername(user);
|
||||
String password = server.getMetadata().get("password");
|
||||
this.config.setPassword(password);
|
||||
}
|
||||
if (server.getMetadata().containsKey("configPath")) {
|
||||
String path = server.getMetadata().get("configPath");
|
||||
if (url.endsWith("/") && path.startsWith("/")) {
|
||||
url = url.substring(0, url.length() - 1);
|
||||
}
|
||||
url = url + path;
|
||||
}
|
||||
this.config.setUri(url);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
log.warn("Could not locate configserver via discovery", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+7
-5
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -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;
|
||||
|
||||
-208
@@ -1,208 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import com.netflix.appinfo.DataCenterInfo;
|
||||
import com.netflix.appinfo.DataCenterInfo.Name;
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.appinfo.UniqueIdentifier;
|
||||
import com.netflix.discovery.converters.Converters.ApplicationsConverter;
|
||||
import com.netflix.discovery.converters.Converters.InstanceInfoConverter;
|
||||
import com.netflix.discovery.shared.Applications;
|
||||
import com.thoughtworks.xstream.MarshallingStrategy;
|
||||
import com.thoughtworks.xstream.converters.Converter;
|
||||
import com.thoughtworks.xstream.converters.ConverterLookup;
|
||||
import com.thoughtworks.xstream.converters.DataHolder;
|
||||
import com.thoughtworks.xstream.converters.MarshallingContext;
|
||||
import com.thoughtworks.xstream.converters.UnmarshallingContext;
|
||||
import com.thoughtworks.xstream.core.TreeMarshallingStrategy;
|
||||
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
|
||||
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
|
||||
import com.thoughtworks.xstream.mapper.Mapper;
|
||||
|
||||
/**
|
||||
* A special purpose wrapper for an XStream TreeMarshallingStrategy that is aware of the
|
||||
* {@link InstanceInfo} type and can create a more useful {@link DataCenterInfo} for it
|
||||
* after unmarshalling. If the InstanceInfo has a metadataMap containing an
|
||||
* <code>instanceId</code>, and the DataCenterInfo is not already an AmazonInfo, then the
|
||||
* instanceId is used to create an identifier, but appending it to the existing one. This
|
||||
* is useful when not running Eureka in bare EC2 VMs, so the EC2 metadata is not available
|
||||
* for uniquely identifying the InstanceInfo (the default is to just use the hostname, but
|
||||
* that isn't very useful when sitting behind a proxy).
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class DataCenterAwareMarshallingStrategy implements MarshallingStrategy {
|
||||
|
||||
private TreeMarshallingStrategy delegate = new TreeMarshallingStrategy();
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
public DataCenterAwareMarshallingStrategy(ApplicationContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object unmarshal(Object root, HierarchicalStreamReader reader,
|
||||
DataHolder dataHolder, ConverterLookup converterLookup, Mapper mapper) {
|
||||
ConverterLookup wrapped = new DataCenterAwareConverterLookup(converterLookup,
|
||||
this.context);
|
||||
return this.delegate.unmarshal(root, reader, dataHolder, wrapped, mapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void marshal(HierarchicalStreamWriter writer, Object obj,
|
||||
ConverterLookup converterLookup, Mapper mapper, DataHolder dataHolder) {
|
||||
ConverterLookup wrapped = new DataCenterAwareConverterLookup(converterLookup,
|
||||
this.context);
|
||||
this.delegate.marshal(writer, obj, wrapped, mapper, dataHolder);
|
||||
}
|
||||
|
||||
public static class InstanceIdDataCenterInfo implements DataCenterInfo,
|
||||
UniqueIdentifier {
|
||||
|
||||
private String instanceId;
|
||||
|
||||
public InstanceIdDataCenterInfo(String instanceId) {
|
||||
this.instanceId = instanceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Name getName() {
|
||||
return Name.MyOwn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.instanceId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class DataCenterAwareConverterLookup implements ConverterLookup {
|
||||
|
||||
private ConverterLookup delegate;
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
public DataCenterAwareConverterLookup(ConverterLookup delegate,
|
||||
ApplicationContext context) {
|
||||
this.delegate = delegate;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Converter lookupConverterForType(@SuppressWarnings("rawtypes") Class type) {
|
||||
Converter converter = this.delegate.lookupConverterForType(type);
|
||||
if (InstanceInfo.class == type) {
|
||||
return new DataCenterAwareConverter();
|
||||
}
|
||||
else if (Applications.class == type) {
|
||||
return new PublishingApplicationsConverter(this.context);
|
||||
}
|
||||
return converter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class PublishingApplicationsConverter extends ApplicationsConverter {
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
public PublishingApplicationsConverter(ApplicationContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object unmarshal(HierarchicalStreamReader reader,
|
||||
UnmarshallingContext unmarshallingContext) {
|
||||
Object obj = super.unmarshal(reader, unmarshallingContext);
|
||||
|
||||
ProxyFactory factory = new ProxyFactory(obj);
|
||||
factory.addAdvice(new SetVersionInterceptor(this.context));
|
||||
return factory.getProxy();
|
||||
}
|
||||
}
|
||||
|
||||
@CommonsLog
|
||||
private static class SetVersionInterceptor implements MethodInterceptor {
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
public SetVersionInterceptor(ApplicationContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
Object ret = invocation.proceed();
|
||||
if ("setVersion".equals(invocation.getMethod().getName())) {
|
||||
Long version = Long.class.cast(invocation.getArguments()[0]);
|
||||
log.debug("Applications.setVersion() called with version: " + version);
|
||||
this.context.publishEvent(new HeartbeatEvent(invocation
|
||||
.getThis(), version));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class DataCenterAwareConverter extends InstanceInfoConverter {
|
||||
|
||||
@Override
|
||||
public void marshal(Object source, HierarchicalStreamWriter writer,
|
||||
MarshallingContext context) {
|
||||
InstanceInfo info = (InstanceInfo) source;
|
||||
String instanceId = info.getMetadata().get("instanceId");
|
||||
DataCenterInfo dataCenter = info.getDataCenterInfo();
|
||||
if (instanceId != null && Name.Amazon != dataCenter.getName()) {
|
||||
String old = info.getId();
|
||||
String id = old.endsWith(instanceId) ? old : old + ":" + instanceId;
|
||||
info = new InstanceInfo.Builder(info).setDataCenterInfo(
|
||||
new InstanceIdDataCenterInfo(id)).build();
|
||||
source = info;
|
||||
}
|
||||
super.marshal(source, writer, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object unmarshal(HierarchicalStreamReader reader,
|
||||
UnmarshallingContext context) {
|
||||
Object obj = super.unmarshal(reader, context);
|
||||
InstanceInfo info = (InstanceInfo) obj;
|
||||
String instanceId = info.getMetadata().get("instanceId");
|
||||
DataCenterInfo dataCenter = info.getDataCenterInfo();
|
||||
if (instanceId != null && Name.Amazon != dataCenter.getName()) {
|
||||
String old = info.getId();
|
||||
String id = old.endsWith(instanceId) ? old : old + ":" + instanceId;
|
||||
info = new InstanceInfo.Builder(info).setDataCenterInfo(
|
||||
new InstanceIdDataCenterInfo(id)).build();
|
||||
obj = info;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import com.netflix.appinfo.EurekaInstanceConfig;
|
||||
import com.netflix.discovery.DiscoveryManager;
|
||||
import com.netflix.discovery.EurekaClientConfig;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class DiscoveryManagerInitializer {
|
||||
|
||||
@Autowired
|
||||
private EurekaClientConfig clientConfig;
|
||||
|
||||
@Autowired
|
||||
private EurekaInstanceConfig instanceConfig;
|
||||
|
||||
public synchronized void init() {
|
||||
if (DiscoveryManager.getInstance().getDiscoveryClient() == null) {
|
||||
DiscoveryManager.getInstance().initComponent(this.instanceConfig,
|
||||
this.clientConfig);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-113
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
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.builder.ParentContextApplicationContextInitializer;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
|
||||
import org.springframework.cloud.client.discovery.event.ParentHeartbeatEvent;
|
||||
import org.springframework.cloud.client.discovery.noop.NoopDiscoveryClientAutoConfiguration;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.netflix.appinfo.EurekaInstanceConfig;
|
||||
import com.netflix.discovery.EurekaClientConfig;
|
||||
import com.netflix.discovery.converters.JsonXStream;
|
||||
import com.netflix.discovery.converters.XmlXStream;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
@ConditionalOnClass(EurekaClientConfig.class)
|
||||
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
|
||||
@AutoConfigureBefore(NoopDiscoveryClientAutoConfiguration.class)
|
||||
public class EurekaClientAutoConfiguration implements ApplicationListener<ParentContextApplicationContextInitializer.ParentContextAvailableEvent> {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private static final ConcurrentMap<String, String> listenerAdded = new ConcurrentHashMap<>();
|
||||
|
||||
@Value("${server.port:${SERVER_PORT:${PORT:8080}}}")
|
||||
int nonSecurePort;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
XmlXStream.getInstance().setMarshallingStrategy(
|
||||
new DataCenterAwareMarshallingStrategy(this.applicationContext));
|
||||
JsonXStream.getInstance().setMarshallingStrategy(
|
||||
new DataCenterAwareMarshallingStrategy(this.applicationContext));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(EurekaClientConfig.class)
|
||||
public EurekaClientConfigBean eurekaClientConfigBean() {
|
||||
return new EurekaClientConfigBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(EurekaInstanceConfig.class)
|
||||
public EurekaInstanceConfigBean eurekaInstanceConfigBean() {
|
||||
EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean();
|
||||
instance.setNonSecurePort(this.nonSecurePort);
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* propagate HeartbeatEvent from parent to child. Do it via a
|
||||
* ParentHeartbeatEvent since events get published to the parent context,
|
||||
* otherwise results in a stack overflow
|
||||
* @param event
|
||||
*/
|
||||
@Override
|
||||
public void onApplicationEvent(final ParentContextApplicationContextInitializer.ParentContextAvailableEvent event) {
|
||||
final ConfigurableApplicationContext context = event.getApplicationContext();
|
||||
String childId = context.getId();
|
||||
ApplicationContext parent = context.getParent();
|
||||
if (parent != null && "bootstrap".equals(parent.getId())
|
||||
&& parent instanceof ConfigurableApplicationContext) {
|
||||
if (listenerAdded.putIfAbsent(childId, childId) == null) {
|
||||
@SuppressWarnings("resource")
|
||||
ConfigurableApplicationContext ctx = (ConfigurableApplicationContext) parent;
|
||||
ctx.addApplicationListener(new ApplicationListener<HeartbeatEvent>() {
|
||||
@Override
|
||||
public void onApplicationEvent(HeartbeatEvent dhe) {
|
||||
context.publishEvent(new ParentHeartbeatEvent(dhe
|
||||
.getSource(), dhe.getValue()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-189
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import com.netflix.discovery.EurekaClientConfig;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties("eureka.client")
|
||||
public class EurekaClientConfigBean implements EurekaClientConfig {
|
||||
|
||||
public static final String DEFAULT_URL = "http://localhost:8761"
|
||||
+ EurekaServerConfigBean.DEFAULT_PREFIX + "/";
|
||||
|
||||
public static final String DEFAULT_ZONE = "defaultZone";
|
||||
|
||||
private static final int MINUTES = 60;
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
private int registryFetchIntervalSeconds = 30;
|
||||
|
||||
private int instanceInfoReplicationIntervalSeconds = 30;
|
||||
|
||||
private int initialInstanceInfoReplicationIntervalSeconds = 40;
|
||||
|
||||
private int eurekaServiceUrlPollIntervalSeconds = 5 * MINUTES;
|
||||
|
||||
private String proxyPort;
|
||||
|
||||
private String proxyHost;
|
||||
|
||||
private String proxyUserName;
|
||||
|
||||
private String proxyPassword;
|
||||
|
||||
private int eurekaServerReadTimeoutSeconds = 8;
|
||||
|
||||
private int eurekaServerConnectTimeoutSeconds = 5;
|
||||
|
||||
private String backupRegistryImpl;
|
||||
|
||||
private int eurekaServerTotalConnections = 200;
|
||||
|
||||
private int eurekaServerTotalConnectionsPerHost = 50;
|
||||
|
||||
private String eurekaServerURLContext;
|
||||
|
||||
private String eurekaServerPort;
|
||||
|
||||
private String eurekaServerDNSName;
|
||||
|
||||
private String region = "us-east-1";
|
||||
|
||||
private int eurekaConnectionIdleTimeoutSeconds = 30;
|
||||
|
||||
private String registryRefreshSingleVipAddress;
|
||||
|
||||
private int heartbeatExecutorThreadPoolSize = 2;
|
||||
|
||||
private int heartbeatExecutorExponentialBackOffBound = 10;
|
||||
|
||||
private int cacheRefreshExecutorThreadPoolSize = 2;
|
||||
|
||||
private int cacheRefreshExecutorExponentialBackOffBound = 10;
|
||||
|
||||
private Map<String, String> serviceUrl = new HashMap<String, String>();
|
||||
{
|
||||
this.serviceUrl.put(DEFAULT_ZONE, DEFAULT_URL);
|
||||
}
|
||||
|
||||
private boolean gZipContent = true;
|
||||
|
||||
private boolean useDnsForFetchingServiceUrls = false;
|
||||
|
||||
private boolean registerWithEureka = true;
|
||||
|
||||
private boolean preferSameZoneEureka = true;
|
||||
|
||||
private boolean logDeltaDiff;
|
||||
|
||||
private boolean disableDelta;
|
||||
|
||||
private String fetchRemoteRegionsRegistry;
|
||||
|
||||
private Map<String, String> availabilityZones = new HashMap<String, String>();
|
||||
|
||||
private boolean filterOnlyUpInstances = true;
|
||||
|
||||
private boolean fetchRegistry = true;
|
||||
|
||||
private String dollarReplacement = "_-";
|
||||
|
||||
private String escapeCharReplacement = "__";
|
||||
|
||||
@Override
|
||||
public boolean shouldGZipContent() {
|
||||
return this.gZipContent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldUseDnsForFetchingServiceUrls() {
|
||||
return this.useDnsForFetchingServiceUrls;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRegisterWithEureka() {
|
||||
return this.registerWithEureka;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldPreferSameZoneEureka() {
|
||||
return this.preferSameZoneEureka;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldLogDeltaDiff() {
|
||||
return this.logDeltaDiff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldDisableDelta() {
|
||||
return this.disableDelta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fetchRegistryForRemoteRegions() {
|
||||
return this.fetchRemoteRegionsRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getAvailabilityZones(String region) {
|
||||
String value = this.availabilityZones.get(region);
|
||||
if (value == null) {
|
||||
value = DEFAULT_ZONE;
|
||||
}
|
||||
return value.split(",");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getEurekaServerServiceUrls(String myZone) {
|
||||
String serviceUrls = this.serviceUrl.get(myZone);
|
||||
if (serviceUrls == null || serviceUrls.isEmpty()) {
|
||||
serviceUrls = this.serviceUrl.get(DEFAULT_ZONE);
|
||||
}
|
||||
if (serviceUrls != null) {
|
||||
return Arrays.asList(serviceUrls.split(","));
|
||||
}
|
||||
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldFilterOnlyUpInstances() {
|
||||
return this.filterOnlyUpInstances;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldFetchRegistry() {
|
||||
return this.fetchRegistry;
|
||||
}
|
||||
|
||||
}
|
||||
-221
@@ -1,221 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.actuate.metrics.reader.MetricReader;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
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.context.embedded.EmbeddedServletContainerInitializedEvent;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import com.netflix.appinfo.ApplicationInfoManager;
|
||||
import com.netflix.appinfo.EurekaInstanceConfig;
|
||||
import com.netflix.appinfo.HealthCheckHandler;
|
||||
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
|
||||
import com.netflix.discovery.DiscoveryManager;
|
||||
import com.netflix.discovery.EurekaClientConfig;
|
||||
import com.netflix.discovery.shared.EurekaJerseyClient;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
@ConditionalOnClass(EurekaClientConfig.class)
|
||||
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
|
||||
@CommonsLog
|
||||
public class EurekaDiscoveryClientConfiguration implements SmartLifecycle, Ordered {
|
||||
|
||||
private AtomicBoolean running = new AtomicBoolean(false);
|
||||
|
||||
private int order = 0;
|
||||
|
||||
private AtomicInteger port = new AtomicInteger(0);
|
||||
|
||||
@Autowired
|
||||
private EurekaInstanceConfigBean instanceConfig;
|
||||
|
||||
@Autowired(required = false)
|
||||
private HealthCheckHandler healthCheckHandler;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
@PreDestroy
|
||||
public void close() {
|
||||
closeDiscoveryClientJersey();
|
||||
log.info("Removing application " + this.instanceConfig.getAppname()
|
||||
+ " from eureka");
|
||||
DiscoveryManager.getInstance().shutdownComponent();
|
||||
}
|
||||
|
||||
private void closeDiscoveryClientJersey() {
|
||||
log.info("Closing DiscoveryClient.jerseyClient");
|
||||
Field jerseyClientField = ReflectionUtils.findField(
|
||||
com.netflix.discovery.DiscoveryClient.class, "discoveryJerseyClient",
|
||||
EurekaJerseyClient.JerseyClient.class);
|
||||
if (jerseyClientField != null) {
|
||||
try {
|
||||
jerseyClientField.setAccessible(true);
|
||||
if (DiscoveryManager.getInstance() != null &&
|
||||
DiscoveryManager.getInstance().getDiscoveryClient() != null) {
|
||||
Object obj = jerseyClientField.get(DiscoveryManager.getInstance()
|
||||
.getDiscoveryClient());
|
||||
if (obj != null) {
|
||||
EurekaJerseyClient.JerseyClient jerseyClient = (EurekaJerseyClient.JerseyClient) obj;
|
||||
jerseyClient.destroyResources();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
log.error("Error closing DiscoveryClient.jerseyClient", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
// only set the port if the nonSecurePort is 0 and this.port != 0
|
||||
if (this.port.get() != 0 && this.instanceConfig.getNonSecurePort() == 0) {
|
||||
this.instanceConfig.setNonSecurePort(this.port.get());
|
||||
}
|
||||
// only initialize if nonSecurePort is greater than 0 and it isn't already running
|
||||
// because of containerPortInitializer below
|
||||
if (!this.running.get() && this.instanceConfig.getNonSecurePort() > 0) {
|
||||
discoveryManagerIntitializer().init();
|
||||
|
||||
log.info("Registering application " + this.instanceConfig.getAppname()
|
||||
+ " with eureka with status "
|
||||
+ this.instanceConfig.getInitialStatus());
|
||||
|
||||
ApplicationInfoManager.getInstance().setInstanceStatus(
|
||||
this.instanceConfig.getInitialStatus());
|
||||
|
||||
if (this.healthCheckHandler != null) {
|
||||
DiscoveryManager.getInstance().getDiscoveryClient()
|
||||
.registerHealthCheck(this.healthCheckHandler);
|
||||
}
|
||||
this.context.publishEvent(new InstanceRegisteredEvent<>(this,
|
||||
this.instanceConfig));
|
||||
this.running.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
log.info("Unregistering application " + this.instanceConfig.getAppname()
|
||||
+ " with eureka with status OUT_OF_SERVICE");
|
||||
ApplicationInfoManager.getInstance().setInstanceStatus(
|
||||
InstanceStatus.OUT_OF_SERVICE);
|
||||
this.running.set(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPhase() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoStartup() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(Runnable callback) {
|
||||
callback.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(DiscoveryManagerInitializer.class)
|
||||
public DiscoveryManagerInitializer discoveryManagerIntitializer() {
|
||||
return new DiscoveryManagerInitializer();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Lazy
|
||||
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
|
||||
@ConditionalOnMissingBean(com.netflix.discovery.DiscoveryClient.class)
|
||||
public com.netflix.discovery.DiscoveryClient eurekaDiscoveryClient() {
|
||||
return DiscoveryManager.getInstance().getDiscoveryClient();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DiscoveryClient discoveryClient() {
|
||||
return new EurekaDiscoveryClient();
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected ApplicationListener<EmbeddedServletContainerInitializedEvent> containerPortInitializer() {
|
||||
return new ApplicationListener<EmbeddedServletContainerInitializedEvent>() {
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) {
|
||||
// TODO: take SSL into account when Spring Boot 1.2 is available
|
||||
EurekaDiscoveryClientConfiguration.this.port.compareAndSet(0, event
|
||||
.getEmbeddedServletContainer().getPort());
|
||||
EurekaDiscoveryClientConfiguration.this.start();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(Endpoint.class)
|
||||
@ConditionalOnBean(MetricReader.class)
|
||||
protected static class EurekaHealthIndicatorConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public EurekaHealthIndicator eurekaHealthIndicator(
|
||||
com.netflix.discovery.DiscoveryClient eurekaDiscoveryClient,
|
||||
MetricReader metrics, EurekaInstanceConfig config) {
|
||||
return new EurekaHealthIndicator(eurekaDiscoveryClient, metrics, config);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-153
@@ -1,153 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import com.netflix.appinfo.DataCenterInfo;
|
||||
import com.netflix.appinfo.EurekaInstanceConfig;
|
||||
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
|
||||
import com.netflix.appinfo.UniqueIdentifier;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties("eureka.instance")
|
||||
public class EurekaInstanceConfigBean implements EurekaInstanceConfig {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(EurekaInstanceConfigBean.class);
|
||||
|
||||
@Getter(AccessLevel.PRIVATE)
|
||||
@Setter(AccessLevel.PRIVATE)
|
||||
private String[] hostInfo = initHostInfo();
|
||||
|
||||
@Value("${spring.application.name:unknown}")
|
||||
private String appname = "unknown";
|
||||
|
||||
private String appGroupName;
|
||||
|
||||
private boolean instanceEnabledOnit;
|
||||
|
||||
private int nonSecurePort = 80;
|
||||
|
||||
private int securePort = 443;
|
||||
|
||||
private boolean nonSecurePortEnabled = true;
|
||||
|
||||
private boolean securePortEnabled;
|
||||
|
||||
private int leaseRenewalIntervalInSeconds = 30;
|
||||
|
||||
private int leaseExpirationDurationInSeconds = 90;
|
||||
|
||||
@Value("${spring.application.name:unknown}")
|
||||
private String virtualHostName;
|
||||
|
||||
private String secureVirtualHostName;
|
||||
|
||||
private String aSGName;
|
||||
|
||||
private Map<String, String> metadataMap = new HashMap<>();
|
||||
|
||||
private DataCenterInfo dataCenterInfo = new IdentifyingDataCenterInfo();
|
||||
|
||||
private String ipAddress = this.hostInfo[0];
|
||||
|
||||
private String statusPageUrlPath = "/info";
|
||||
|
||||
private String statusPageUrl;
|
||||
|
||||
private String homePageUrlPath = "/";
|
||||
|
||||
private String homePageUrl;
|
||||
|
||||
private String healthCheckUrlPath = "/health";
|
||||
|
||||
private String healthCheckUrl;
|
||||
|
||||
private String secureHealthCheckUrl;
|
||||
|
||||
private String namespace = "eureka";
|
||||
|
||||
private String hostname = this.hostInfo[1];
|
||||
|
||||
private boolean preferIpAddress = false;
|
||||
|
||||
private InstanceStatus initialStatus = InstanceStatus.UP;
|
||||
|
||||
public String getHostname() {
|
||||
return this.preferIpAddress ? this.ipAddress : this.hostname;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getSecurePortEnabled() {
|
||||
return this.securePortEnabled;
|
||||
}
|
||||
|
||||
private String[] initHostInfo() {
|
||||
String[] info = new String[2];
|
||||
try {
|
||||
info[0] = InetAddress.getLocalHost().getHostAddress();
|
||||
info[1] = InetAddress.getLocalHost().getHostName();
|
||||
}
|
||||
catch (UnknownHostException ex) {
|
||||
logger.error("Cannot get host info", ex);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHostName(boolean refresh) {
|
||||
return this.preferIpAddress ? this.ipAddress : this.hostname;
|
||||
}
|
||||
|
||||
private final class IdentifyingDataCenterInfo implements DataCenterInfo,
|
||||
UniqueIdentifier {
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private Name name = Name.MyOwn;
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
String instanceId = EurekaInstanceConfigBean.this.metadataMap
|
||||
.get("instanceId");
|
||||
if (instanceId != null) {
|
||||
String old = getHostname();
|
||||
String id = old.endsWith(instanceId) ? old : old + ":" + instanceId;
|
||||
return id;
|
||||
}
|
||||
return getHostname();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-208
@@ -1,208 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import com.netflix.eureka.EurekaServerConfig;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties("eureka.server")
|
||||
public class EurekaServerConfigBean implements EurekaServerConfig {
|
||||
|
||||
public static final String DEFAULT_PREFIX = "/eureka";
|
||||
|
||||
private static final int MINUTES = 60 * 1000;
|
||||
|
||||
private String aWSAccessId;
|
||||
|
||||
private String aWSSecretKey;
|
||||
|
||||
private int eIPBindRebindRetries = 3;
|
||||
|
||||
private int eIPBindingRetryIntervalMs = 5 * MINUTES;
|
||||
|
||||
private boolean enableSelfPreservation = true;
|
||||
|
||||
private double renewalPercentThreshold = 0.85;
|
||||
|
||||
private int renewalThresholdUpdateIntervalMs = 10 * MINUTES;
|
||||
|
||||
private int peerEurekaNodesUpdateIntervalMs = 15 * MINUTES;
|
||||
|
||||
private int numberOfReplicationRetries = 5;
|
||||
|
||||
private int peerEurekaStatusRefreshTimeIntervalMs = 30 * 1000;
|
||||
|
||||
private int waitTimeInMsWhenSyncEmpty = 5 * MINUTES;
|
||||
|
||||
private int peerNodeConnectTimeoutMs = 200;
|
||||
|
||||
private int peerNodeReadTimeoutMs = 200;
|
||||
|
||||
private int peerNodeTotalConnections = 1000;
|
||||
|
||||
private int peerNodeTotalConnectionsPerHost = 500;
|
||||
|
||||
private int peerNodeConnectionIdleTimeoutSeconds = 30;
|
||||
|
||||
private long retentionTimeInMSInDeltaQueue = 3 * MINUTES;
|
||||
|
||||
private long deltaRetentionTimerIntervalInMs = 30 * 1000;
|
||||
|
||||
private long evictionIntervalTimerInMs = 60 * 1000;
|
||||
|
||||
private int aSGQueryTimeoutMs = 300;
|
||||
|
||||
private long aSGUpdateIntervalMs = 5 * MINUTES;
|
||||
|
||||
private long responseCacheAutoExpirationInSeconds = 180;
|
||||
|
||||
private long responseCacheUpdateIntervalMs = 30 * 1000;
|
||||
|
||||
private boolean disableDelta;
|
||||
|
||||
private long maxIdleThreadInMinutesAgeForStatusReplication = 10;
|
||||
|
||||
private int minThreadsForStatusReplication = 1;
|
||||
|
||||
private int maxThreadsForStatusReplication = 1;
|
||||
|
||||
private int maxElementsInStatusReplicationPool = 10000;
|
||||
|
||||
private boolean syncWhenTimestampDiffers = true;
|
||||
|
||||
private int registrySyncRetries = 5;
|
||||
|
||||
private int maxElementsInPeerReplicationPool = 10000;
|
||||
|
||||
private long maxIdleThreadAgeInMinutesForPeerReplication = 15;
|
||||
|
||||
private int minThreadsForPeerReplication = 5;
|
||||
|
||||
private int maxThreadsForPeerReplication = 20;
|
||||
|
||||
private int maxTimeForReplication = 30000;
|
||||
|
||||
private boolean primeAwsReplicaConnections = true;
|
||||
|
||||
private boolean disableDeltaForRemoteRegions;
|
||||
|
||||
private int remoteRegionConnectTimeoutMs = 1000;
|
||||
|
||||
private int remoteRegionReadTimeoutMs = 1000;
|
||||
|
||||
private int remoteRegionTotalConnections = 1000;
|
||||
|
||||
private int remoteRegionTotalConnectionsPerHost = 500;
|
||||
|
||||
private int remoteRegionConnectionIdleTimeoutSeconds = 30;
|
||||
|
||||
private boolean gZipContentFromRemoteRegion = true;
|
||||
|
||||
private Map<String, String> remoteRegionUrlsWithName = new HashMap<String, String>();
|
||||
|
||||
private String[] remoteRegionUrls;
|
||||
|
||||
private Map<String, Set<String>> remoteRegionAppWhitelist;
|
||||
|
||||
private int remoteRegionRegistryFetchInterval = 30;
|
||||
|
||||
private String remoteRegionTrustStore = "";
|
||||
|
||||
private String remoteRegionTrustStorePassword = "changeit";
|
||||
|
||||
private boolean disableTransparentFallbackToOtherRegion;
|
||||
|
||||
private boolean batchReplication;
|
||||
|
||||
private boolean rateLimiterEnabled = false;
|
||||
|
||||
private boolean rateLimiterThrottleStandardClients = false;
|
||||
|
||||
private Set<String> rateLimiterPrivilegedClients = Collections.emptySet();
|
||||
|
||||
private int rateLimiterBurstSize = 10;
|
||||
|
||||
private int rateLimiterRegistryFetchAverageRate = 500;
|
||||
|
||||
private int rateLimiterFullFetchAverageRate = 100;
|
||||
|
||||
private boolean logIdentityHeaders = true;
|
||||
|
||||
@Override
|
||||
public boolean shouldEnableSelfPreservation() {
|
||||
return this.enableSelfPreservation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldDisableDelta() {
|
||||
return this.disableDelta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldSyncWhenTimestampDiffers() {
|
||||
return this.syncWhenTimestampDiffers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldPrimeAwsReplicaConnections() {
|
||||
return this.primeAwsReplicaConnections;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldDisableDeltaForRemoteRegions() {
|
||||
return this.disableDeltaForRemoteRegions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldGZipContentFromRemoteRegion() {
|
||||
return this.gZipContentFromRemoteRegion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getRemoteRegionAppWhitelist(String regionName) {
|
||||
return this.remoteRegionAppWhitelist.get(regionName == null ? "global"
|
||||
: regionName.trim().toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean disableTransparentFallbackToOtherRegion() {
|
||||
return this.disableTransparentFallbackToOtherRegion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldBatchReplication() {
|
||||
return this.batchReplication;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldLogIdentityHeaders() {
|
||||
return this.logIdentityHeaders;
|
||||
}
|
||||
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
|
||||
import feign.MethodMetadata;
|
||||
|
||||
/**
|
||||
* Feign contract method parameter processor.
|
||||
*
|
||||
* @author Jakub Narloch
|
||||
* @author Abhijit Sarkar
|
||||
*/
|
||||
public interface AnnotatedParameterProcessor {
|
||||
|
||||
/**
|
||||
* Retrieves the processor supported annotation type.
|
||||
*
|
||||
* @return the annotation type
|
||||
*/
|
||||
Class<? extends Annotation> getAnnotationType();
|
||||
|
||||
/**
|
||||
* Process the annotated parameter.
|
||||
*
|
||||
* @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, Method method);
|
||||
|
||||
/**
|
||||
* Specifies the parameter context.
|
||||
*
|
||||
* @author Jakub Narloch
|
||||
*/
|
||||
interface AnnotatedParameterContext {
|
||||
|
||||
/**
|
||||
* Retrieves the method metadata.
|
||||
*
|
||||
* @return the method metadata
|
||||
*/
|
||||
MethodMetadata getMethodMetadata();
|
||||
|
||||
/**
|
||||
* Retrieves the index of the parameter.
|
||||
*
|
||||
* @return the parameter index
|
||||
*/
|
||||
int getParameterIndex();
|
||||
|
||||
/**
|
||||
* Sets the parameter name.
|
||||
*
|
||||
* @param name the name of the parameter
|
||||
*/
|
||||
void setParameterName(String name);
|
||||
|
||||
/**
|
||||
* Sets the template parameter.
|
||||
*
|
||||
* @param name the template parameter
|
||||
* @param rest the existing parameter values
|
||||
* @return parameters
|
||||
*/
|
||||
Collection<String> setTemplateParameter(String name, Collection<String> rest);
|
||||
}
|
||||
}
|
||||
+38
@@ -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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
+33
@@ -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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
+16
-2
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -37,7 +37,7 @@ import org.springframework.context.annotation.Import;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
@Import({ FeignClientsConfiguration.class, FeignClientsRegistrar.class })
|
||||
@Import(FeignClientsRegistrar.class)
|
||||
public @interface EnableFeignClients {
|
||||
|
||||
/**
|
||||
@@ -71,4 +71,18 @@ public @interface EnableFeignClients {
|
||||
*/
|
||||
Class<?>[] basePackageClasses() default {};
|
||||
|
||||
/**
|
||||
* A custom <code>@Configuration</code> for all feign clients. Can contain override
|
||||
* <code>@Bean</code> definition for the pieces that make up the client, for instance
|
||||
* {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
|
||||
*
|
||||
* @see FeignClientsConfiguration for the defaults
|
||||
*/
|
||||
Class<?>[] defaultConfiguration() default {};
|
||||
|
||||
/**
|
||||
* List of classes annotated with @FeignClient. If not empty, disables classpath scanning.
|
||||
* @return
|
||||
*/
|
||||
Class<?>[] clients() default {};
|
||||
}
|
||||
|
||||
+182
-8
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -16,13 +16,39 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
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
|
||||
@@ -30,8 +56,156 @@ import feign.Feign;
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(Feign.class)
|
||||
@AutoConfigureAfter(ArchaiusAutoConfiguration.class)
|
||||
@Import(FeignClientsConfiguration.class)
|
||||
@EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class})
|
||||
public class FeignAutoConfiguration {
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<FeignClientSpecification> configurations = new ArrayList<>();
|
||||
|
||||
@Bean
|
||||
public HasFeatures feignFeature() {
|
||||
return HasFeatures.namedFeature("Feign", Feign.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FeignContext feignContext() {
|
||||
FeignContext context = new FeignContext();
|
||||
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(httpClientProperties.isDisableSslValidation(), 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();
|
||||
Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
|
||||
this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation).
|
||||
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(okhttp3.OkHttpClient client) {
|
||||
return new OkHttpClient(client);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+65
-6
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -22,6 +22,8 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
/**
|
||||
* Annotation for interfaces declaring that a REST client with that interface should be
|
||||
* created (e.g. for autowiring into another component). If ribbon is available it will be
|
||||
@@ -29,6 +31,7 @@ import java.lang.annotation.Target;
|
||||
* using a <code>@RibbonClient</code> with the same name (i.e. value) as the feign client.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Venil Noronha
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@@ -36,20 +39,76 @@ import java.lang.annotation.Target;
|
||||
public @interface FeignClient {
|
||||
|
||||
/**
|
||||
* The serviceId with optional protocol prefix. Synonym for {@link #serviceId()
|
||||
* serviceId}. Either serviceId or url must be specified but not both.
|
||||
* The name of the service with optional protocol prefix. Synonym for {@link #name()
|
||||
* name}. A name must be specified for all clients, whether or not a url is provided.
|
||||
* Can be specified as property key, eg: ${propertyKey}.
|
||||
*/
|
||||
@AliasFor("name")
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* The serviceId with optional protocol prefix. Synonym for {@link #value() value}.
|
||||
* Either serviceId or url must be specified but not both.
|
||||
* The service id with optional protocol prefix. Synonym for {@link #value() value}.
|
||||
*
|
||||
* @deprecated use {@link #name() name} instead
|
||||
*/
|
||||
@Deprecated
|
||||
String serviceId() default "";
|
||||
|
||||
/**
|
||||
* The service id with optional protocol prefix. Synonym for {@link #value() value}.
|
||||
*/
|
||||
@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).
|
||||
*/
|
||||
String url() default "";
|
||||
|
||||
/**
|
||||
* Whether 404s should be decoded instead of throwing FeignExceptions
|
||||
*/
|
||||
boolean decode404() default false;
|
||||
|
||||
/**
|
||||
* A custom <code>@Configuration</code> for the feign client. Can contain override
|
||||
* <code>@Bean</code> definition for the pieces that make up the client, for instance
|
||||
* {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
|
||||
*
|
||||
* @see FeignClientsConfiguration for the defaults
|
||||
*/
|
||||
Class<?>[] configuration() default {};
|
||||
|
||||
/**
|
||||
* Fallback class for the specified Feign client interface. The fallback class must
|
||||
* implement the interface annotated by this annotation and be a valid spring bean.
|
||||
*/
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
+291
-74
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -16,14 +16,17 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
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.annotation.Autowired;
|
||||
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.StringUtils;
|
||||
|
||||
@@ -34,18 +37,22 @@ import feign.Logger;
|
||||
import feign.Request;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.Retryer;
|
||||
import feign.Target.HardCodedTarget;
|
||||
import feign.codec.Decoder;
|
||||
import feign.codec.Encoder;
|
||||
import feign.codec.ErrorDecoder;
|
||||
import feign.ribbon.LoadBalancingTarget;
|
||||
import feign.slf4j.Slf4jLogger;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Venil Noronha
|
||||
* @author Eko Kurniawan Khannedy
|
||||
* @author Gregor Zurowski
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean {
|
||||
class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
|
||||
ApplicationContextAware {
|
||||
/***********************************
|
||||
* WARNING! Nothing in this class should be @Autowired. It causes NPEs because of some lifecycle race condition.
|
||||
***********************************/
|
||||
|
||||
private Class<?> type;
|
||||
|
||||
@@ -53,92 +60,207 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean {
|
||||
|
||||
private String url;
|
||||
|
||||
@Autowired
|
||||
private Decoder decoder;
|
||||
private String path;
|
||||
|
||||
@Autowired
|
||||
private Encoder encoder;
|
||||
private boolean decode404;
|
||||
|
||||
@Autowired
|
||||
private Logger logger;
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Autowired
|
||||
private Contract contract;
|
||||
private Class<?> fallback = void.class;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Logger.Level logLevel;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Retryer retryer;
|
||||
|
||||
@Autowired(required = false)
|
||||
private ErrorDecoder errorDecoder;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Request.Options options;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Client ribbonClient;
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<RequestInterceptor> requestInterceptors;
|
||||
private Class<?> fallbackFactory = void.class;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (StringUtils.hasText(this.name)) {
|
||||
Assert.state(!StringUtils.hasText(this.url),
|
||||
"Either value or url can be specified, but not both");
|
||||
}
|
||||
Assert.hasText(this.name, "Name must be set");
|
||||
}
|
||||
|
||||
protected Feign.Builder feign() {
|
||||
Feign.Builder builder = Feign.builder()
|
||||
// required values
|
||||
.logger(this.logger).encoder(this.encoder).decoder(this.decoder)
|
||||
.contract(this.contract);
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||
this.applicationContext = context;
|
||||
}
|
||||
|
||||
// optional values
|
||||
if (this.logLevel != null) {
|
||||
builder.logLevel(this.logLevel);
|
||||
}
|
||||
if (this.retryer != null) {
|
||||
builder.retryer(this.retryer);
|
||||
}
|
||||
if (this.errorDecoder != null) {
|
||||
builder.errorDecoder(this.errorDecoder);
|
||||
}
|
||||
if (this.options != null) {
|
||||
builder.options(this.options);
|
||||
}
|
||||
if (this.requestInterceptors != null) {
|
||||
builder.requestInterceptors(this.requestInterceptors);
|
||||
}
|
||||
protected Feign.Builder feign(FeignContext context) {
|
||||
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
|
||||
Logger logger = loggerFactory.create(this.type);
|
||||
|
||||
// @formatter:off
|
||||
Feign.Builder builder = get(context, Feign.Builder.class)
|
||||
// required values
|
||||
.logger(logger)
|
||||
.encoder(get(context, Encoder.class))
|
||||
.decoder(get(context, Decoder.class))
|
||||
.contract(get(context, Contract.class));
|
||||
// @formatter:on
|
||||
|
||||
configureFeign(context, builder);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
protected <T> T loadBalance(Feign.Builder builder, Class<T> type, String schemeName) {
|
||||
builder.logger(new Slf4jLogger(type)); // TODO: how to have choice here?
|
||||
if (this.ribbonClient != null) {
|
||||
return builder.client(this.ribbonClient).target(type, schemeName);
|
||||
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);
|
||||
}
|
||||
else {
|
||||
return builder.target(LoadBalancingTarget.create(type, schemeName));
|
||||
}
|
||||
|
||||
protected void configureUsingConfiguration(FeignContext context, Feign.Builder builder) {
|
||||
Logger.Level level = getOptional(context, Logger.Level.class);
|
||||
if (level != null) {
|
||||
builder.logLevel(level);
|
||||
}
|
||||
Retryer retryer = getOptional(context, Retryer.class);
|
||||
if (retryer != null) {
|
||||
builder.retryer(retryer);
|
||||
}
|
||||
ErrorDecoder errorDecoder = getOptional(context, ErrorDecoder.class);
|
||||
if (errorDecoder != null) {
|
||||
builder.errorDecoder(errorDecoder);
|
||||
}
|
||||
Request.Options options = getOptional(context, Request.Options.class);
|
||||
if (options != null) {
|
||||
builder.options(options);
|
||||
}
|
||||
Map<String, RequestInterceptor> requestInterceptors = context.getInstances(
|
||||
this.name, RequestInterceptor.class);
|
||||
if (requestInterceptors != null) {
|
||||
builder.requestInterceptors(requestInterceptors.values());
|
||||
}
|
||||
|
||||
if (decode404) {
|
||||
builder.decode404();
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
T instance = context.getInstance(this.name, type);
|
||||
if (instance == null) {
|
||||
throw new IllegalStateException("No bean found of type " + type + " for "
|
||||
+ this.name);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
protected <T> T getOptional(FeignContext context, Class<T> type) {
|
||||
return context.getInstance(this.name, type);
|
||||
}
|
||||
|
||||
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
|
||||
HardCodedTarget<T> target) {
|
||||
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-netflix-ribbon?");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject() throws Exception {
|
||||
if (StringUtils.hasText(this.name) && !this.name.startsWith("http")) {
|
||||
this.name = "http://" + this.name;
|
||||
}
|
||||
if (StringUtils.hasText(this.name)) {
|
||||
return loadBalance(feign(), this.type, this.name);
|
||||
FeignContext context = applicationContext.getBean(FeignContext.class);
|
||||
Feign.Builder builder = feign(context);
|
||||
|
||||
if (!StringUtils.hasText(this.url)) {
|
||||
String url;
|
||||
if (!this.name.startsWith("http")) {
|
||||
url = "http://" + this.name;
|
||||
}
|
||||
else {
|
||||
url = this.name;
|
||||
}
|
||||
url += cleanPath();
|
||||
return loadBalance(builder, context, new HardCodedTarget<>(this.type,
|
||||
this.name, url));
|
||||
}
|
||||
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
|
||||
this.url = "http://" + this.url;
|
||||
}
|
||||
return feign().target(this.type, 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));
|
||||
}
|
||||
|
||||
private String cleanPath() {
|
||||
String path = this.path.trim();
|
||||
if (StringUtils.hasLength(path)) {
|
||||
if (!path.startsWith("/")) {
|
||||
path = "/" + path;
|
||||
}
|
||||
if (path.endsWith("/")) {
|
||||
path = path.substring(0, path.length() - 1);
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -151,4 +273,99 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(Class<?> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+173
@@ -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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
import org.springframework.cloud.context.named.NamedContextFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Gregor Zurowski
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
+82
-11
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -16,40 +16,111 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
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.boot.autoconfigure.web.HttpMessageConverters;
|
||||
import org.springframework.cloud.netflix.feign.support.ResponseEntityDecoder;
|
||||
import org.springframework.cloud.netflix.feign.support.SpringDecoder;
|
||||
import org.springframework.cloud.netflix.feign.support.SpringEncoder;
|
||||
import org.springframework.cloud.netflix.feign.support.SpringMvcContract;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
|
||||
import com.netflix.hystrix.HystrixCommand;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.Feign;
|
||||
import feign.Logger;
|
||||
import feign.slf4j.Slf4jLogger;
|
||||
import feign.Retryer;
|
||||
import feign.codec.Decoder;
|
||||
import feign.codec.Encoder;
|
||||
import feign.hystrix.HystrixFeign;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Venil Noronha
|
||||
*/
|
||||
@Configuration
|
||||
public class FeignClientsConfiguration {
|
||||
|
||||
@Autowired
|
||||
private ObjectFactory<HttpMessageConverters> messageConverters;
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<AnnotatedParameterProcessor> parameterProcessors = new ArrayList<>();
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<FeignFormatterRegistrar> feignFormatterRegistrars = new ArrayList<>();
|
||||
|
||||
@Autowired(required = false)
|
||||
private Logger logger;
|
||||
|
||||
@Bean
|
||||
public SpringDecoder feignDecoder() {
|
||||
return new SpringDecoder();
|
||||
@ConditionalOnMissingBean
|
||||
public Decoder feignDecoder() {
|
||||
return new ResponseEntityDecoder(new SpringDecoder(this.messageConverters));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpringEncoder feignEncoder() {
|
||||
return new SpringEncoder();
|
||||
@ConditionalOnMissingBean
|
||||
public Encoder feignEncoder() {
|
||||
return new SpringEncoder(this.messageConverters);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Logger feignLogger() {
|
||||
return new Slf4jLogger();
|
||||
@ConditionalOnMissingBean
|
||||
public Contract feignContract(ConversionService feignConversionService) {
|
||||
return new SpringMvcContract(this.parameterProcessors, feignConversionService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Contract feignContract() {
|
||||
return new SpringMvcContract();
|
||||
public FormattingConversionService feignConversionService() {
|
||||
FormattingConversionService conversionService = new DefaultFormattingConversionService();
|
||||
for (FeignFormatterRegistrar feignFormatterRegistrar : feignFormatterRegistrars) {
|
||||
feignFormatterRegistrar.registerFormatters(conversionService);
|
||||
}
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
|
||||
protected static class HystrixFeignConfiguration {
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
@ConditionalOnMissingBean
|
||||
@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(Retryer retryer) {
|
||||
return Feign.builder().retryer(retryer);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(FeignLoggerFactory.class)
|
||||
public FeignLoggerFactory feignLoggerFactory() {
|
||||
return new DefaultFeignLoggerFactory(logger);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+271
-38
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -16,8 +16,15 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -25,31 +32,46 @@ import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
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;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.filter.AbstractClassTestingTypeFilter;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @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
|
||||
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private Environment environment;
|
||||
|
||||
public FeignClientsRegistrar() {
|
||||
}
|
||||
|
||||
@@ -64,15 +86,65 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
|
||||
public void registerBeanDefinitions(AnnotationMetadata metadata,
|
||||
BeanDefinitionRegistry registry) {
|
||||
registerDefaultConfiguration(metadata, registry);
|
||||
registerFeignClients(metadata, registry);
|
||||
}
|
||||
|
||||
Set<String> basePackages = getBasePackages(importingClassMetadata);
|
||||
private void registerDefaultConfiguration(AnnotationMetadata metadata,
|
||||
BeanDefinitionRegistry registry) {
|
||||
Map<String, Object> defaultAttrs = metadata
|
||||
.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
|
||||
|
||||
if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
|
||||
String name;
|
||||
if (metadata.hasEnclosingClass()) {
|
||||
name = "default." + metadata.getEnclosingClassName();
|
||||
}
|
||||
else {
|
||||
name = "default." + metadata.getClassName();
|
||||
}
|
||||
registerClientConfiguration(registry, name,
|
||||
defaultAttrs.get("defaultConfiguration"));
|
||||
}
|
||||
}
|
||||
|
||||
public void registerFeignClients(AnnotationMetadata metadata,
|
||||
BeanDefinitionRegistry registry) {
|
||||
ClassPathScanningCandidateComponentProvider scanner = getScanner();
|
||||
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
|
||||
scanner.setResourceLoader(this.resourceLoader);
|
||||
|
||||
Set<String> basePackages;
|
||||
|
||||
Map<String, Object> attrs = metadata
|
||||
.getAnnotationAttributes(EnableFeignClients.class.getName());
|
||||
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
|
||||
FeignClient.class);
|
||||
final Class<?>[] clients = attrs == null ? null
|
||||
: (Class<?>[]) attrs.get("clients");
|
||||
if (clients == null || clients.length == 0) {
|
||||
scanner.addIncludeFilter(annotationTypeFilter);
|
||||
basePackages = getBasePackages(metadata);
|
||||
}
|
||||
else {
|
||||
final Set<String> clientClasses = new HashSet<>();
|
||||
basePackages = new HashSet<>();
|
||||
for (Class<?> clazz : clients) {
|
||||
basePackages.add(ClassUtils.getPackageName(clazz));
|
||||
clientClasses.add(clazz.getCanonicalName());
|
||||
}
|
||||
AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
|
||||
@Override
|
||||
protected boolean match(ClassMetadata metadata) {
|
||||
String cleaned = metadata.getClassName().replaceAll("\\$", ".");
|
||||
return clientClasses.contains(cleaned);
|
||||
}
|
||||
};
|
||||
scanner.addIncludeFilter(
|
||||
new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
|
||||
}
|
||||
|
||||
for (String basePackage : basePackages) {
|
||||
Set<BeanDefinition> candidateComponents = scanner
|
||||
.findCandidateComponents(basePackage);
|
||||
@@ -84,69 +156,150 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
||||
Assert.isTrue(annotationMetadata.isInterface(),
|
||||
"@FeignClient can only be specified on an interface");
|
||||
|
||||
BeanDefinitionHolder holder = createBeanDefinition(annotationMetadata);
|
||||
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
|
||||
Map<String, Object> attributes = annotationMetadata
|
||||
.getAnnotationAttributes(
|
||||
FeignClient.class.getCanonicalName());
|
||||
|
||||
String name = getClientName(attributes);
|
||||
registerClientConfiguration(registry, name,
|
||||
attributes.get("configuration"));
|
||||
|
||||
registerFeignClient(registry, annotationMetadata, attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BeanDefinitionHolder createBeanDefinition(
|
||||
AnnotationMetadata annotationMetadata) {
|
||||
Map<String, Object> attributes = annotationMetadata
|
||||
.getAnnotationAttributes(FeignClient.class.getCanonicalName());
|
||||
|
||||
private void registerFeignClient(BeanDefinitionRegistry registry,
|
||||
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
|
||||
String className = annotationMetadata.getClassName();
|
||||
BeanDefinitionBuilder definition = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(FeignClientFactoryBean.class);
|
||||
validate(attributes);
|
||||
definition.addPropertyValue("url", getUrl(attributes));
|
||||
definition.addPropertyValue("name", getServiceId(attributes));
|
||||
definition.addPropertyValue("path", getPath(attributes));
|
||||
String name = getName(attributes);
|
||||
definition.addPropertyValue("name", name);
|
||||
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 beanName = StringUtils.uncapitalize(className.substring(className
|
||||
.lastIndexOf(".") + 1));
|
||||
return new BeanDefinitionHolder(definition.getBeanDefinition(), beanName);
|
||||
String alias = name + "FeignClient";
|
||||
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
|
||||
|
||||
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");
|
||||
}
|
||||
AnnotationAttributes annotation = AnnotationAttributes.fromMap(attributes);
|
||||
// This blows up if an aliased property is overspecified
|
||||
annotation.getAliasedString("name", FeignClient.class, null);
|
||||
}
|
||||
|
||||
private String getServiceId(Map<String, Object> attributes) {
|
||||
if (StringUtils.hasText((String) attributes.get("name"))) {
|
||||
return (String) attributes.get("name");
|
||||
/* for testing */ String getName(Map<String, Object> attributes) {
|
||||
String name = (String) attributes.get("serviceId");
|
||||
if (!StringUtils.hasText(name)) {
|
||||
name = (String) attributes.get("name");
|
||||
}
|
||||
return (String) attributes.get("value");
|
||||
if (!StringUtils.hasText(name)) {
|
||||
name = (String) attributes.get("value");
|
||||
}
|
||||
name = resolve(name);
|
||||
if (!StringUtils.hasText(name)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String host = null;
|
||||
try {
|
||||
String url;
|
||||
if (!name.startsWith("http://") && !name.startsWith("https://")) {
|
||||
url = "http://" + name;
|
||||
} else {
|
||||
url = name;
|
||||
}
|
||||
host = new URI(url).getHost();
|
||||
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
}
|
||||
Assert.state(host != null, "Service id not legal hostname (" + name + ")");
|
||||
return name;
|
||||
}
|
||||
|
||||
private String resolve(String value) {
|
||||
if (StringUtils.hasText(value)) {
|
||||
return this.environment.resolvePlaceholders(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private String getUrl(Map<String, Object> attributes) {
|
||||
return (String) attributes.get("url");
|
||||
String url = resolve((String) attributes.get("url"));
|
||||
if (StringUtils.hasText(url) && !(url.startsWith("#{") && url.contains("}"))) {
|
||||
if (!url.contains("://")) {
|
||||
url = "http://" + url;
|
||||
}
|
||||
try {
|
||||
new URL(url);
|
||||
}
|
||||
catch (MalformedURLException e) {
|
||||
throw new IllegalArgumentException(url + " is malformed", e);
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
private String getPath(Map<String, Object> attributes) {
|
||||
String path = resolve((String) attributes.get("path"));
|
||||
if (StringUtils.hasText(path)) {
|
||||
path = path.trim();
|
||||
if (!path.startsWith("/")) {
|
||||
path = "/" + path;
|
||||
}
|
||||
if (path.endsWith("/")) {
|
||||
path = path.substring(0, path.length() - 1);
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
protected ClassPathScanningCandidateComponentProvider getScanner() {
|
||||
return new ClassPathScanningCandidateComponentProvider(false) {
|
||||
return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
|
||||
|
||||
@Override
|
||||
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
|
||||
protected boolean isCandidateComponent(
|
||||
AnnotatedBeanDefinition beanDefinition) {
|
||||
if (beanDefinition.getMetadata().isIndependent()) {
|
||||
// TODO until SPR-11711 will be resolved
|
||||
if (beanDefinition.getMetadata().isInterface()
|
||||
&& beanDefinition.getMetadata().getInterfaceNames().length == 1
|
||||
&& Annotation.class.getName().equals(
|
||||
beanDefinition.getMetadata().getInterfaceNames()[0])) {
|
||||
&& beanDefinition.getMetadata()
|
||||
.getInterfaceNames().length == 1
|
||||
&& Annotation.class.getName().equals(beanDefinition
|
||||
.getMetadata().getInterfaceNames()[0])) {
|
||||
try {
|
||||
Class<?> target = ClassUtils.forName(beanDefinition
|
||||
.getMetadata().getClassName(),
|
||||
Class<?> target = ClassUtils.forName(
|
||||
beanDefinition.getMetadata().getClassName(),
|
||||
FeignClientsRegistrar.this.classLoader);
|
||||
return !target.isAnnotation();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
this.logger.error("Could not load target class: "
|
||||
+ beanDefinition.getMetadata().getClassName(), ex);
|
||||
this.logger.error(
|
||||
"Could not load target class: "
|
||||
+ beanDefinition.getMetadata().getClassName(),
|
||||
ex);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -178,10 +331,90 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
||||
}
|
||||
|
||||
if (basePackages.isEmpty()) {
|
||||
basePackages.add(ClassUtils.getPackageName(importingClassMetadata
|
||||
.getClassName()));
|
||||
basePackages.add(
|
||||
ClassUtils.getPackageName(importingClassMetadata.getClassName()));
|
||||
}
|
||||
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) {
|
||||
return null;
|
||||
}
|
||||
String value = (String) client.get("value");
|
||||
if (!StringUtils.hasText(value)) {
|
||||
value = (String) client.get("name");
|
||||
}
|
||||
if (!StringUtils.hasText(value)) {
|
||||
value = (String) client.get("serviceId");
|
||||
}
|
||||
if (StringUtils.hasText(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Either 'name' or 'value' must be provided in @"
|
||||
+ FeignClient.class.getSimpleName());
|
||||
}
|
||||
|
||||
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
|
||||
Object configuration) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(FeignClientSpecification.class);
|
||||
builder.addConstructorArgValue(name);
|
||||
builder.addConstructorArgValue(configuration);
|
||||
registry.registerBeanDefinition(
|
||||
name + "." + FeignClientSpecification.class.getSimpleName(),
|
||||
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.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class AllTypeFilter implements TypeFilter {
|
||||
|
||||
private final List<TypeFilter> delegates;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AllTypeFilter} to match if all the given delegates match.
|
||||
*
|
||||
* @param delegates must not be {@literal null}.
|
||||
*/
|
||||
public AllTypeFilter(List<TypeFilter> delegates) {
|
||||
|
||||
Assert.notNull(delegates);
|
||||
this.delegates = delegates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(MetadataReader metadataReader,
|
||||
MetadataReaderFactory metadataReaderFactory) throws IOException {
|
||||
|
||||
for (TypeFilter filter : this.delegates) {
|
||||
if (!filter.match(metadataReader, metadataReaderFactory)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
import org.springframework.cloud.context.named.NamedContextFactory;
|
||||
|
||||
/**
|
||||
* A factory that creates instances of feign classes. It creates a Spring
|
||||
* ApplicationContext per client name, and extracts the beans that it needs from there.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
|
||||
|
||||
public FeignContext() {
|
||||
super(FeignClientsConfiguration.class, "feign", "feign.client.name");
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
import org.springframework.format.FormatterRegistrar;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
|
||||
/**
|
||||
* Allows an application to customize the Feign {@link FormattingConversionService}.
|
||||
*
|
||||
* @author Matt Benson
|
||||
*/
|
||||
public interface FeignFormatterRegistrar extends FormatterRegistrar {
|
||||
|
||||
}
|
||||
+36
@@ -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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
+111
@@ -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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
+29
@@ -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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
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);
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.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.PathVariable;
|
||||
|
||||
import feign.MethodMetadata;
|
||||
|
||||
import static feign.Util.checkState;
|
||||
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;
|
||||
|
||||
@Override
|
||||
public Class<? extends Annotation> getAnnotationType() {
|
||||
return ANNOTATION;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.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;
|
||||
|
||||
import feign.MethodMetadata;
|
||||
|
||||
import static feign.Util.checkState;
|
||||
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;
|
||||
|
||||
@Override
|
||||
public Class<? extends Annotation> getAnnotationType() {
|
||||
return ANNOTATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
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.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;
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.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;
|
||||
|
||||
import static feign.Util.checkState;
|
||||
import static feign.Util.emptyToNull;
|
||||
|
||||
import feign.MethodMetadata;
|
||||
|
||||
/**
|
||||
* {@link RequestParam} parameter processor.
|
||||
*
|
||||
* @author Jakub Narloch
|
||||
* @author Abhijit Sarkar
|
||||
* @see AnnotatedParameterProcessor
|
||||
*/
|
||||
public class RequestParamParameterProcessor implements AnnotatedParameterProcessor {
|
||||
|
||||
private static final Class<RequestParam> ANNOTATION = RequestParam.class;
|
||||
|
||||
@Override
|
||||
public Class<? extends Annotation> getAnnotationType() {
|
||||
return ANNOTATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
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",
|
||||
parameterIndex);
|
||||
context.setParameterName(name);
|
||||
|
||||
Collection<String> query = context.setTemplateParameter(name,
|
||||
data.template().queries().get(name));
|
||||
data.template().query(name, query);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.encoding;
|
||||
|
||||
import feign.RequestInterceptor;
|
||||
import feign.RequestTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The base request interceptor.
|
||||
*
|
||||
* @author Jakub Narloch
|
||||
*/
|
||||
public abstract class BaseRequestInterceptor implements RequestInterceptor {
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.encoding;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Feign;
|
||||
|
||||
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.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.netflix.feign.FeignAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Configures the Feign response compression.
|
||||
*
|
||||
* @author Jakub Narloch
|
||||
* @see FeignAcceptGzipEncodingInterceptor
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(FeignClientEncodingProperties.class)
|
||||
@ConditionalOnClass(Feign.class)
|
||||
@ConditionalOnBean(Client.class)
|
||||
@ConditionalOnProperty(value = "feign.compression.response.enabled", matchIfMissing = false)
|
||||
//The OK HTTP client uses "transparent" compression.
|
||||
//If the accept-encoding header is present it disable transparent compression
|
||||
@ConditionalOnMissingBean(type = "okhttp3.OkHttpClient")
|
||||
@AutoConfigureAfter(FeignAutoConfiguration.class)
|
||||
public class FeignAcceptGzipEncodingAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public FeignAcceptGzipEncodingInterceptor feignAcceptGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
|
||||
return new FeignAcceptGzipEncodingInterceptor(properties);
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.encoding;
|
||||
|
||||
import feign.RequestTemplate;
|
||||
|
||||
/**
|
||||
* Enables the HTTP response payload compression by specifying the {@code Accept-Encoding} headers.
|
||||
* Although this does not yet mean that the requests will be compressed, it requires the remote server
|
||||
* to understand the header and be configured to compress responses. Still no all responses might be compressed
|
||||
* based on the media type matching and other factors like the response content length.
|
||||
*
|
||||
* @author Jakub Narloch
|
||||
*/
|
||||
public class FeignAcceptGzipEncodingInterceptor extends BaseRequestInterceptor {
|
||||
|
||||
/**
|
||||
* Creates new instance of {@link FeignAcceptGzipEncodingInterceptor}.
|
||||
*
|
||||
* @param properties the encoding properties
|
||||
*/
|
||||
protected FeignAcceptGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void apply(RequestTemplate template) {
|
||||
|
||||
addHeader(template, HttpEncoding.ACCEPT_ENCODING_HEADER, HttpEncoding.GZIP_ENCODING,
|
||||
HttpEncoding.DEFLATE_ENCODING);
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.encoding;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* The Feign encoding properties.
|
||||
*
|
||||
* @author Jakub Narloch
|
||||
*/
|
||||
@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 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();
|
||||
}
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.encoding;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Feign;
|
||||
|
||||
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.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.netflix.feign.FeignAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Configures the Feign request compression.
|
||||
*
|
||||
* @author Jakub Narloch
|
||||
* @see FeignContentGzipEncodingInterceptor
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(FeignClientEncodingProperties.class)
|
||||
@ConditionalOnClass(Feign.class)
|
||||
@ConditionalOnBean(Client.class)
|
||||
//The OK HTTP client uses "transparent" compression.
|
||||
//If the content-encoding header is present it disable transparent compression
|
||||
@ConditionalOnMissingBean(type = "okhttp3.OkHttpClient")
|
||||
@ConditionalOnProperty(value = "feign.compression.request.enabled", matchIfMissing = false)
|
||||
@AutoConfigureAfter(FeignAutoConfiguration.class)
|
||||
public class FeignContentGzipEncodingAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public FeignContentGzipEncodingInterceptor feignContentGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
|
||||
return new FeignContentGzipEncodingInterceptor(properties);
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.encoding;
|
||||
|
||||
import feign.RequestTemplate;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Enables the HTTP request payload compression by specifying the {@code Content-Encoding} headers.
|
||||
*
|
||||
* @author Jakub Narloch
|
||||
*/
|
||||
public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor {
|
||||
|
||||
/**
|
||||
* Creates new instance of {@link FeignContentGzipEncodingInterceptor}.
|
||||
*
|
||||
* @param properties the encoding properties
|
||||
*/
|
||||
protected FeignContentGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void apply(RequestTemplate template) {
|
||||
|
||||
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) {
|
||||
|
||||
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) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
for (String mimeType : getProperties().getMimeTypes()) {
|
||||
if (contentTypes.contains(mimeType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.encoding;
|
||||
|
||||
/**
|
||||
* Lists all constants used by Feign encoders.
|
||||
*
|
||||
* @author Jakub Narloch
|
||||
*/
|
||||
public interface HttpEncoding {
|
||||
|
||||
/**
|
||||
* The HTTP Content-Length header.
|
||||
*/
|
||||
String CONTENT_LENGTH = "Content-Length";
|
||||
|
||||
/**
|
||||
* The HTTP Content-Type header.
|
||||
*/
|
||||
String CONTENT_TYPE = "Content-Type";
|
||||
|
||||
/**
|
||||
* The HTTP Accept-Encoding header.
|
||||
*/
|
||||
String ACCEPT_ENCODING_HEADER = "Accept-Encoding";
|
||||
|
||||
/**
|
||||
* The HTTP Content-Encoding header.
|
||||
*/
|
||||
String CONTENT_ENCODING_HEADER = "Content-Encoding";
|
||||
|
||||
/**
|
||||
* The GZIP encoding.
|
||||
*/
|
||||
String GZIP_ENCODING = "gzip";
|
||||
|
||||
/**
|
||||
* The Deflate encoding.
|
||||
*/
|
||||
String DEFLATE_ENCODING = "deflate";
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import 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;
|
||||
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
|
||||
/**
|
||||
* Factory for SpringLoadBalancer instances that caches the entries created.
|
||||
*
|
||||
* @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) {
|
||||
if (this.cache.containsKey(clientName)) {
|
||||
return this.cache.get(clientName);
|
||||
}
|
||||
IClientConfig config = this.factory.getClientConfig(clientName);
|
||||
ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
|
||||
ServerIntrospector serverIntrospector = this.factory.getInstance(clientName, ServerIntrospector.class);
|
||||
FeignLoadBalancer client = enableRetry ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
|
||||
loadBalancedRetryPolicyFactory, loadBalancedBackOffPolicyFactory, loadBalancedRetryListenerFactory) : new FeignLoadBalancer(lb, config, serverIntrospector);
|
||||
this.cache.put(clientName, client);
|
||||
return client;
|
||||
}
|
||||
|
||||
}
|
||||
+38
@@ -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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import feign.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);
|
||||
}
|
||||
}
|
||||
+82
-31
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -16,11 +16,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.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;
|
||||
@@ -30,32 +41,26 @@ import com.netflix.client.RetryHandler;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
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 static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttpsIfNeeded;
|
||||
|
||||
public class RibbonLoadBalancer
|
||||
extends
|
||||
AbstractLoadBalancerAwareClient<RibbonLoadBalancer.RibbonRequest, RibbonLoadBalancer.RibbonResponse> {
|
||||
public class FeignLoadBalancer extends
|
||||
AbstractLoadBalancerAwareClient<FeignLoadBalancer.RibbonRequest, FeignLoadBalancer.RibbonResponse> {
|
||||
|
||||
private final Client delegate;
|
||||
protected int connectTimeout;
|
||||
protected int readTimeout;
|
||||
protected IClientConfig clientConfig;
|
||||
protected ServerIntrospector serverIntrospector;
|
||||
|
||||
private final int connectTimeout;
|
||||
|
||||
private final int readTimeout;
|
||||
|
||||
private final IClientConfig clientConfig;
|
||||
|
||||
public RibbonLoadBalancer(Client delegate, ILoadBalancer lb,
|
||||
IClientConfig clientConfig) {
|
||||
public FeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig,
|
||||
ServerIntrospector serverIntrospector) {
|
||||
super(lb, clientConfig);
|
||||
this.setRetryHandler(RetryHandler.DEFAULT);
|
||||
this.clientConfig = clientConfig;
|
||||
this.delegate = delegate;
|
||||
this.connectTimeout = clientConfig.get(CommonClientConfigKey.ConnectTimeout);
|
||||
this.readTimeout = clientConfig.get(CommonClientConfigKey.ReadTimeout);
|
||||
this.serverIntrospector = serverIntrospector;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,22 +68,24 @@ public class RibbonLoadBalancer
|
||||
throws IOException {
|
||||
Request.Options options;
|
||||
if (configOverride != null) {
|
||||
options = new Request.Options(configOverride.get(
|
||||
CommonClientConfigKey.ConnectTimeout, this.connectTimeout),
|
||||
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);
|
||||
}
|
||||
Response response = this.delegate.execute(request.toRequest(), options);
|
||||
Response response = request.client().execute(request.toRequest(), options);
|
||||
return new RibbonResponse(request.getUri(), response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
|
||||
RibbonRequest request, IClientConfig requestConfig) {
|
||||
if (this.clientConfig.get(CommonClientConfigKey.OkToRetryOnAllOperations, false)) {
|
||||
if (this.clientConfig.get(CommonClientConfigKey.OkToRetryOnAllOperations,
|
||||
false)) {
|
||||
return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
|
||||
requestConfig);
|
||||
}
|
||||
@@ -92,24 +99,68 @@ public class RibbonLoadBalancer
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI reconstructURIWithServer(Server server, URI original) {
|
||||
URI uri = updateToHttpsIfNeeded(original, this.clientConfig, this.serverIntrospector, server);
|
||||
return super.reconstructURIWithServer(server, uri);
|
||||
}
|
||||
|
||||
static class RibbonRequest extends ClientRequest implements Cloneable {
|
||||
|
||||
private final Request request;
|
||||
private final Client client;
|
||||
|
||||
RibbonRequest(Request request, URI uri) {
|
||||
this.request = request;
|
||||
RibbonRequest(Client client, Request request, URI uri) {
|
||||
this.client = client;
|
||||
setUri(uri);
|
||||
this.request = toRequest(request);
|
||||
}
|
||||
|
||||
private Request toRequest(Request request) {
|
||||
Map<String, Collection<String>> headers = new LinkedHashMap<>(
|
||||
request.headers());
|
||||
return Request.create(request.method(),getUri().toASCIIString(),headers,request.body(),request.charset());
|
||||
}
|
||||
|
||||
Request toRequest() {
|
||||
return new RequestTemplate().method(this.request.method())
|
||||
.append(getUri().toASCIIString()).headers(this.request.headers())
|
||||
.body(this.request.body(), this.request.charset()).request();
|
||||
return toRequest(this.request);
|
||||
}
|
||||
|
||||
Client client() {
|
||||
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.request, getUri());
|
||||
return new RibbonRequest(this.client, this.request, getUri());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,4 +212,4 @@ public class RibbonLoadBalancer
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+119
@@ -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
|
||||
* *
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import org.springframework.cloud.netflix.feign.ribbon.RibbonLoadBalancer.RibbonRequest;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import com.netflix.client.ClientException;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
|
||||
import dagger.Lazy;
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
|
||||
/**
|
||||
* @author Julien Roy
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class FeignRibbonClient implements Client {
|
||||
|
||||
private Client defaultClient = new Default(new LazySSLSocketFactory(),
|
||||
new LazyHostnameVerifier());
|
||||
|
||||
private SpringClientFactory factory;
|
||||
|
||||
public FeignRibbonClient(SpringClientFactory factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response execute(Request request, Request.Options options) throws IOException {
|
||||
try {
|
||||
URI asUri = URI.create(request.url());
|
||||
String clientName = asUri.getHost();
|
||||
URI uriWithoutSchemeAndPort = URI.create(request.url().replace(
|
||||
asUri.getScheme() + "://" + asUri.getHost(),
|
||||
asUri.getScheme() + "://"));
|
||||
RibbonLoadBalancer.RibbonRequest ribbonRequest = new RibbonRequest(request,
|
||||
uriWithoutSchemeAndPort);
|
||||
LBClient client = getClient(clientName);
|
||||
return client.getLoadBalancer()
|
||||
.executeWithLoadBalancer(ribbonRequest, client.config).toResponse();
|
||||
}
|
||||
catch (ClientException ex) {
|
||||
if (ex.getCause() instanceof IOException) {
|
||||
throw IOException.class.cast(ex.getCause());
|
||||
}
|
||||
ReflectionUtils.rethrowRuntimeException(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
private class LBClient {
|
||||
private final IClientConfig config;
|
||||
private final RibbonLoadBalancer loadBalancer;
|
||||
}
|
||||
|
||||
private LBClient getClient(String clientName) {
|
||||
IClientConfig config = this.factory.getClientConfig(clientName);
|
||||
ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
|
||||
RibbonLoadBalancer loadBalancer = new RibbonLoadBalancer(this.defaultClient, lb,
|
||||
config);
|
||||
return new LBClient(config, loadBalancer);
|
||||
}
|
||||
|
||||
public void setDefaultClient(Client defaultClient) {
|
||||
this.defaultClient = defaultClient;
|
||||
}
|
||||
|
||||
private static class LazySSLSocketFactory implements Lazy<SSLSocketFactory> {
|
||||
|
||||
@Override
|
||||
public SSLSocketFactory get() {
|
||||
return (SSLSocketFactory) SSLSocketFactory.getDefault();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class LazyHostnameVerifier implements Lazy<HostnameVerifier> {
|
||||
|
||||
@Override
|
||||
public HostnameVerifier get() {
|
||||
return HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+42
-6
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -18,15 +18,24 @@ package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
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.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;
|
||||
|
||||
/**
|
||||
* Autoconfiguration to be activated if Feign is in use and needs to be use Ribbon as a
|
||||
@@ -37,9 +46,36 @@ import feign.Feign;
|
||||
@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
|
||||
public Client feignRibbonClient(SpringClientFactory factory) {
|
||||
return new FeignRibbonClient(factory);
|
||||
@Primary
|
||||
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
|
||||
public CachingSpringLoadBalancerFactory cachingLBClientFactory(
|
||||
SpringClientFactory factory) {
|
||||
return new CachingSpringLoadBalancerFactory(factory);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
@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
|
||||
@ConditionalOnMissingBean
|
||||
public Request.Options feignRequestOptions() {
|
||||
return LoadBalancerFeignClient.DEFAULT_OPTIONS;
|
||||
}
|
||||
}
|
||||
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import feign.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.apache.http.impl.client.HttpClientBuilder;
|
||||
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(httpClientProperties.isDisableSslValidation(), 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
|
||||
@ConditionalOnProperty(value = "feign.compression.response.enabled", havingValue = "true")
|
||||
public CloseableHttpClient customHttpClient(HttpClientConnectionManager httpClientConnectionManager,
|
||||
FeignHttpClientProperties httpClientProperties) {
|
||||
HttpClientBuilder builder = HttpClientBuilder.create().disableCookieManagement().useSystemProperties();
|
||||
this.httpClient = createClient(builder, httpClientConnectionManager, httpClientProperties);
|
||||
return this.httpClient;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "feign.compression.response.enabled", havingValue = "false", matchIfMissing = true)
|
||||
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory, HttpClientConnectionManager httpClientConnectionManager,
|
||||
FeignHttpClientProperties httpClientProperties) {
|
||||
this.httpClient = createClient(httpClientFactory.createBuilder(), httpClientConnectionManager, httpClientProperties);
|
||||
return this.httpClient;
|
||||
}
|
||||
|
||||
private CloseableHttpClient createClient(HttpClientBuilder builder, HttpClientConnectionManager httpClientConnectionManager,
|
||||
FeignHttpClientProperties httpClientProperties) {
|
||||
RequestConfig defaultRequestConfig = RequestConfig.custom()
|
||||
.setConnectTimeout(httpClientProperties.getConnectionTimeout())
|
||||
.setRedirectsEnabled(httpClientProperties.isFollowRedirects())
|
||||
.build();
|
||||
CloseableHttpClient httpClient = builder.setDefaultRequestConfig(defaultRequestConfig).
|
||||
setConnectionManager(httpClientConnectionManager).build();
|
||||
return 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
|
||||
import com.netflix.client.ClientException;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.DefaultClientConfigImpl;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class LoadBalancerFeignClient implements Client {
|
||||
|
||||
static final Request.Options DEFAULT_OPTIONS = new Request.Options();
|
||||
|
||||
private final Client delegate;
|
||||
private CachingSpringLoadBalancerFactory lbClientFactory;
|
||||
private SpringClientFactory clientFactory;
|
||||
|
||||
public LoadBalancerFeignClient(Client delegate,
|
||||
CachingSpringLoadBalancerFactory lbClientFactory,
|
||||
SpringClientFactory clientFactory) {
|
||||
this.delegate = delegate;
|
||||
this.lbClientFactory = lbClientFactory;
|
||||
this.clientFactory = clientFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response execute(Request request, Request.Options options) throws IOException {
|
||||
try {
|
||||
URI asUri = URI.create(request.url());
|
||||
String clientName = asUri.getHost();
|
||||
URI uriWithoutHost = cleanUrl(request.url(), clientName);
|
||||
FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
|
||||
this.delegate, request, uriWithoutHost);
|
||||
|
||||
IClientConfig requestConfig = getClientConfig(options, clientName);
|
||||
return lbClient(clientName).executeWithLoadBalancer(ribbonRequest,
|
||||
requestConfig).toResponse();
|
||||
}
|
||||
catch (ClientException e) {
|
||||
IOException io = findIOException(e);
|
||||
if (io != null) {
|
||||
throw io;
|
||||
}
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
IClientConfig getClientConfig(Request.Options options, String clientName) {
|
||||
IClientConfig requestConfig;
|
||||
if (options == DEFAULT_OPTIONS) {
|
||||
requestConfig = this.clientFactory.getClientConfig(clientName);
|
||||
} else {
|
||||
requestConfig = new FeignOptionsClientConfig(options);
|
||||
}
|
||||
return requestConfig;
|
||||
}
|
||||
|
||||
protected IOException findIOException(Throwable t) {
|
||||
if (t == null) {
|
||||
return null;
|
||||
}
|
||||
if (t instanceof IOException) {
|
||||
return (IOException) t;
|
||||
}
|
||||
return findIOException(t.getCause());
|
||||
}
|
||||
|
||||
public Client getDelegate() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
static URI cleanUrl(String originalUrl, String host) {
|
||||
String newUrl = originalUrl.replaceFirst(host, "");
|
||||
StringBuffer buffer = new StringBuffer(newUrl);
|
||||
if((newUrl.startsWith("https://") && newUrl.length() == 8) ||
|
||||
(newUrl.startsWith("http://") && newUrl.length() == 7)) {
|
||||
buffer.append("/");
|
||||
}
|
||||
return URI.create(buffer.toString());
|
||||
}
|
||||
|
||||
private FeignLoadBalancer lbClient(String clientName) {
|
||||
return this.lbClientFactory.create(clientName);
|
||||
}
|
||||
|
||||
static class FeignOptionsClientConfig extends DefaultClientConfigImpl {
|
||||
|
||||
public FeignOptionsClientConfig(Request.Options options) {
|
||||
setProperty(CommonClientConfigKey.ConnectTimeout,
|
||||
options.connectTimeoutMillis());
|
||||
setProperty(CommonClientConfigKey.ReadTimeout, options.readTimeoutMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadProperties(String clientName) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadDefaultValues() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+88
@@ -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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import feign.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(httpClientProperties.isDisableSslValidation()).
|
||||
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);
|
||||
}
|
||||
}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
*
|
||||
* * 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
|
||||
* *
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
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.RibbonRecoveryCallback;
|
||||
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 org.springframework.util.StreamUtils;
|
||||
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())) {
|
||||
byte[] byteArray = response.body() == null ? new byte[]{} : StreamUtils.copyToByteArray(response.body().asInputStream());
|
||||
response.close();
|
||||
throw new RibbonResponseStatusCodeException(RetryableFeignLoadBalancer.this.clientName, response,
|
||||
byteArray, request.getUri());
|
||||
}
|
||||
return new RibbonResponse(request.getUri(), response);
|
||||
}
|
||||
}, new RibbonRecoveryCallback<RibbonResponse, Response>() {
|
||||
@Override
|
||||
protected RibbonResponse createResponse(Response response, URI uri) {
|
||||
return new RibbonResponse(uri, 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));
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import feign.Response;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import org.springframework.cloud.client.loadbalancer.RetryableStatusCodeException;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* A {@link RetryableStatusCodeException} for {@link Response}s
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class RibbonResponseStatusCodeException extends RetryableStatusCodeException {
|
||||
private Response response;
|
||||
|
||||
public RibbonResponseStatusCodeException(String serviceId, Response response, byte[] body, URI uri) {
|
||||
super(serviceId, response.status(), response, uri);
|
||||
this.response = Response.builder().body(new ByteArrayInputStream(body), body.length)
|
||||
.headers(response.headers()).reason(response.reason())
|
||||
.status(response.status()).request(response.request()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package org.springframework.cloud.netflix.feign.support;
|
||||
|
||||
import com.netflix.hystrix.HystrixCommand;
|
||||
import com.netflix.hystrix.HystrixCommandGroupKey;
|
||||
import com.netflix.hystrix.HystrixThreadPoolKey;
|
||||
|
||||
/**
|
||||
* Convenience class for implementing feign fallbacks that return {@link HystrixCommand}.
|
||||
* Also useful for return types of {@link rx.Observable} and {@link java.util.concurrent.Future}.
|
||||
* For those return types, just call {@link FallbackCommand#observe()} or {@link FallbackCommand#queue()} respectively.
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class FallbackCommand<T> extends HystrixCommand<T> {
|
||||
|
||||
private T result;
|
||||
|
||||
public FallbackCommand(T result) {
|
||||
this(result, "fallback");
|
||||
}
|
||||
|
||||
protected FallbackCommand(T result, String groupname) {
|
||||
super(HystrixCommandGroupKey.Factory.asKey(groupname));
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public FallbackCommand(T result, HystrixCommandGroupKey group) {
|
||||
super(group);
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public FallbackCommand(T result, HystrixCommandGroupKey group, int executionIsolationThreadTimeoutInMilliseconds) {
|
||||
super(group, executionIsolationThreadTimeoutInMilliseconds);
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public FallbackCommand(T result, HystrixCommandGroupKey group, HystrixThreadPoolKey threadPool) {
|
||||
super(group, threadPool);
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public FallbackCommand(T result, HystrixCommandGroupKey group, HystrixThreadPoolKey threadPool, int executionIsolationThreadTimeoutInMilliseconds) {
|
||||
super(group, threadPool, executionIsolationThreadTimeoutInMilliseconds);
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public FallbackCommand(T result, Setter setter) {
|
||||
super(setter);
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T run() throws Exception {
|
||||
return this.result;
|
||||
}
|
||||
}
|
||||
+110
@@ -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
|
||||
* *
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.feign.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;
|
||||
}
|
||||
}
|
||||
+13
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -18,6 +18,8 @@ package org.springframework.cloud.netflix.feign.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -35,4 +37,14 @@ public class FeignUtils {
|
||||
return httpHeaders;
|
||||
}
|
||||
|
||||
static Map<String, Collection<String>> getHeaders(HttpHeaders httpHeaders) {
|
||||
LinkedHashMap<String, Collection<String>> headers = new LinkedHashMap<>();
|
||||
|
||||
for (Map.Entry<String, List<String>> entry : httpHeaders.entrySet()) {
|
||||
headers.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package org.springframework.cloud.netflix.feign.support;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import feign.FeignException;
|
||||
import feign.Response;
|
||||
import feign.codec.Decoder;
|
||||
|
||||
/**
|
||||
* Decoder adds compatibility for Spring MVC's ResponseEntity to any other decoder via
|
||||
* composition.
|
||||
* @author chadjaros
|
||||
*/
|
||||
public class ResponseEntityDecoder implements Decoder {
|
||||
|
||||
private Decoder decoder;
|
||||
|
||||
public ResponseEntityDecoder(Decoder decoder) {
|
||||
this.decoder = decoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object decode(final Response response, Type type) throws IOException,
|
||||
FeignException {
|
||||
|
||||
if (isParameterizeHttpEntity(type)) {
|
||||
type = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
Object decodedObject = decoder.decode(response, type);
|
||||
|
||||
return createResponse(decodedObject, response);
|
||||
}
|
||||
else if (isHttpEntity(type)) {
|
||||
return createResponse(null, response);
|
||||
}
|
||||
else {
|
||||
return decoder.decode(response, type);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isParameterizeHttpEntity(Type type) {
|
||||
if (type instanceof ParameterizedType) {
|
||||
return isHttpEntity(((ParameterizedType) type).getRawType());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isHttpEntity(Type type) {
|
||||
if (type instanceof Class) {
|
||||
Class c = (Class) type;
|
||||
return HttpEntity.class.isAssignableFrom(c);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> ResponseEntity<T> createResponse(Object instance, Response response) {
|
||||
|
||||
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
|
||||
for (String key : response.headers().keySet()) {
|
||||
headers.put(key, new LinkedList<>(response.headers().get(key)));
|
||||
}
|
||||
|
||||
return new ResponseEntity<>((T) instance, headers, HttpStatus.valueOf(response
|
||||
.status()));
|
||||
}
|
||||
}
|
||||
+13
-13
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -16,22 +16,21 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign.support;
|
||||
|
||||
import static org.springframework.cloud.netflix.feign.support.FeignUtils.getHttpHeaders;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.WildcardType;
|
||||
|
||||
import javax.inject.Provider;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.web.client.HttpMessageConverterExtractor;
|
||||
|
||||
import static org.springframework.cloud.netflix.feign.support.FeignUtils.getHttpHeaders;
|
||||
|
||||
import feign.FeignException;
|
||||
import feign.Response;
|
||||
import feign.codec.DecodeException;
|
||||
@@ -42,19 +41,20 @@ import feign.codec.Decoder;
|
||||
*/
|
||||
public class SpringDecoder implements Decoder {
|
||||
|
||||
@Autowired
|
||||
private Provider<HttpMessageConverters> messageConverters;
|
||||
private ObjectFactory<HttpMessageConverters> messageConverters;
|
||||
|
||||
public SpringDecoder() {
|
||||
public SpringDecoder(ObjectFactory<HttpMessageConverters> messageConverters) {
|
||||
this.messageConverters = messageConverters;
|
||||
}
|
||||
|
||||
@Override
|
||||
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.get().getConverters());
|
||||
type, this.messageConverters.getObject().getConverters());
|
||||
|
||||
return extractor.extractData(new FeignResponseAdapter(response));
|
||||
}
|
||||
|
||||
+40
-17
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -19,37 +19,45 @@ package org.springframework.cloud.netflix.feign.support;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.inject.Provider;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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 org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
|
||||
|
||||
import static org.springframework.cloud.netflix.feign.support.FeignUtils.getHeaders;
|
||||
import static org.springframework.cloud.netflix.feign.support.FeignUtils.getHttpHeaders;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author ScienJus
|
||||
*/
|
||||
@CommonsLog
|
||||
public class SpringEncoder implements Encoder {
|
||||
|
||||
@Autowired
|
||||
private Provider<HttpMessageConverters> messageConverters;
|
||||
private static final Log log = LogFactory.getLog(SpringEncoder.class);
|
||||
|
||||
private ObjectFactory<HttpMessageConverters> messageConverters;
|
||||
|
||||
public SpringEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
|
||||
this.messageConverters = messageConverters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Object requestBody, RequestTemplate request)
|
||||
public void encode(Object requestBody, Type bodyType, RequestTemplate request)
|
||||
throws EncodeException {
|
||||
// template.body(conversionService.convert(object, String.class));
|
||||
if (requestBody != null) {
|
||||
@@ -62,8 +70,8 @@ public class SpringEncoder implements Encoder {
|
||||
requestContentType = MediaType.valueOf(type);
|
||||
}
|
||||
|
||||
for (HttpMessageConverter<?> messageConverter : this.messageConverters.get()
|
||||
.getConverters()) {
|
||||
for (HttpMessageConverter<?> messageConverter : this.messageConverters
|
||||
.getObject().getConverters()) {
|
||||
if (messageConverter.canWrite(requestType, requestContentType)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
if (requestContentType != null) {
|
||||
@@ -87,8 +95,23 @@ public class SpringEncoder implements Encoder {
|
||||
catch (IOException ex) {
|
||||
throw new EncodeException("Error converting request body", ex);
|
||||
}
|
||||
request.body(outputMessage.getOutputStream().toByteArray(),
|
||||
Charset.forName("UTF-8")); // TODO: set charset
|
||||
// clear headers
|
||||
request.headers(null);
|
||||
// converters can modify headers, so update the request
|
||||
// with the modified headers
|
||||
request.headers(getHeaders(outputMessage.getHeaders()));
|
||||
|
||||
// do not use charset for binary data and protobuf
|
||||
Charset charset;
|
||||
if (messageConverter instanceof ByteArrayHttpMessageConverter) {
|
||||
charset = null;
|
||||
} else if (messageConverter instanceof ProtobufHttpMessageConverter &&
|
||||
ProtobufHttpMessageConverter.PROTOBUF.isCompatibleWith(outputMessage.getHeaders().getContentType())) {
|
||||
charset = null;
|
||||
} else {
|
||||
charset = StandardCharsets.UTF_8;
|
||||
}
|
||||
request.body(outputMessage.getOutputStream().toByteArray(), charset);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -105,10 +128,10 @@ public class SpringEncoder implements Encoder {
|
||||
|
||||
private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
|
||||
private final RequestTemplate request;
|
||||
private final HttpHeaders httpHeaders;
|
||||
|
||||
private FeignOutputMessage(RequestTemplate request) {
|
||||
this.request = request;
|
||||
httpHeaders = getHttpHeaders(request.headers());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -118,7 +141,7 @@ public class SpringEncoder implements Encoder {
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return getHttpHeaders(this.request.headers());
|
||||
return this.httpHeaders;
|
||||
}
|
||||
|
||||
public ByteArrayOutputStream getOutputStream() {
|
||||
|
||||
+311
-110
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -18,82 +18,193 @@ 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;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.cloud.netflix.feign.AnnotatedParameterProcessor;
|
||||
import org.springframework.cloud.netflix.feign.annotation.PathVariableParameterProcessor;
|
||||
import org.springframework.cloud.netflix.feign.annotation.RequestHeaderParameterProcessor;
|
||||
import org.springframework.cloud.netflix.feign.annotation.RequestParamParameterProcessor;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.MethodMetadata;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
import static feign.Util.checkState;
|
||||
import static feign.Util.emptyToNull;
|
||||
import static org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.Feign;
|
||||
import feign.MethodMetadata;
|
||||
import feign.Param;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Abhijit Sarkar
|
||||
*/
|
||||
public class SpringMvcContract extends Contract.BaseContract {
|
||||
public class SpringMvcContract extends Contract.BaseContract
|
||||
implements ResourceLoaderAware {
|
||||
|
||||
private static final String ACCEPT = "Accept";
|
||||
|
||||
private static final String CONTENT_TYPE = "Content-Type";
|
||||
|
||||
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
|
||||
|
||||
private final Map<Class<? extends Annotation>, AnnotatedParameterProcessor> annotatedArgumentProcessors;
|
||||
private final Map<String, Method> processedMethods = new HashMap<>();
|
||||
|
||||
private final ConversionService conversionService;
|
||||
private final Param.Expander expander;
|
||||
private ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
|
||||
public SpringMvcContract() {
|
||||
this(Collections.<AnnotatedParameterProcessor> emptyList());
|
||||
}
|
||||
|
||||
public SpringMvcContract(
|
||||
List<AnnotatedParameterProcessor> annotatedParameterProcessors) {
|
||||
this(annotatedParameterProcessors, new DefaultConversionService());
|
||||
}
|
||||
|
||||
public SpringMvcContract(
|
||||
List<AnnotatedParameterProcessor> annotatedParameterProcessors,
|
||||
ConversionService conversionService) {
|
||||
Assert.notNull(annotatedParameterProcessors,
|
||||
"Parameter processors can not be null.");
|
||||
Assert.notNull(conversionService, "ConversionService can not be null.");
|
||||
|
||||
List<AnnotatedParameterProcessor> processors;
|
||||
if (!annotatedParameterProcessors.isEmpty()) {
|
||||
processors = new ArrayList<>(annotatedParameterProcessors);
|
||||
}
|
||||
else {
|
||||
processors = getDefaultAnnotatedArgumentsProcessors();
|
||||
}
|
||||
this.annotatedArgumentProcessors = toAnnotatedArgumentProcessorMap(processors);
|
||||
this.conversionService = conversionService;
|
||||
this.expander = new ConvertingExpander(conversionService);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processAnnotationOnMethod(MethodMetadata data,
|
||||
Annotation methodAnnotation, Method method) {
|
||||
RequestMapping mapping = RequestMapping.class.cast(methodAnnotation);
|
||||
if (mapping != null) {
|
||||
// HTTP Method
|
||||
checkOne(method, mapping.method(), "method");
|
||||
data.template().method(mapping.method()[0].name());
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
// path
|
||||
checkOne(method, mapping.value(), "value");
|
||||
|
||||
String methodAnnotationValue = mapping.value()[0];
|
||||
String pathValue = emptyToNull(methodAnnotationValue);
|
||||
checkState(pathValue != null, "value was empty on method %s",
|
||||
method.getName());
|
||||
if (!methodAnnotationValue.startsWith("/")
|
||||
&& !data.template().toString().endsWith("/")) {
|
||||
methodAnnotationValue = "/" + methodAnnotationValue;
|
||||
}
|
||||
data.template().append(methodAnnotationValue);
|
||||
|
||||
// produces
|
||||
checkAtMostOne(method, mapping.produces(), "produces");
|
||||
String[] serverProduces = mapping.produces();
|
||||
String clientAccepts = serverProduces.length == 0 ? null
|
||||
: emptyToNull(serverProduces[0]);
|
||||
if (clientAccepts != null) {
|
||||
data.template().header(ACCEPT, clientAccepts);
|
||||
}
|
||||
|
||||
// consumes
|
||||
checkAtMostOne(method, mapping.consumes(), "consumes");
|
||||
String[] serverConsumes = mapping.consumes();
|
||||
String clientProduces = serverConsumes.length == 0 ? null
|
||||
: emptyToNull(serverConsumes[0]);
|
||||
if (clientProduces != null) {
|
||||
data.template().header(CONTENT_TYPE, clientProduces);
|
||||
}
|
||||
|
||||
// headers
|
||||
// TODO: only supports one header value per key
|
||||
if (mapping.headers() != null && mapping.headers().length > 0) {
|
||||
for (String header : mapping.headers()) {
|
||||
int colon = header.indexOf(':');
|
||||
data.template().header(header.substring(0, colon),
|
||||
header.substring(colon + 2));
|
||||
@Override
|
||||
protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
|
||||
if (clz.getInterfaces().length == 0) {
|
||||
RequestMapping classAnnotation = findMergedAnnotation(clz,
|
||||
RequestMapping.class);
|
||||
if (classAnnotation != null) {
|
||||
// Prepend path from class annotation if specified
|
||||
if (classAnnotation.value().length > 0) {
|
||||
String pathValue = emptyToNull(classAnnotation.value()[0]);
|
||||
pathValue = resolve(pathValue);
|
||||
if (!pathValue.startsWith("/")) {
|
||||
pathValue = "/" + pathValue;
|
||||
}
|
||||
data.template().insert(0, pathValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
|
||||
this.processedMethods.put(Feign.configKey(targetType, method), method);
|
||||
MethodMetadata md = super.parseAndValidateMetadata(targetType, method);
|
||||
|
||||
RequestMapping classAnnotation = findMergedAnnotation(targetType,
|
||||
RequestMapping.class);
|
||||
if (classAnnotation != null) {
|
||||
// produces - use from class annotation only if method has not specified this
|
||||
if (!md.template().headers().containsKey(ACCEPT)) {
|
||||
parseProduces(md, method, classAnnotation);
|
||||
}
|
||||
|
||||
// consumes -- use from class annotation only if method has not specified this
|
||||
if (!md.template().headers().containsKey(CONTENT_TYPE)) {
|
||||
parseConsumes(md, method, classAnnotation);
|
||||
}
|
||||
|
||||
// headers -- class annotation is inherited to methods, always write these if
|
||||
// present
|
||||
parseHeaders(md, method, classAnnotation);
|
||||
}
|
||||
return md;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processAnnotationOnMethod(MethodMetadata data,
|
||||
Annotation methodAnnotation, Method method) {
|
||||
if (!RequestMapping.class.isInstance(methodAnnotation) && !methodAnnotation
|
||||
.annotationType().isAnnotationPresent(RequestMapping.class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
RequestMapping methodMapping = findMergedAnnotation(method, RequestMapping.class);
|
||||
// HTTP Method
|
||||
RequestMethod[] methods = methodMapping.method();
|
||||
if (methods.length == 0) {
|
||||
methods = new RequestMethod[] { RequestMethod.GET };
|
||||
}
|
||||
checkOne(method, methods, "method");
|
||||
data.template().method(methods[0].name());
|
||||
|
||||
// path
|
||||
checkAtMostOne(method, methodMapping.value(), "value");
|
||||
if (methodMapping.value().length > 0) {
|
||||
String pathValue = emptyToNull(methodMapping.value()[0]);
|
||||
if (pathValue != null) {
|
||||
pathValue = resolve(pathValue);
|
||||
// Append path from @RequestMapping if value is present on method
|
||||
if (!pathValue.startsWith("/")
|
||||
&& !data.template().toString().endsWith("/")) {
|
||||
pathValue = "/" + pathValue;
|
||||
}
|
||||
data.template().append(pathValue);
|
||||
}
|
||||
}
|
||||
|
||||
// produces
|
||||
parseProduces(data, method, methodMapping);
|
||||
|
||||
// consumes
|
||||
parseConsumes(data, method, methodMapping);
|
||||
|
||||
// headers
|
||||
parseHeaders(data, method, methodMapping);
|
||||
|
||||
data.indexToExpander(new LinkedHashMap<Integer, Param.Expander>());
|
||||
}
|
||||
|
||||
private String resolve(String value) {
|
||||
if (StringUtils.hasText(value)
|
||||
&& this.resourceLoader instanceof ConfigurableApplicationContext) {
|
||||
return ((ConfigurableApplicationContext) this.resourceLoader).getEnvironment()
|
||||
.resolvePlaceholders(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private void checkAtMostOne(Method method, Object[] values, String fieldName) {
|
||||
checkState(values != null && (values.length == 0 || values.length == 1),
|
||||
"Method %s can only contain at most 1 %s field. Found: %s",
|
||||
@@ -111,69 +222,159 @@ public class SpringMvcContract extends Contract.BaseContract {
|
||||
protected boolean processAnnotationsOnParameter(MethodMetadata data,
|
||||
Annotation[] annotations, int paramIndex) {
|
||||
boolean isHttpAnnotation = false;
|
||||
// TODO: support spring parameter annotations?
|
||||
|
||||
AnnotatedParameterProcessor.AnnotatedParameterContext context = new SimpleAnnotatedParameterContext(
|
||||
data, paramIndex);
|
||||
Method method = this.processedMethods.get(data.configKey());
|
||||
for (Annotation parameterAnnotation : annotations) {
|
||||
Class<? extends Annotation> annotationType = parameterAnnotation
|
||||
.annotationType();
|
||||
if (annotationType == PathVariable.class) {
|
||||
String name = PathVariable.class.cast(parameterAnnotation).value();
|
||||
checkState(emptyToNull(name) != null,
|
||||
"PathVariable annotation was empty on param %s.", paramIndex);
|
||||
nameParam(data, name, paramIndex);
|
||||
isHttpAnnotation = true;
|
||||
String varName = '{' + name + '}';
|
||||
if (data.template().url().indexOf(varName) == -1
|
||||
&& !searchMapValues(data.template().queries(), varName)
|
||||
&& !searchMapValues(data.template().headers(), varName)) {
|
||||
data.formParams().add(name);
|
||||
}
|
||||
AnnotatedParameterProcessor processor = this.annotatedArgumentProcessors
|
||||
.get(parameterAnnotation.annotationType());
|
||||
if (processor != null) {
|
||||
Annotation processParameterAnnotation;
|
||||
// synthesize, handling @AliasFor, while falling back to parameter name on
|
||||
// missing String #value():
|
||||
processParameterAnnotation = synthesizeWithMethodParameterNameAsFallbackValue(
|
||||
parameterAnnotation, method, paramIndex);
|
||||
isHttpAnnotation |= processor.processArgument(context,
|
||||
processParameterAnnotation, method);
|
||||
}
|
||||
else if (annotationType == RequestParam.class) {
|
||||
String name = RequestParam.class.cast(parameterAnnotation).value();
|
||||
checkState(emptyToNull(name) != null,
|
||||
"QueryParam.value() was empty on parameter %s", paramIndex);
|
||||
Collection<String> query = addTemplatedParam(data.template().queries()
|
||||
.get(name), name);
|
||||
data.template().query(name, query);
|
||||
nameParam(data, name, paramIndex);
|
||||
isHttpAnnotation = true;
|
||||
}
|
||||
else if (annotationType == RequestHeader.class) {
|
||||
String name = RequestHeader.class.cast(parameterAnnotation).value();
|
||||
checkState(emptyToNull(name) != null,
|
||||
"HeaderParam.value() was empty on parameter %s", paramIndex);
|
||||
Collection<String> header = addTemplatedParam(data.template().headers()
|
||||
.get(name), name);
|
||||
data.template().header(name, header);
|
||||
nameParam(data, name, paramIndex);
|
||||
isHttpAnnotation = true;
|
||||
}
|
||||
|
||||
// TODO
|
||||
/*
|
||||
* else if (annotationType == FormParam.class) { String name =
|
||||
* FormParam.class.cast(parameterAnnotation).value();
|
||||
* checkState(emptyToNull(name) != null,
|
||||
* "FormParam.value() was empty on parameter %s", paramIndex);
|
||||
* data.formParams().add(name); nameParam(data, name, paramIndex);
|
||||
* isHttpAnnotation = true; }
|
||||
*/
|
||||
|
||||
}
|
||||
if (isHttpAnnotation && data.indexToExpander().get(paramIndex) == null
|
||||
&& this.conversionService.canConvert(
|
||||
method.getParameterTypes()[paramIndex], String.class)) {
|
||||
data.indexToExpander().put(paramIndex, this.expander);
|
||||
}
|
||||
return isHttpAnnotation;
|
||||
}
|
||||
|
||||
private <K, V> boolean searchMapValues(Map<K, Collection<V>> map, V search) {
|
||||
Collection<Collection<V>> values = map.values();
|
||||
if (values == null) {
|
||||
return false;
|
||||
private void parseProduces(MethodMetadata md, Method method,
|
||||
RequestMapping annotation) {
|
||||
checkAtMostOne(method, annotation.produces(), "produces");
|
||||
String[] serverProduces = annotation.produces();
|
||||
String clientAccepts = serverProduces.length == 0 ? null
|
||||
: emptyToNull(serverProduces[0]);
|
||||
if (clientAccepts != null) {
|
||||
md.template().header(ACCEPT, clientAccepts);
|
||||
}
|
||||
for (Collection<V> entry : values) {
|
||||
if (entry.contains(search)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void parseConsumes(MethodMetadata md, Method method,
|
||||
RequestMapping annotation) {
|
||||
checkAtMostOne(method, annotation.consumes(), "consumes");
|
||||
String[] serverConsumes = annotation.consumes();
|
||||
String clientProduces = serverConsumes.length == 0 ? null
|
||||
: emptyToNull(serverConsumes[0]);
|
||||
if (clientProduces != null) {
|
||||
md.template().header(CONTENT_TYPE, clientProduces);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseHeaders(MethodMetadata md, Method method,
|
||||
RequestMapping annotation) {
|
||||
// TODO: only supports one header value per key
|
||||
if (annotation.headers() != null && annotation.headers().length > 0) {
|
||||
for (String header : annotation.headers()) {
|
||||
int index = header.indexOf('=');
|
||||
if (!header.contains("!=") && index >= 0) {
|
||||
md.template().header(resolve(header.substring(0, index)),
|
||||
resolve(header.substring(index + 1).trim()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Class<? extends Annotation>, AnnotatedParameterProcessor> toAnnotatedArgumentProcessorMap(
|
||||
List<AnnotatedParameterProcessor> processors) {
|
||||
Map<Class<? extends Annotation>, AnnotatedParameterProcessor> result = new HashMap<>();
|
||||
for (AnnotatedParameterProcessor processor : processors) {
|
||||
result.put(processor.getAnnotationType(), processor);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<AnnotatedParameterProcessor> getDefaultAnnotatedArgumentsProcessors() {
|
||||
|
||||
List<AnnotatedParameterProcessor> annotatedArgumentResolvers = new ArrayList<>();
|
||||
|
||||
annotatedArgumentResolvers.add(new PathVariableParameterProcessor());
|
||||
annotatedArgumentResolvers.add(new RequestParamParameterProcessor());
|
||||
annotatedArgumentResolvers.add(new RequestHeaderParameterProcessor());
|
||||
|
||||
return annotatedArgumentResolvers;
|
||||
}
|
||||
|
||||
private Annotation synthesizeWithMethodParameterNameAsFallbackValue(
|
||||
Annotation parameterAnnotation, Method method, int parameterIndex) {
|
||||
Map<String, Object> annotationAttributes = AnnotationUtils
|
||||
.getAnnotationAttributes(parameterAnnotation);
|
||||
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 (shouldAddParameterName(parameterIndex, parameterTypes, parameterNames)) {
|
||||
annotationAttributes.put(AnnotationUtils.VALUE,
|
||||
parameterNames[parameterIndex]);
|
||||
}
|
||||
}
|
||||
return AnnotationUtils.synthesizeAnnotation(annotationAttributes,
|
||||
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 {
|
||||
|
||||
private final MethodMetadata methodMetadata;
|
||||
|
||||
private final int parameterIndex;
|
||||
|
||||
public SimpleAnnotatedParameterContext(MethodMetadata methodMetadata,
|
||||
int parameterIndex) {
|
||||
this.methodMetadata = methodMetadata;
|
||||
this.parameterIndex = parameterIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodMetadata getMethodMetadata() {
|
||||
return this.methodMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getParameterIndex() {
|
||||
return this.parameterIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameterName(String name) {
|
||||
nameParam(this.methodMetadata, name, this.parameterIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> setTemplateParameter(String name,
|
||||
Collection<String> rest) {
|
||||
return addTemplatedParam(rest, name);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConvertingExpander implements Param.Expander {
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
public ConvertingExpander(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String expand(Object value) {
|
||||
return this.conversionService.convert(value, String.class);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+4
-3
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -16,11 +16,11 @@
|
||||
|
||||
package org.springframework.cloud.netflix.hystrix;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.ConditionalOnEnabledHealthIndicator;
|
||||
import org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@@ -30,14 +30,15 @@ import com.netflix.hystrix.Hystrix;
|
||||
* Auto configuration for Hystrix.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ Hystrix.class, HealthIndicator.class })
|
||||
@ConditionalOnProperty(value = "health.hystrix.enabled", matchIfMissing = true)
|
||||
@AutoConfigureAfter({ HealthIndicatorAutoConfiguration.class })
|
||||
public class HystrixAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnEnabledHealthIndicator("hystrix")
|
||||
public HystrixHealthIndicator hystrixHealthIndicator() {
|
||||
return new HystrixHealthIndicator();
|
||||
}
|
||||
|
||||
+23
-4
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -32,6 +32,9 @@ import org.springframework.boot.actuate.metrics.GaugeService;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.client.actuator.HasFeatures;
|
||||
import org.springframework.cloud.client.actuator.NamedFeature;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -47,6 +50,7 @@ import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServl
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Christian Dupuis
|
||||
* @author Venil Noronha
|
||||
*/
|
||||
@Configuration
|
||||
public class HystrixCircuitBreakerConfiguration {
|
||||
@@ -61,6 +65,11 @@ public class HystrixCircuitBreakerConfiguration {
|
||||
return new HystrixShutdownHook();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HasFeatures hystrixFeature() {
|
||||
return HasFeatures.namedFeatures(new NamedFeature("Hystrix", HystrixCommandAspect.class));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnProperty(value = "hystrix.stream.endpoint.enabled", matchIfMissing = true)
|
||||
@ConditionalOnWebApplication
|
||||
@@ -72,10 +81,16 @@ public class HystrixCircuitBreakerConfiguration {
|
||||
return new HystrixStreamEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HasFeatures hystrixStreamFeature() {
|
||||
return HasFeatures.namedFeature("Hystrix Stream Servlet", HystrixStreamEndpoint.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnProperty(value = "hystrix.metrics.enabled", matchIfMissing = true)
|
||||
@ConditionalOnClass({ HystrixMetricsPoller.class, GaugeService.class })
|
||||
@EnableConfigurationProperties(HystrixMetricsProperties.class)
|
||||
protected static class HystrixMetricsPollerConfiguration implements SmartLifecycle {
|
||||
|
||||
private static Log logger = LogFactory
|
||||
@@ -84,6 +99,9 @@ public class HystrixCircuitBreakerConfiguration {
|
||||
@Autowired(required = false)
|
||||
private GaugeService gauges;
|
||||
|
||||
@Autowired
|
||||
private HystrixMetricsProperties metricsProperties;
|
||||
|
||||
private ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
private HystrixMetricsPoller poller;
|
||||
@@ -113,7 +131,8 @@ public class HystrixCircuitBreakerConfiguration {
|
||||
}
|
||||
|
||||
};
|
||||
this.poller = new HystrixMetricsPoller(listener, 2000);
|
||||
this.poller = new HystrixMetricsPoller(listener,
|
||||
metricsProperties.getPollingIntervalMs());
|
||||
// start polling and it will write directly to the listener
|
||||
this.poller.start();
|
||||
logger.info("Starting poller");
|
||||
@@ -139,7 +158,7 @@ public class HystrixCircuitBreakerConfiguration {
|
||||
else if (value instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> sub = (Map<String, Object>) value;
|
||||
addMetrics(sub, prefix);
|
||||
addMetrics(sub, prefix + "." + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-3
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -19,8 +19,12 @@ package org.springframework.cloud.netflix.hystrix;
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public interface HystrixConstants {
|
||||
public class HystrixConstants {
|
||||
|
||||
String HYSTRIX_STREAM_NAME = "spring.cloud.hystrix.stream";
|
||||
public static final String HYSTRIX_STREAM_DESTINATION = "springCloudHystrixStream";
|
||||
|
||||
private HystrixConstants() {
|
||||
throw new AssertionError("Must not instantiate constant utility class");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-4
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -30,8 +30,8 @@ import com.netflix.hystrix.HystrixCommandMetrics;
|
||||
/**
|
||||
* A {@link HealthIndicator} implementation for Hystrix circuit breakers.
|
||||
* <p>
|
||||
* This default implementation will set the system to <code>OUT_OF_SERVICE</code> and
|
||||
* include all open circuits by name.
|
||||
* This default implementation will not change the system state (e.g. <code>OK</code>) but
|
||||
* includes all open circuits by name.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
*/
|
||||
@@ -47,7 +47,7 @@ public class HystrixHealthIndicator extends AbstractHealthIndicator {
|
||||
for (HystrixCommandMetrics metrics : HystrixCommandMetrics.getInstances()) {
|
||||
HystrixCircuitBreaker circuitBreaker = HystrixCircuitBreaker.Factory
|
||||
.getInstance(metrics.getCommandKey());
|
||||
if (circuitBreaker.isOpen()) {
|
||||
if (circuitBreaker != null && circuitBreaker.isOpen()) {
|
||||
openCircuitBreakers.add(metrics.getCommandGroup().name() + "::"
|
||||
+ metrics.getCommandKey().name());
|
||||
}
|
||||
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.hystrix;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author Venil Noronha
|
||||
* @author Gregor Zurowski
|
||||
*/
|
||||
@ConfigurationProperties("hystrix.metrics")
|
||||
public class HystrixMetricsProperties {
|
||||
|
||||
/** Enable Hystrix metrics polling. Defaults to true. */
|
||||
private boolean enabled = true;
|
||||
|
||||
/** 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();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+82
@@ -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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.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 {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+86
@@ -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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.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));
|
||||
}
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.metrics;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
|
||||
/**
|
||||
* @author Jon Schneider
|
||||
*/
|
||||
public class DefaultMetricsTagProvider implements MetricsTagProvider {
|
||||
@Override
|
||||
public Map<String, String> clientHttpRequestTags(HttpRequest request,
|
||||
ClientHttpResponse response) {
|
||||
String urlTemplate = RestTemplateUrlTemplateHolder.getRestTemplateUrlTemplate();
|
||||
if (urlTemplate == null) {
|
||||
urlTemplate = "none";
|
||||
}
|
||||
|
||||
String status;
|
||||
try {
|
||||
status = (response == null) ? "CLIENT_ERROR" : ((Integer) response
|
||||
.getRawStatusCode()).toString();
|
||||
}
|
||||
catch (IOException e) {
|
||||
status = "IO_ERROR";
|
||||
}
|
||||
|
||||
String host = request.getURI().getHost();
|
||||
if( host == null ) {
|
||||
host = "none";
|
||||
}
|
||||
|
||||
String strippedUrlTemplate = urlTemplate.replaceAll("^https?://[^/]+/", "");
|
||||
|
||||
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
|
||||
public Map<String, String> httpRequestTags(HttpServletRequest request,
|
||||
HttpServletResponse response, Object handler, String caller) {
|
||||
Map<String, String> tags = new HashMap<>();
|
||||
|
||||
tags.put("method", request.getMethod());
|
||||
tags.put("status", ((Integer) response.getStatus()).toString());
|
||||
|
||||
String uri = (String) request
|
||||
.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
|
||||
if (uri == null) {
|
||||
uri = request.getPathInfo();
|
||||
}
|
||||
if (!StringUtils.hasText(uri)) {
|
||||
uri = "/";
|
||||
}
|
||||
uri = sanitizeUrlTemplate(uri.substring(1));
|
||||
tags.put("uri", uri.isEmpty() ? "root" : uri);
|
||||
|
||||
Object exception = request.getAttribute("exception");
|
||||
if (exception != null) {
|
||||
tags.put("exception", exception.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
if (caller != null) {
|
||||
tags.put("caller", caller);
|
||||
}
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* As is, the urlTemplate is not suitable for use with Atlas, as all interactions with
|
||||
* Atlas take place via query parameters
|
||||
*/
|
||||
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";
|
||||
}
|
||||
return sanitized;
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.metrics;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
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.monitor.MonitorConfig;
|
||||
import com.netflix.servo.tag.SmallTagMap;
|
||||
import com.netflix.servo.tag.Tags;
|
||||
|
||||
/**
|
||||
* Intercepts RestTemplate requests and records metrics about execution time and results.
|
||||
*
|
||||
* @author Jon Schneider
|
||||
*/
|
||||
public class MetricsClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
|
||||
/**
|
||||
* The interceptor writes to a Servo MonitorRegistry, which we get away with for now
|
||||
* because our Spectator implementation is underpinned by a ServoRegistry. When Spring
|
||||
* Boot (Actuator) provides a more general purpose abstraction for dimensional metrics
|
||||
* systems, this can be moved there and rewritten against that abstraction.
|
||||
*/
|
||||
private final ServoMonitorCache servoMonitorCache;
|
||||
|
||||
private final Collection<MetricsTagProvider> tagProviders;
|
||||
|
||||
private final 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,
|
||||
ClientHttpRequestExecution execution) throws IOException {
|
||||
long startTime = System.nanoTime();
|
||||
|
||||
ClientHttpResponse response = null;
|
||||
try {
|
||||
response = execution.execute(request, body);
|
||||
return response;
|
||||
}
|
||||
finally {
|
||||
SmallTagMap.Builder builder = SmallTagMap.builder();
|
||||
for (MetricsTagProvider tagProvider : tagProviders) {
|
||||
for (Map.Entry<String, String> tag : tagProvider
|
||||
.clientHttpRequestTags(request, response).entrySet()) {
|
||||
builder.add(Tags.newTag(tag.getKey(), tag.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
MonitorConfig.Builder monitorConfigBuilder = MonitorConfig
|
||||
.builder(metricName);
|
||||
monitorConfigBuilder.withTags(builder);
|
||||
|
||||
servoMonitorCache.getTimer(monitorConfigBuilder.build())
|
||||
.record(System.nanoTime() - startTime, TimeUnit.NANOSECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
+98
@@ -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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.metrics;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
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.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
|
||||
import com.netflix.servo.MonitorRegistry;
|
||||
import com.netflix.servo.monitor.MonitorConfig;
|
||||
import com.netflix.servo.tag.SmallTagMap;
|
||||
import com.netflix.servo.tag.Tags;
|
||||
|
||||
import static org.springframework.web.context.request.RequestAttributes.SCOPE_REQUEST;
|
||||
|
||||
/**
|
||||
* Intercepts incoming HTTP requests and records metrics about execution time and results.
|
||||
*
|
||||
* @author Jon Schneider
|
||||
*/
|
||||
public class MetricsHandlerInterceptor extends HandlerInterceptorAdapter {
|
||||
@Value("${netflix.metrics.rest.metricName:rest}")
|
||||
String metricName;
|
||||
|
||||
@Value("${netflix.metrics.rest.callerHeader:#{null}}")
|
||||
String callerHeader;
|
||||
|
||||
@Autowired
|
||||
MonitorRegistry registry;
|
||||
|
||||
@Autowired
|
||||
ServoMonitorCache servoMonitorCache;
|
||||
|
||||
@Autowired
|
||||
Collection<MetricsTagProvider> tagProviders;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler) throws Exception {
|
||||
RequestContextHolder.getRequestAttributes().setAttribute("requestStartTime",
|
||||
System.nanoTime(), SCOPE_REQUEST);
|
||||
return super.preHandle(request, response, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler, Exception ex) throws Exception {
|
||||
RequestContextHolder.getRequestAttributes().setAttribute("exception", ex,
|
||||
SCOPE_REQUEST);
|
||||
Long startTime = (Long) RequestContextHolder.getRequestAttributes().getAttribute(
|
||||
"requestStartTime", SCOPE_REQUEST);
|
||||
if (startTime != null)
|
||||
recordMetric(request, response, handler, startTime);
|
||||
super.afterCompletion(request, response, handler, ex);
|
||||
}
|
||||
|
||||
protected void recordMetric(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler, Long startTime) {
|
||||
String caller = null;
|
||||
if (callerHeader != null) {
|
||||
caller = request.getHeader(callerHeader);
|
||||
}
|
||||
|
||||
SmallTagMap.Builder builder = SmallTagMap.builder();
|
||||
for (MetricsTagProvider tagProvider : tagProviders) {
|
||||
Map<String, String> tags = tagProvider.httpRequestTags(request, response,
|
||||
handler, caller);
|
||||
for (Map.Entry<String, String> tag : tags.entrySet()) {
|
||||
builder.add(Tags.newTag(tag.getKey(), tag.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
MonitorConfig.Builder monitorConfigBuilder = MonitorConfig.builder(metricName);
|
||||
monitorConfigBuilder.withTags(builder);
|
||||
|
||||
servoMonitorCache.getTimer(monitorConfigBuilder.build()).record(
|
||||
System.nanoTime() - startTime, TimeUnit.NANOSECONDS);
|
||||
}
|
||||
}
|
||||
+131
@@ -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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.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.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;
|
||||
|
||||
/**
|
||||
* @author Jon Schneider
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(value = "spring.cloud.netflix.metrics.enabled", havingValue = "true", matchIfMissing = true)
|
||||
@ConditionalOnClass({ Monitors.class, MetricReader.class })
|
||||
public class MetricsInterceptorConfiguration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication
|
||||
@ConditionalOnClass(WebMvcConfigurerAdapter.class)
|
||||
static class MetricsWebResourceConfiguration extends WebMvcConfigurerAdapter {
|
||||
@Bean
|
||||
MetricsHandlerInterceptor servoMonitoringWebResourceInterceptor() {
|
||||
return new MetricsHandlerInterceptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(servoMonitoringWebResourceInterceptor());
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass({ RestTemplate.class, JoinPoint.class })
|
||||
@ConditionalOnProperty(value = "spring.aop.enabled", havingValue = "true", matchIfMissing = true)
|
||||
static class MetricsRestTemplateAspectConfiguration {
|
||||
|
||||
@Bean
|
||||
RestTemplateUrlTemplateCapturingAspect restTemplateUrlTemplateCapturingAspect() {
|
||||
return new RestTemplateUrlTemplateCapturingAspect();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass({ RestTemplate.class, HttpServletRequest.class }) // HttpServletRequest implicitly required by MetricsTagProvider
|
||||
static class MetricsRestTemplateConfiguration {
|
||||
|
||||
@Value("${netflix.metrics.restClient.metricName:restclient}")
|
||||
String metricName;
|
||||
|
||||
@Bean
|
||||
MetricsClientHttpRequestInterceptor spectatorLoggingClientHttpRequestInterceptor(
|
||||
Collection<MetricsTagProvider> tagProviders,
|
||||
ServoMonitorCache servoMonitorCache) {
|
||||
return new MetricsClientHttpRequestInterceptor(tagProviders,
|
||||
servoMonitorCache, this.metricName);
|
||||
}
|
||||
|
||||
@Bean
|
||||
BeanPostProcessor spectatorRestTemplateInterceptorPostProcessor() {
|
||||
return new MetricsInterceptorPostProcessor();
|
||||
}
|
||||
|
||||
private static class MetricsInterceptorPostProcessor
|
||||
implements BeanPostProcessor, ApplicationContextAware {
|
||||
private ApplicationContext context;
|
||||
private MetricsClientHttpRequestInterceptor interceptor;
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) {
|
||||
if (bean instanceof RestTemplate) {
|
||||
if (this.interceptor == null) {
|
||||
this.interceptor = this.context
|
||||
.getBean(MetricsClientHttpRequestInterceptor.class);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context)
|
||||
throws BeansException {
|
||||
this.context = context;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.metrics;
|
||||
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
|
||||
/**
|
||||
* @author Jon Schneider
|
||||
*/
|
||||
public interface MetricsTagProvider {
|
||||
/**
|
||||
* @param request RestTemplate client HTTP request
|
||||
* @param response may be null in the event of a client error
|
||||
* @return a map of tags added to every client HTTP request metric
|
||||
*/
|
||||
Map<String, String> clientHttpRequestTags(HttpRequest request,
|
||||
ClientHttpResponse response);
|
||||
|
||||
/**
|
||||
* @param request HTTP request
|
||||
* @param response HTTP response
|
||||
* @param handler the request method that is responsible for handling the request
|
||||
* @return a map of tags added to every Spring MVC HTTP request metric
|
||||
*/
|
||||
Map<String, String> httpRequestTags(HttpServletRequest request,
|
||||
HttpServletResponse response, Object handler, String caller);
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.metrics;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
|
||||
/**
|
||||
* @author Jon Schneider
|
||||
*/
|
||||
public class MetricsTagProviderAdapter implements MetricsTagProvider {
|
||||
@Override
|
||||
public Map<String, String> clientHttpRequestTags(HttpRequest request,
|
||||
ClientHttpResponse response) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> httpRequestTags(HttpServletRequest request,
|
||||
HttpServletResponse response, Object handler, String caller) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.metrics;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
|
||||
/**
|
||||
* Captures the still-templated URI because currently the ClientHttpRequestInterceptor
|
||||
* currently only gives us the means to retrieve the substituted URI.
|
||||
*
|
||||
* @author Jon Schneider
|
||||
*/
|
||||
@Aspect
|
||||
public class RestTemplateUrlTemplateCapturingAspect {
|
||||
@Around("execution(* org.springframework.web.client.RestOperations+.*(String, ..))")
|
||||
public Object captureUrlTemplate(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
try {
|
||||
String urlTemplate = (String) joinPoint.getArgs()[0];
|
||||
RestTemplateUrlTemplateHolder.setRestTemplateUrlTemplate(urlTemplate);
|
||||
return joinPoint.proceed();
|
||||
}
|
||||
finally {
|
||||
RestTemplateUrlTemplateHolder.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user