Compare commits
556 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
630f18792a | ||
|
|
d0cb04a224 | ||
|
|
0e8ac2f43c | ||
|
|
0c60700d82 | ||
|
|
dc285b6c2f | ||
|
|
74009b38c4 | ||
|
|
e820be1de9 | ||
|
|
7db3cbd03d | ||
|
|
27d2433993 | ||
|
|
64eda4cdf7 | ||
|
|
f05961cf07 | ||
|
|
be713c9ccf | ||
|
|
00dc3ee945 | ||
|
|
927007774b | ||
|
|
f4994f66c9 | ||
|
|
aef973a4d9 | ||
|
|
ba618ceba0 | ||
|
|
7f22eca8dc | ||
|
|
862018b63f | ||
|
|
f38143ff7b | ||
|
|
481cde05bf | ||
|
|
a1e6413704 | ||
|
|
8d1b9a18b4 | ||
|
|
6e21fe56e9 | ||
|
|
f0ac042b3c | ||
|
|
13a4264488 | ||
|
|
7ffb65cc38 | ||
|
|
2464c360c0 | ||
|
|
1917ccf35c | ||
|
|
4f9c14da97 | ||
|
|
58e23c1b8c | ||
|
|
34156fc092 | ||
|
|
86fde56129 | ||
|
|
d997d3a7bb | ||
|
|
4e689855f4 | ||
|
|
eace16d7fd | ||
|
|
53b7f9b26f | ||
|
|
983c667725 | ||
|
|
96fd660d46 | ||
|
|
7d1770da8a | ||
|
|
b83f6867f3 | ||
|
|
fa7db42f50 | ||
|
|
80c07a5c5b | ||
|
|
59a4c85aeb | ||
|
|
7fc01d38e8 | ||
|
|
3010422f4e | ||
|
|
1f78bb1a98 | ||
|
|
1d7f32efe4 | ||
|
|
1e3d980437 | ||
|
|
8654b5eef8 | ||
|
|
5781ad1de0 | ||
|
|
e2a1fa15d3 | ||
|
|
50da7413d8 | ||
|
|
e45b945024 | ||
|
|
f3668a77ef | ||
|
|
2aee6cab20 | ||
|
|
6233a0b960 | ||
|
|
0ae469d8e2 | ||
|
|
bcad47ebd5 | ||
|
|
71d179b8c3 | ||
|
|
af35e5100e | ||
|
|
96c97c7dd2 | ||
|
|
d5a41fc8b6 | ||
|
|
c3c3afb3c3 | ||
|
|
4f32dcda16 | ||
|
|
67717e73ad | ||
|
|
1753734581 | ||
|
|
3e408e535c | ||
|
|
f154292cb5 | ||
|
|
ac261533c3 | ||
|
|
1982c984ee | ||
|
|
fb5f82c44f | ||
|
|
840cea06db | ||
|
|
3237e2488e | ||
|
|
98e1e40e18 | ||
|
|
def3fc7b2e | ||
|
|
6057b8f90a | ||
|
|
2847f761bb | ||
|
|
b0a2d2ad80 | ||
|
|
564b814f77 | ||
|
|
433585a231 | ||
|
|
6a4750e302 | ||
|
|
748a6f11c1 | ||
|
|
bdd9a82682 | ||
|
|
c551c157d1 | ||
|
|
7e777229ba | ||
|
|
612bfcba37 | ||
|
|
c44e00a100 | ||
|
|
7f46b3194a | ||
|
|
50b7999dee | ||
|
|
871f84b1ff | ||
|
|
4018a95af0 | ||
|
|
ffe3ff5830 | ||
|
|
9b9b0ab10e | ||
|
|
b19e5fe839 | ||
|
|
f6caf41960 | ||
|
|
67d57de166 | ||
|
|
4d79f17a3a | ||
|
|
245d6d6fc7 | ||
|
|
66bd1feadf | ||
|
|
c02e3549eb | ||
|
|
210b8dcf9d | ||
|
|
bbefa788fd | ||
|
|
2f6eb0908e | ||
|
|
51e4d44fc1 | ||
|
|
ed502daeb9 | ||
|
|
d7da527bd5 | ||
|
|
7c5146bc97 | ||
|
|
91cdf9990b | ||
|
|
7d03609462 | ||
|
|
7be9a06e76 | ||
|
|
3eaecacac0 | ||
|
|
9c5e23090c | ||
|
|
c7b05e530d | ||
|
|
889fa25e58 | ||
|
|
da6945224f | ||
|
|
026a727480 | ||
|
|
2404f547ab | ||
|
|
a9dd82dcf4 | ||
|
|
95443be313 | ||
|
|
5d1805b10e | ||
|
|
72eb175dc3 | ||
|
|
d5cbcaaa15 | ||
|
|
bfa722f6da | ||
|
|
6b9d35983a | ||
|
|
6cf50c7cc9 | ||
|
|
cf05851752 | ||
|
|
1cbbf17392 | ||
|
|
96afeb19b2 | ||
|
|
56da72b16e | ||
|
|
b781868876 | ||
|
|
6a05f4b497 | ||
|
|
b9fa617374 | ||
|
|
c59baee1f5 | ||
|
|
98531d6b2f | ||
|
|
8df5b45525 | ||
|
|
ebba966d52 | ||
|
|
169f35d41f | ||
|
|
8bec9e88a7 | ||
|
|
854aa0153f | ||
|
|
77eaaae6ce | ||
|
|
fd7badec0a | ||
|
|
a834b6ea21 | ||
|
|
31fad72172 | ||
|
|
ef14195e71 | ||
|
|
a2fab32356 | ||
|
|
2dd0ecfa49 | ||
|
|
849fe15728 | ||
|
|
76afab3c78 | ||
|
|
21250794f5 | ||
|
|
9f85ceb2ed | ||
|
|
b6ecdef0c5 | ||
|
|
4dfd528533 | ||
|
|
3131601477 | ||
|
|
5921cf5f0d | ||
|
|
788f7d5eb1 | ||
|
|
79cb79481e | ||
|
|
41f0061dca | ||
|
|
f981e4f886 | ||
|
|
ebf30e488a | ||
|
|
7ab889baac | ||
|
|
8d9d893839 | ||
|
|
205233f8c9 | ||
|
|
6ee9527ed9 | ||
|
|
ee56ffa346 | ||
|
|
3fcc478356 | ||
|
|
26e4cd2f4a | ||
|
|
509524144d | ||
|
|
676285e20b | ||
|
|
43edb0f814 | ||
|
|
1c684a4e32 | ||
|
|
156af0e2a4 | ||
|
|
28d9c465fd | ||
|
|
a285510656 | ||
|
|
d6042d142e | ||
|
|
be8d368e72 | ||
|
|
75698d9390 | ||
|
|
9d343694cc | ||
|
|
fddb2a3be0 | ||
|
|
3b44eddbb9 | ||
|
|
ba4100e22d | ||
|
|
72155e31dd | ||
|
|
60903c844e | ||
|
|
5c810b5ff6 | ||
|
|
e3a8c2d392 | ||
|
|
1f27d5f3e3 | ||
|
|
abb790da54 | ||
|
|
f4a2f2af94 | ||
|
|
29a21d060b | ||
|
|
de19861974 | ||
|
|
e5766e31dc | ||
|
|
fca727ecaf | ||
|
|
d009c9b2ed | ||
|
|
fd2e84f781 | ||
|
|
2b7676449d | ||
|
|
4b2c6eba63 | ||
|
|
84147280cf | ||
|
|
e62d9f8201 | ||
|
|
4cec4e64f9 | ||
|
|
9eb647428c | ||
|
|
b68808b325 | ||
|
|
a9dce0efbf | ||
|
|
de63f81238 | ||
|
|
a05afe45c0 | ||
|
|
236996caa1 | ||
|
|
b808c93885 | ||
|
|
7865b74359 | ||
|
|
143e1880d4 | ||
|
|
1be467cfe9 | ||
|
|
7f8f157616 | ||
|
|
c140ad6828 | ||
|
|
50e41dba25 | ||
|
|
c73fc5f266 | ||
|
|
a5f3adc477 | ||
|
|
b77764c1cf | ||
|
|
714b188560 | ||
|
|
47315f188a | ||
|
|
384a8a3991 | ||
|
|
7a80c00439 | ||
|
|
43486ce1e2 | ||
|
|
627c1e953e | ||
|
|
da141cc6bd | ||
|
|
8f3a9a6297 | ||
|
|
e8d496b0ad | ||
|
|
f5accfbe8f | ||
|
|
16e291036e | ||
|
|
a79b8b77b5 | ||
|
|
8e2d6acdf8 | ||
|
|
70c5123f7a | ||
|
|
7cfbaee199 | ||
|
|
fa8d78e18f | ||
|
|
4f5e72a31a | ||
|
|
7c5822fb7c | ||
|
|
60df140bfd | ||
|
|
f41e905842 | ||
|
|
9824fe6da3 | ||
|
|
1fb37f4b67 | ||
|
|
a8a6861a21 | ||
|
|
614222bdc3 | ||
|
|
8e98dda795 | ||
|
|
3686306505 | ||
|
|
d29aa78022 | ||
|
|
85fa406e1d | ||
|
|
d501e2016f | ||
|
|
cf0cd66ac9 | ||
|
|
c921f657b1 | ||
|
|
c93c1d84c5 | ||
|
|
2e66b1a546 | ||
|
|
48629146ed | ||
|
|
a6c8502416 | ||
|
|
fc74dc33c1 | ||
|
|
73de9dfc33 | ||
|
|
68d78a4aa4 | ||
|
|
b618e0e884 | ||
|
|
2d1a6e0edc | ||
|
|
76a7e92787 | ||
|
|
a2178b9a5f | ||
|
|
dcf0fecdb0 | ||
|
|
f88db8abf9 | ||
|
|
0c8b0f5e19 | ||
|
|
b794872147 | ||
|
|
86614f035b | ||
|
|
5f3a472c5e | ||
|
|
7eb2a405d3 | ||
|
|
1d030183bb | ||
|
|
d211692306 | ||
|
|
86c7d95c80 | ||
|
|
e99d43b25c | ||
|
|
730fc0a911 | ||
|
|
1ea92d7165 | ||
|
|
0dfc0ca853 | ||
|
|
474f82b3cf | ||
|
|
ae80109c77 | ||
|
|
807fe63cbe | ||
|
|
44ed3259f9 | ||
|
|
93b2b5fab8 | ||
|
|
58e807439c | ||
|
|
ca2f34bfac | ||
|
|
b8719d7f76 | ||
|
|
be0fdd9045 | ||
|
|
1d2b583c0c | ||
|
|
9def35e2e1 | ||
|
|
a11127ea58 | ||
|
|
af80864e08 | ||
|
|
f6a4fbdf87 | ||
|
|
61f48be2a6 | ||
|
|
8516925537 | ||
|
|
f828ec3d08 | ||
|
|
1eb1706166 | ||
|
|
ffd3b6daf6 | ||
|
|
76d538264a | ||
|
|
02d0a06195 | ||
|
|
c43325e402 | ||
|
|
63bc4b2d00 | ||
|
|
6e5da92263 | ||
|
|
6358719255 | ||
|
|
bdfd92299a | ||
|
|
aa4c9896ba | ||
|
|
1549f1feed | ||
|
|
2a9a713f3e | ||
|
|
0377dac71f | ||
|
|
13217a11c0 | ||
|
|
146b81ee5e | ||
|
|
8502792aad | ||
|
|
3d9447327a | ||
|
|
288fc67621 | ||
|
|
d4bc751a20 | ||
|
|
4cf160e5d3 | ||
|
|
98f29d6a6e | ||
|
|
92bb0a561b | ||
|
|
1329f6f0e1 | ||
|
|
a3dbfaba88 | ||
|
|
2032f39bf9 | ||
|
|
8840415739 | ||
|
|
bdf2fd21c3 | ||
|
|
bd1e326404 | ||
|
|
ebd31bc3d3 | ||
|
|
7185191d91 | ||
|
|
1a1dd44f34 | ||
|
|
95fd147268 | ||
|
|
88657fdf63 | ||
|
|
03de684940 | ||
|
|
b445b99572 | ||
|
|
bd4343c5d8 | ||
|
|
a58c13cd9f | ||
|
|
cf23ecc12c | ||
|
|
86754c2324 | ||
|
|
e6f0a8fdf3 | ||
|
|
fa9fc531d0 | ||
|
|
30bf2c1ad3 | ||
|
|
d326da9344 | ||
|
|
8f6494163d | ||
|
|
490cabefb2 | ||
|
|
66ff86d6fb | ||
|
|
0e5479d55e | ||
|
|
3eb3e0104d | ||
|
|
079e6116b2 | ||
|
|
b6a3adb21c | ||
|
|
ecf831c0f5 | ||
|
|
adfa0be79e | ||
|
|
7ec91802b2 | ||
|
|
1e15eb31c7 | ||
|
|
8d5e61a9a2 | ||
|
|
64500b113a | ||
|
|
a7e5fad571 | ||
|
|
531b00b6fd | ||
|
|
ce3fc5792b | ||
|
|
a477b2fb73 | ||
|
|
6a5d121615 | ||
|
|
90960e0574 | ||
|
|
b6980f7cf8 | ||
|
|
2fb3472de0 | ||
|
|
6dacbafc29 | ||
|
|
f1fcbd7396 | ||
|
|
681e389f19 | ||
|
|
06b7274364 | ||
|
|
b4f7de4858 | ||
|
|
6261232ce6 | ||
|
|
eb35b90950 | ||
|
|
958cea4e0d | ||
|
|
7f184bae50 | ||
|
|
3d68103a19 | ||
|
|
612851bce0 | ||
|
|
e365dae9e4 | ||
|
|
b6d5fc02dc | ||
|
|
59d139ace2 | ||
|
|
1d7a72f992 | ||
|
|
45f2ce261e | ||
|
|
f79e49db5a | ||
|
|
218aebaa0f | ||
|
|
f11870d6bc | ||
|
|
ebea6bb92c | ||
|
|
4806d6ada0 | ||
|
|
6909748bda | ||
|
|
982fc8440c | ||
|
|
e4df07a00c | ||
|
|
0ee30aca7e | ||
|
|
b994bcbcfb | ||
|
|
cc23b44409 | ||
|
|
de36ef697d | ||
|
|
e0d224a330 | ||
|
|
adc2106bec | ||
|
|
6cdbe957a7 | ||
|
|
14808cb01c | ||
|
|
1edadbfee8 | ||
|
|
043a23ecdf | ||
|
|
383ef37989 | ||
|
|
9cd9a8d3ea | ||
|
|
db8c94f433 | ||
|
|
22fb3e5bef | ||
|
|
6329722f17 | ||
|
|
2dee61223d | ||
|
|
f83fc52e9c | ||
|
|
2f8a04c15d | ||
|
|
a1501af7a7 | ||
|
|
a1fdfc9cc9 | ||
|
|
2dddf3c2a5 | ||
|
|
84061cf60f | ||
|
|
557c2c7858 | ||
|
|
cb2d355bc9 | ||
|
|
ed1b642d5b | ||
|
|
411f5e1951 | ||
|
|
450506ca96 | ||
|
|
bf700aa88f | ||
|
|
c108ab196c | ||
|
|
cac8662c01 | ||
|
|
197c58ef98 | ||
|
|
d4c2723759 | ||
|
|
c34a6d8f49 | ||
|
|
5de580c00b | ||
|
|
ed1fb151d7 | ||
|
|
4c4913c5d0 | ||
|
|
adb0dc5963 | ||
|
|
740bc8ae31 | ||
|
|
30a0b12020 | ||
|
|
b0b8b0db3b | ||
|
|
71244732f2 | ||
|
|
820af60c19 | ||
|
|
b3252359bd | ||
|
|
9ade568597 | ||
|
|
cf5224a5f6 | ||
|
|
6c4a83d14d | ||
|
|
0439b95139 | ||
|
|
72a618a0b7 | ||
|
|
0f7520e67b | ||
|
|
fcd69474a2 | ||
|
|
3130e00bab | ||
|
|
1c063b57ec | ||
|
|
efeff3b720 | ||
|
|
ea15634f47 | ||
|
|
e744d001c6 | ||
|
|
06e486e5e2 | ||
|
|
95180ffdef | ||
|
|
e5bd85e9aa | ||
|
|
ec4ec2ff77 | ||
|
|
5a1c9215bc | ||
|
|
53457b73ad | ||
|
|
8cb9fcf01a | ||
|
|
2c88b0e75e | ||
|
|
4daffdc31f | ||
|
|
16dec2cde5 | ||
|
|
82e5fb593e | ||
|
|
4a60ecb3ff | ||
|
|
38307405ed | ||
|
|
fe332d0cbd | ||
|
|
e45f75c1d6 | ||
|
|
a77f450757 | ||
|
|
9f8e946ea3 | ||
|
|
7633687665 | ||
|
|
a1e37643d0 | ||
|
|
8440b58d6c | ||
|
|
a97f8efe21 | ||
|
|
d83e7c22ac | ||
|
|
01a5b32b22 | ||
|
|
8834f57582 | ||
|
|
57fd7b96b4 | ||
|
|
14fe2fbc84 | ||
|
|
0b86edb654 | ||
|
|
07ab74902c | ||
|
|
e4c6e860c1 | ||
|
|
02cf587fcf | ||
|
|
081773163b | ||
|
|
292ff63699 | ||
|
|
f1a97c0219 | ||
|
|
e545010034 | ||
|
|
ef57fa9a25 | ||
|
|
1bd0d891a4 | ||
|
|
9a51c1de1d | ||
|
|
e4bb75dd8e | ||
|
|
66e1e08ae2 | ||
|
|
708cd5a644 | ||
|
|
138c9aba97 | ||
|
|
e5e17b4496 | ||
|
|
cadaebefb9 | ||
|
|
e2fb7935f6 | ||
|
|
16b3dab600 | ||
|
|
758972890e | ||
|
|
a88285d935 | ||
|
|
d7832fb931 | ||
|
|
f073c9a0bc | ||
|
|
63ef83ec62 | ||
|
|
51c50415e1 | ||
|
|
2f08fa6476 | ||
|
|
3091972fc5 | ||
|
|
8e14df9a95 | ||
|
|
683cec6f4d | ||
|
|
ec6e5aa3b1 | ||
|
|
f4c00a9b6f | ||
|
|
c071ec5d6c | ||
|
|
29148c4b42 | ||
|
|
7340caa4a9 | ||
|
|
7d23dfa73d | ||
|
|
19932820a9 | ||
|
|
6b59c54087 | ||
|
|
58f5ee999a | ||
|
|
32e8c033ea | ||
|
|
84a96f750f | ||
|
|
6560e71e48 | ||
|
|
ee8a18a528 | ||
|
|
c36fc36964 | ||
|
|
a34f85bfc9 | ||
|
|
6f00efa077 | ||
|
|
469dff8478 | ||
|
|
c50eb79b1d | ||
|
|
985bf6ed25 | ||
|
|
bd51696e08 | ||
|
|
2764d59a4f | ||
|
|
c1509bb712 | ||
|
|
b14f32deb3 | ||
|
|
62145a2aad | ||
|
|
8fb88a57b2 | ||
|
|
44f12ae003 | ||
|
|
bf97ef3a08 | ||
|
|
aeefc3b8a2 | ||
|
|
5763718816 | ||
|
|
3b142b7504 | ||
|
|
0f52d32424 | ||
|
|
f5f3c48f38 | ||
|
|
9f9f5aa000 | ||
|
|
be4ca0287b | ||
|
|
a84ed72f28 | ||
|
|
8eb5f56f42 | ||
|
|
df1aa2b379 | ||
|
|
da2befcb97 | ||
|
|
92d0f7e796 | ||
|
|
6aa3949d05 | ||
|
|
fa833d9224 | ||
|
|
c9261f8475 | ||
|
|
a1df62af08 | ||
|
|
aa12413f4e | ||
|
|
2ccd9ba10f | ||
|
|
81ae68c571 | ||
|
|
51b45f4713 | ||
|
|
66e1f58879 | ||
|
|
06bb5180cf | ||
|
|
d83d7f879c | ||
|
|
b8e92c949e | ||
|
|
91548a0ca9 | ||
|
|
dbca25cd54 | ||
|
|
539ea15ae5 | ||
|
|
ad1d9f33d4 | ||
|
|
df98c1a58e | ||
|
|
56d1969c96 | ||
|
|
4b341436af | ||
|
|
4d3d73b4c1 | ||
|
|
55ab279e7f | ||
|
|
82ab994608 | ||
|
|
1c4035e677 | ||
|
|
afdc9e7207 | ||
|
|
9b83abb06a | ||
|
|
26d1a10bd0 | ||
|
|
7e55dcc46d | ||
|
|
45802d2403 | ||
|
|
3660a2a4e5 | ||
|
|
bed81bd93d | ||
|
|
131855cdc5 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -10,3 +10,6 @@
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
/target/
|
||||
/.idea/
|
||||
/redkale.iml
|
||||
|
||||
20
README.md
20
README.md
@@ -1,27 +1,27 @@
|
||||
<h1>项目介绍</h1>
|
||||
<b>项目介绍</b>
|
||||
<p>
|
||||
Redkale (中文名: 红菜苔,一种湖北特产蔬菜) 是基于Java 8全新的微服务框架, 包含HTTP、WebSocket、TCP/UDP、数据序列化、数据缓存、依赖注入等功能。 本框架致力于简化集中式和微服务架构的开发,在增强开发敏捷性的同时保持高性能。
|
||||
Redkale (中文名: 红菜苔,一种湖北特产蔬菜) 是基于Java 11全新的微服务框架, 包含HTTP、WebSocket、TCP/UDP、数据序列化、数据缓存、依赖注入等功能。 本框架致力于简化集中式和微服务架构的开发,在增强开发敏捷性的同时保持高性能。
|
||||
</p>
|
||||
<strong>RedKale 有如下主要特点:</strong>
|
||||
<ol>
|
||||
<li>大量使用Java 8新特性(接口默认值、Stream、Lambda、JDk8内置的ASM等)</li>
|
||||
<li>大量使用Java 8+新特性(接口默认值、Stream、Lambda、内置的ASM、HttpClient等)</li>
|
||||
<li>提供HTTP服务,同时内置JSON功能与限时缓存功能</li>
|
||||
<li>TCP层完全使用NIO.2,并统一TCP与UDP的接口换</li>
|
||||
<li>TCP层完全使用NIO,并统一TCP与UDP的接口换</li>
|
||||
<li>提供分布式与集中式部署的无缝切换</li>
|
||||
<li>提供类似JPA功能,包含数据缓存自动同步、分表分库与简洁的数据层操作接口</li>
|
||||
<li>可以动态修改已依赖注入的资源</li>
|
||||
</ol>
|
||||
</ol>
|
||||
|
||||
<strong>Redkale 设计理念</strong>
|
||||
<p>
|
||||
作为一个全新的微服务框架,Redkale在接口定义上使用了Java 8大量的新语法,接口有默认实现、接口带静态方法、重复注解等特性,同时在设计上与主流框架有很大不同。Redkale是按组件形式设计的,而非以容器为主,几乎每个子包都是能提供独立功能的组件。如Tomcat是按容器设计的,所有web资源/配置由Tomcat控制,开发者很能难控制到Tomcat内部,而Redkale的HTTP服务只是个组件,开发者既可以自己启动和配置HttpServer,也可以把Redkale当成容器通过Redkale进程来初始化服务。Spring的Ioc容器也是如此,Redkale提供的依赖注入仅通过ResouceFactory一个类来控制,非常轻量,并且可动态更改已注入的资源。Spring提倡控制反转思想,而自身的容器却让开发者很难控制。Redkale是一个既能以组件形式也能以容器形式存在的框架。从整体上看,Redkale的架构分两层:接口和默认实现。开发者若想替换掉Redkale内置的HTTP服务而使用符合JavaEE规范的HttpServlet, 可以采用自定义协议基于JSR 340(Servlet 3.1)来实现自己的HTTP服务;若想使用Hibernate作为数据库操作,可以写一个自己的DataSource实现类;JSON的序列化和反序列化也可以使用第三方的实现;Memcached或Redis也可以作为另一个CacheSource的实现替换Redkale的默认实现。这其实包含了控制反转的思想,让框架里的各个组件均可让开发者控制。<br/>
|
||||
与主流框架比,功能上Redkale显得很简单,这体现了Redkale的简易性,而并非是不足,从一个良好的设计习惯或架构上来看,有些常用功能是不需要提供的,如Redkale的HTTP服务不支持HTTPS和JSP,HTTPS比HTTP多了一层加密解密,这种密集型的计算不是Java的专长,通常提供HTTP服务的架构不会将Java动态服务器放在最前端,而是在前方会放nginx或apache,除了负载均衡还能静动分离,因此HTTPS的加解密应交给nginx这样的高性能服务器处理。Redkale再提供HTTPS服务就显得鸡肋。JSP其实算是一个落后的技术,现在是一个多样化终端的时代,终端不只局限于桌面程序和PC浏览器,还有原生App、混合式App、微信端、移动H5、提供第三方接口等各种形式的终端,这些都不是JSP能方便兼顾的,而HTTP+JSON作为通用性接口可以避免重复开发,模版引擎的功能加上各种强大的JS框架足以取代JSP。Redkale在功能上做了筛选,不会为了迎合主流而提供,而是以良好的设计思想为指导。这是Redkale的主导思维。
|
||||
作为一个全新的微服务框架,Redkale在接口定义上使用了Java 8以上版本的大量新特性,接口有默认实现、接口带静态方法、重复注解等特性,同时在设计上与主流框架有很大不同。Redkale是按组件形式设计的,而非以容器为主,几乎每个子包都是能提供独立功能的组件。如Tomcat是按容器设计的,所有web资源/配置由Tomcat控制,开发者很能难控制到Tomcat内部,而Redkale的HTTP服务只是个组件,开发者既可以自己启动和配置HttpServer,也可以把Redkale当成容器通过Redkale进程来初始化服务。Spring的Ioc容器也是如此,Redkale提供的依赖注入仅通过ResouceFactory一个类来控制,非常轻量,并且可动态更改已注入的资源。Spring提倡控制反转思想,而自身的容器却让开发者很难控制。Redkale是一个既能以组件形式也能以容器形式存在的框架。从整体上看,Redkale的架构分两层:接口和默认实现。开发者若想替换掉Redkale内置的HTTP服务而使用符合JavaEE规范的HttpServlet, 可以采用自定义协议基于JSR 340(Servlet 3.1)来实现自己的HTTP服务;若想使用Hibernate作为数据库操作,可以写一个自己的DataSource实现类;JSON的序列化和反序列化也可以使用第三方的实现;Memcached或Redis也可以作为另一个CacheSource的实现替换Redkale的默认实现。这其实包含了控制反转的思想,让框架里的各个组件均可让开发者控制。<br/>
|
||||
与主流框架比,功能上Redkale显得很简单,这体现了Redkale的简易性,而并非是不足,从一个良好的设计习惯或架构上来看,有些常用功能是不需要提供的,如Redkale的HTTP服务不支持JSP, JSP其实算是一个落后的技术,现在是一个多样化终端的时代,终端不只局限于桌面程序和PC浏览器,还有原生App、混合式App、微信端、移动H5、提供第三方接口等各种形式的终端,这些都不是JSP能方便兼顾的,而HTTP+JSON作为通用性接口可以避免重复开发,模版引擎的功能加上各种强大的JS框架足以取代JSP。Redkale在功能上做了筛选,不会为了迎合主流而提供,而是以良好的设计思想为指导。这是Redkale的主导思维。
|
||||
</p>
|
||||
|
||||
|
||||
<h5>详情请访问: <a href='https://redkale.org' target='_blank'>https://redkale.org</a></h5>
|
||||
<b>详情请访问: <a href='https://redkale.org' target='_blank'>https://redkale.org</a></b>
|
||||
|
||||
<h5>基本文档: <a href='https://redkale.org/articles.html' target='_blank'>https://redkale.org/articles.html</a></h5>
|
||||
<b>基本文档: <a href='https://redkale.org/articles.html' target='_blank'>https://redkale.org/articles.html</a></b>
|
||||
|
||||
<h5>欢迎加入Redkale QQ群: 527523235</h5>
|
||||
<b>欢迎加入Redkale QQ群: 527523235</b>
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
@ECHO OFF
|
||||
|
||||
SET APP_HOME=%~dp0
|
||||
|
||||
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
|
||||
|
||||
java -DCMD=APIDOC -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application
|
||||
7
bin/apidoc.cmd
Normal file
7
bin/apidoc.cmd
Normal file
@@ -0,0 +1,7 @@
|
||||
@ECHO OFF
|
||||
|
||||
SET APP_HOME=%~dp0
|
||||
|
||||
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
|
||||
|
||||
java -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application apidoc
|
||||
@@ -15,4 +15,4 @@ do
|
||||
done
|
||||
export CLASSPATH=$CLASSPATH:$lib
|
||||
echo "$APP_HOME"
|
||||
java -DCMD=APIDOC -DAPP_HOME="$APP_HOME" org.redkale.boot.Application
|
||||
java -DAPP_HOME="$APP_HOME" org.redkale.boot.Application apidoc
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
@ECHO OFF
|
||||
|
||||
SET APP_HOME=%~dp0
|
||||
|
||||
|
||||
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
|
||||
|
||||
java -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application
|
||||
|
||||
java -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application %*
|
||||
23
bin/redkale.sh
Normal file
23
bin/redkale.sh
Normal file
@@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
|
||||
export LC_ALL="zh_CN.UTF-8"
|
||||
|
||||
APP_HOME=`dirname "$0"`
|
||||
|
||||
cd "$APP_HOME"/..
|
||||
|
||||
APP_HOME=`pwd`
|
||||
|
||||
if [ ! -f "$APP_HOME"/conf/application.xml ]; then
|
||||
APP_HOME="$APP_HOME"/..
|
||||
fi
|
||||
|
||||
lib="$APP_HOME"/lib
|
||||
for jar in `ls $APP_HOME/lib/*.jar`
|
||||
do
|
||||
lib=$lib:$jar
|
||||
done
|
||||
export CLASSPATH=$CLASSPATH:$lib
|
||||
|
||||
echo "$APP_HOME"
|
||||
java -DAPP_HOME="$APP_HOME" org.redkale.boot.Application $@ &
|
||||
@@ -4,6 +4,6 @@ SET APP_HOME=%~dp0
|
||||
|
||||
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
|
||||
|
||||
call "%APP_HOME%\bin\shutdown.bat"
|
||||
call "%APP_HOME%\bin\shutdown.cmd"
|
||||
|
||||
call "%APP_HOME%\bin\start.bat"
|
||||
call "%APP_HOME%\bin\start.cmd"
|
||||
8
bin/start.cmd
Normal file
8
bin/start.cmd
Normal file
@@ -0,0 +1,8 @@
|
||||
@ECHO OFF
|
||||
|
||||
SET APP_HOME=%~dp0
|
||||
|
||||
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
|
||||
|
||||
java -server -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application
|
||||
|
||||
@@ -24,5 +24,5 @@ done
|
||||
export CLASSPATH=$CLASSPATH:$lib
|
||||
|
||||
echo "$APP_HOME"
|
||||
nohup java -DAPP_HOME="$APP_HOME" org.redkale.boot.Application > "$APP_HOME"/logs.out &
|
||||
nohup java -server -DAPP_HOME="$APP_HOME" org.redkale.boot.Application > "$APP_HOME"/logs.out &
|
||||
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<application port="2121">
|
||||
|
||||
<!-- 详细配置说明见: http://redkale.org/redkale.html#redkale_confxml -->
|
||||
|
||||
<resources>
|
||||
|
||||
<application nodeid="10000" port="2020">
|
||||
|
||||
<resources>
|
||||
<properties load="config.properties">
|
||||
<property name="system.property.redkale.convert.protobuf.enumtostring" value="true"/>
|
||||
</properties>
|
||||
</resources>
|
||||
|
||||
<server protocol="HTTP" port="6060">
|
||||
|
||||
<server protocol="HTTP" port="5050">
|
||||
<request>
|
||||
<remoteaddr value="request.headers.X-RemoteAddress"/>
|
||||
</request>
|
||||
|
||||
|
||||
<response>
|
||||
<defcookie domain="" path="/"/>
|
||||
<addheader name="Access-Control-Allow-Origin" value="request.headers.Origin" />
|
||||
@@ -24,10 +23,9 @@
|
||||
|
||||
<filters autoload="true"/>
|
||||
|
||||
<rest path="/pipes" /> <!-- base指定的自定义HttpServlet子类必须标记@HttpUserType, 不设置base则视为没有当前用户信息设置 -->
|
||||
|
||||
<servlets path="/pipes" autoload="true" />
|
||||
<rest path="/pipes" />
|
||||
|
||||
<servlets path="/pipes" autoload="true" />
|
||||
</server>
|
||||
|
||||
</application>
|
||||
|
||||
2
conf/config.properties
Normal file
2
conf/config.properties
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
#
|
||||
@@ -18,8 +18,8 @@ java.util.logging.FileHandler.level = FINER
|
||||
java.util.logging.FileHandler.limit = 10M
|
||||
java.util.logging.FileHandler.count = 20
|
||||
java.util.logging.FileHandler.encoding = UTF-8
|
||||
java.util.logging.FileHandler.pattern = ${APP_HOME}/logs-%m/log-%d.log
|
||||
java.util.logging.FileHandler.unusual = ${APP_HOME}/logs-%m/log-warnerr-%d.log
|
||||
java.util.logging.FileHandler.pattern = ${APP_HOME}/logs-%tY%tm/log-%tY%tm%td.log
|
||||
java.util.logging.FileHandler.unusual = ${APP_HOME}/logs-%tY%tm/log-warnerr-%tY%tm%td.log
|
||||
java.util.logging.FileHandler.append = true
|
||||
|
||||
java.util.logging.ConsoleHandler.level = FINEST
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
|
||||
|
||||
<persistence-unit name="" transaction-type="RESOURCE_LOCAL">
|
||||
<shared-cache-mode>ALL</shared-cache-mode>
|
||||
<properties>
|
||||
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/center?autoReconnect=true&characterEncoding=utf8"/>
|
||||
<property name="javax.persistence.jdbc.user" value="root"/>
|
||||
<property name="javax.persistence.jdbc.password" value="1234"/>
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
|
||||
<!--
|
||||
<persistence-unit name="user.read" transaction-type="RESOURCE_LOCAL">
|
||||
<shared-cache-mode>ALL</shared-cache-mode>
|
||||
<properties>
|
||||
<property name="javax.persistence.jdbc.url" value="jdbc:oracle:thin:@localhost:1521:orcl"/>
|
||||
<property name="javax.persistence.jdbc.user" value="system"/>
|
||||
<property name="javax.persistence.jdbc.password" value="1234"/>
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
|
||||
<persistence-unit name="user.write" transaction-type="RESOURCE_LOCAL">
|
||||
<shared-cache-mode>ALL</shared-cache-mode>
|
||||
<properties>
|
||||
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/center?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true"/>
|
||||
<property name="javax.persistence.jdbc.user" value="root"/>
|
||||
<property name="javax.persistence.jdbc.password" value="1234"/>
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
-->
|
||||
|
||||
</persistence>
|
||||
13
conf/source.properties
Normal file
13
conf/source.properties
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
############ DataSource @Resource(name="platf") ############
|
||||
#redkale.datasource[platf].url = jdbc:mysql://127.0.0.1:3306/platf?allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&serverTimezone=UTC&characterEncoding=utf8
|
||||
#redkale.datasource[platf].user = root
|
||||
#redkale.datasource[platf].password = 12345678
|
||||
### true: auto ddl;
|
||||
#redkale.datasource[platf].table-autoddl = true
|
||||
|
||||
|
||||
############ CacheSource @Resource(name="usersession") ############
|
||||
#redkale.cachesource[usersession].node[0].url = redis://127.0.0.1:6363
|
||||
#redkale.cachesource[usersession].node[0].password = 12345678
|
||||
#redkale.cachesource[usersession].node[0].db = 0
|
||||
@@ -1 +1 @@
|
||||
<EFBFBD><EFBFBD><EFBFBD>н<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>jarĬ<EFBFBD>Ϸ<EFBFBD><EFBFBD>ڴ˴<EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD>н<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>jarĬ<EFBFBD>Ϸ<EFBFBD><EFBFBD>ڴ˴<EFBFBD>
|
||||
18
my/gitrun.sh
Normal file
18
my/gitrun.sh
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
|
||||
export LC_ALL="zh_CN.UTF-8"
|
||||
|
||||
rm -fr redkale
|
||||
|
||||
rm -fr src
|
||||
rm -fr bin
|
||||
rm -fr conf
|
||||
|
||||
git clone https://github.com/redkale/redkale.git
|
||||
|
||||
cp -fr redkale/src ./
|
||||
cp -fr redkale/bin ./
|
||||
cp -fr redkale/conf ./
|
||||
|
||||
mvn clean
|
||||
mvn deploy
|
||||
56
my/pom.xml
56
my/pom.xml
@@ -4,16 +4,40 @@
|
||||
<groupId>org.redkale</groupId>
|
||||
<artifactId>redkale</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>RedkaleProject</name>
|
||||
<url>http://redkale.org</url>
|
||||
<description>redkale -- java framework</description>
|
||||
<version>1.6.2</version>
|
||||
<version>2.7.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
|
||||
<junit.version>5.7.0</junit.version>
|
||||
<maven-plugin.version>3.2.0</maven-plugin.version>
|
||||
<maven-gpg-plugin.version>3.0.1</maven-gpg-plugin.version>
|
||||
<maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
|
||||
<maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
|
||||
<maven-failsafe-plugin.version>3.0.0-M5</maven-failsafe-plugin.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache 2</name>
|
||||
<url>http://www.apache.org/licenses/</url>
|
||||
<url>https://www.apache.org/licenses/</url>
|
||||
<distribution>repo</distribution>
|
||||
<comments>Apache License</comments>
|
||||
</license>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<developers>
|
||||
@@ -21,13 +45,13 @@
|
||||
<id>Redkale</id>
|
||||
<name>redkale</name>
|
||||
<email>redkale@qq.com</email>
|
||||
<url>http://redkale.org</url>
|
||||
<url>https://redkale.org</url>
|
||||
<roles>
|
||||
<role>Project Manager</role>
|
||||
<role>Architect</role>
|
||||
</roles>
|
||||
<organization>redkale</organization>
|
||||
<organizationUrl>http://redkale.org</organizationUrl>
|
||||
<organizationUrl>https://redkale.org</organizationUrl>
|
||||
<properties>
|
||||
<dept>No</dept>
|
||||
</properties>
|
||||
@@ -35,12 +59,6 @@
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
<name>Redkale</name>
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>ossrh</id>
|
||||
@@ -51,17 +69,19 @@
|
||||
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
|
||||
<scm>
|
||||
<url>https://github.com/redkale/redkale</url>
|
||||
<connection>scm:git:git@github.com/redkale/redkale.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:redkale/redkale.git</developerConnection>
|
||||
</scm>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.5.1</version>
|
||||
<version>${maven-compiler-plugin.version}</version>
|
||||
<configuration>
|
||||
<compilerArgument>-parameters</compilerArgument>
|
||||
<encoding>UTF-8</encoding>
|
||||
@@ -74,7 +94,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
<version>${maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<addMavenDescriptor>false</addMavenDescriptor>
|
||||
@@ -88,7 +108,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.6</version>
|
||||
<version>${maven-gpg-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
@@ -99,10 +119,11 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<version>${maven-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
@@ -111,10 +132,11 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<version>${maven-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
@@ -127,7 +149,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<version>${maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<appendAssemblyId>false</appendAssemblyId>
|
||||
<descriptors>
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
<EFBFBD><EFBFBD>Ŀ¼<EFBFBD>µ<EFBFBD><EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>sonatypeʱʹ<EFBFBD>ã<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڹ<EFBFBD><DAB9>̴<EFBFBD><CCB4><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD>Ŀ¼<EFBFBD>µ<EFBFBD><EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>sonatypeʱʹ<EFBFBD>ã<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڹ<EFBFBD><DAB9>̴<EFBFBD><CCB4><EFBFBD><EFBFBD><EFBFBD>
|
||||
|
||||
ʹ<EFBFBD><EFBFBD>gpg<EFBFBD><EFBFBD><EFBFBD><EFBFBD>sonatype<EFBFBD><EFBFBD>Կ:
|
||||
|
||||
1<EFBFBD><EFBFBD> gpg <20>C-gen-key
|
||||
2<EFBFBD><EFBFBD> gpg --keyserver keys.openpgp.org --send-keys <20><><EFBFBD>Ĺ<EFBFBD>Կ(һ<><D2BB>ʮ<EFBFBD><CAAE><EFBFBD><EFBFBD><EFBFBD>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD>֣<EFBFBD><D6A3><EFBFBD><EFBFBD><EFBFBD>DE346FA5)
|
||||
<20><>ʾ<EFBFBD><CABE> gpg: <20>ӹ<EFBFBD>Կ<EFBFBD><D4BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʧ<EFBFBD>ܣ<EFBFBD>Server indicated a failure <20><>ʾ<EFBFBD><CABE><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<!--
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
@@ -92,6 +93,7 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
-->
|
||||
</profile>
|
||||
</profiles>
|
||||
</settings>
|
||||
142
pom.xml
Normal file
142
pom.xml
Normal file
@@ -0,0 +1,142 @@
|
||||
<?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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.redkale</groupId>
|
||||
<artifactId>redkale</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>RedkaleProject</name>
|
||||
<url>https://redkale.org</url>
|
||||
<description>redkale -- java framework</description>
|
||||
<version>2.7.0</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
|
||||
<junit.version>5.7.0</junit.version>
|
||||
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
|
||||
<maven-compiler-plugin.version>3.9.0</maven-compiler-plugin.version>
|
||||
<maven-surefire-plugin.version>3.0.0-M6</maven-surefire-plugin.version>
|
||||
<maven-failsafe-plugin.version>3.0.0-M6</maven-failsafe-plugin.version>
|
||||
</properties>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache 2</name>
|
||||
<url>https://www.apache.org/licenses/</url>
|
||||
<distribution>repo</distribution>
|
||||
<comments>Apache License</comments>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>central</id>
|
||||
<name>Central Repository</name>
|
||||
<url>https://repo.maven.apache.org/maven2</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>sonatype-nexus-snapshots</id>
|
||||
<name>Sonatype Nexus Snapshots</name>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<id>Redkale</id>
|
||||
<name>redkale</name>
|
||||
<email>redkale@qq.com</email>
|
||||
<url>https://redkale.org</url>
|
||||
<roles>
|
||||
<role>Project Manager</role>
|
||||
<role>Architect</role>
|
||||
</roles>
|
||||
<organization>redkale</organization>
|
||||
<organizationUrl>https://redkale.org</organizationUrl>
|
||||
<properties>
|
||||
<dept>No</dept>
|
||||
</properties>
|
||||
<timezone>8</timezone>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<scm>
|
||||
<url>https://github.com/redkale/redkale</url>
|
||||
<connection>scm:git:git@github.com/redkale/redkale.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:redkale/redkale.git</developerConnection>
|
||||
</scm>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${maven-compiler-plugin.version}</version>
|
||||
<configuration>
|
||||
<encoding>UTF-8</encoding>
|
||||
<compilerArgs>
|
||||
<arg>-parameters</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- 需要注释掉, 否则会生成native-image配置信息
|
||||
<plugin>
|
||||
<groupId>org.redkale.maven.plugins</groupId>
|
||||
<artifactId>redkale-maven-plugin</artifactId>
|
||||
<version>1.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>redkale-compile</id>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
-->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>${maven-jar-plugin.version}</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<addMavenDescriptor>false</addMavenDescriptor>
|
||||
<manifest>
|
||||
<mainClass>org.redkale.boot.Application</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>${maven-surefire-plugin.version}</version>
|
||||
<configuration>
|
||||
<forkMode>once</forkMode>
|
||||
<argLine>-Dfile.encoding=UTF-8</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>${maven-failsafe-plugin.version}</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package javax.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @since Common Annotations 1.0
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Resource {
|
||||
public enum AuthenticationType {
|
||||
CONTAINER,
|
||||
APPLICATION
|
||||
}
|
||||
public String name() default "";
|
||||
|
||||
public Class<?> type() default Object.class;
|
||||
public AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
|
||||
public boolean shareable() default true;
|
||||
public String description() default "";
|
||||
public String mappedName() default "";
|
||||
|
||||
public String lookup() default "";
|
||||
}
|
||||
@@ -55,8 +55,8 @@
|
||||
} else {
|
||||
var w = param.required ? "font-weight:bold;" : "";
|
||||
var c = ' style="' + w + '"';
|
||||
if (param.src == "HEADER") c = ' style="color:red;' + w + '"';
|
||||
if (param.src == "COOKIE") c = ' style="color:blue;' + w + '"';
|
||||
if (param.style == "HEADER") c = ' style="color:red;' + w + '"';
|
||||
if (param.style == "COOKIE") c = ' style="color:blue;' + w + '"';
|
||||
paramshtml.push('<tr><td ' + c + '> ' + param.name + ' </td><td> ' + t + '</td><td> ' + param.comment + '</td></tr>');
|
||||
}
|
||||
}
|
||||
51
src/main/java/META-INF/application-template.properties
Normal file
51
src/main/java/META-INF/application-template.properties
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
redkale.nodeid = 1000
|
||||
redkale.port = 6560
|
||||
redkale.lib = ./
|
||||
|
||||
#\u3010resources\u8282\u70b9\u5168\u5c40\u552f\u4e00\u3011
|
||||
#\u3010executor\u8282\u70b9\u5168\u5c40\u552f\u4e00\u3011
|
||||
redkale.resources.executor.threads = 4
|
||||
redkale.resources.executor.hash = false
|
||||
|
||||
#\u3010transport\u8282\u70b9\u5168\u5c40\u552f\u4e00\u3011
|
||||
redkale.resources.transport.bufferCapacity = 32k
|
||||
redkale.resources.transport.bufferPoolSize = 32
|
||||
|
||||
#\u3010excludelibs\u8282\u70b9\u5168\u5c40\u552f\u4e00\u3011
|
||||
redkale.resources.excludelibs.value = ^.*mysql.*$;^.*kafka.*$
|
||||
|
||||
#\u3010cluster\u8282\u70b9\u5168\u5c40\u552f\u4e00\u3011
|
||||
redkale.resources.cluster.type = org.redkalex.cluster.consul.ConsulClusterAgent
|
||||
redkale.resources.cluster.waits= = false
|
||||
redkale.resources.cluster.protocols = SNCP
|
||||
redkale.resources.cluster.ports = 7070;7071
|
||||
|
||||
redkale.resources.mq[0].name =
|
||||
redkale.resources.mq[0].type = org.redkalex.mq.kafka.KafkaMessageAgent
|
||||
redkale.resources.mq[0].servers.value = 127.0.0.1:9101
|
||||
|
||||
redkale.resources.group[0].name =
|
||||
redkale.resources.group[0].protocol = TCP
|
||||
redkale.resources.group[0].node[0].addr = 127.0.0.1
|
||||
redkale.resources.group[0].node[0].port = 7070
|
||||
|
||||
#\u3010listener\u8282\u70b9\u5168\u5c40\u552f\u4e00\u3011
|
||||
redkale.resources.listener.value = org.redkalex.xxx.XXXApplicationListener
|
||||
|
||||
#\u3010properties\u8282\u70b9\u5168\u5c40\u552f\u4e00\u3011
|
||||
redkale.resources.properties.load = config.properties
|
||||
redkale.resources.properties.property[0].name = system.property.yyyy
|
||||
redkale.resources.properties.property[0].value = YYYYYY
|
||||
redkale.resources.properties.property[1].name = xxxxxxx
|
||||
redkale.resources.properties.property[1].value = YYYYYY
|
||||
|
||||
redkale.server[0].protocol = HTTP
|
||||
redkale.server[0].host = 127.0.0.1
|
||||
redkale.server[0].port = 6060
|
||||
redkale.server[0].root = root
|
||||
redkale.server[0].lib =
|
||||
#\u3010\u8282\u70b9\u5728<server>\u4e2d\u552f\u4e00\u3011
|
||||
redkale.server[0].ssl.build = org.redkale.net.SSLBuilder\u5b50\u7c7b
|
||||
|
||||
redkale.server[0].services[0].autoload = true
|
||||
@@ -19,11 +19,13 @@
|
||||
serviceid1_name1 serviceid1_name2 serviceid2_name1 serviceid2_name2
|
||||
-->
|
||||
<!--
|
||||
address: 本地局域网的IP地址, 默认值为默认网卡的ip,当不使用默认值需要指定值,如192.168.1.22
|
||||
nodeid: int 进程的节点ID,用于分布式环境,一个系统中节点ID必须全局唯一,使用cluster时框架会进行唯一性校验
|
||||
name: 进程的名称,用于监控识别,命名规则: 字母、数字、下划线、短横、点
|
||||
address: 本地局域网的IP地址, 默认值为默认网卡的ip,当不使用默认值需要指定值,如192.168.1.22
|
||||
port: required 程序的管理Server的端口,用于关闭或者与监管系统进行数据交互
|
||||
lib: 加上额外的lib路径,多个路径用分号;隔开; 默认为空。 例如: ${APP_HOME}/lib/a.jar;${APP_HOME}/lib2/b.jar;
|
||||
lib: 加上额外的lib路径,多个路径用分号;隔开; 默认为空。 例如: ${APP_HOME}/lib/a.jar;${APP_HOME}/lib2/b.jar;
|
||||
-->
|
||||
<application port="6560" lib="">
|
||||
<application nodeid="1000" port="6560" lib="">
|
||||
|
||||
<!--
|
||||
【节点全局唯一】
|
||||
@@ -31,24 +33,73 @@
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<!--
|
||||
【节点全局唯一】 @since 2.3.0
|
||||
全局Serivce执行的线程池, Application.workExecutor, 没配置该节点将自动创建一个。
|
||||
threads: 线程数,为0表示不启用workExecutor,只用IO线程。默认: CPU核数, 核数=1的情况下默认值为2
|
||||
hash: 是否使用ThreadHashExecutor作为线程池,默认值为:false
|
||||
-->
|
||||
<executor threads="4" hash="false"/>
|
||||
|
||||
<!--
|
||||
【节点全局唯一】
|
||||
transport节点只能有一个,用于配置所有Transport的池参数,没配置该节点将自动创建一个。
|
||||
threads: 线程总数, 默认: <group>节点数*CPU核数*2
|
||||
bufferCapacity: ByteBuffer的初始化大小, 默认: 32K;
|
||||
bufferPoolSize: ByteBuffer池的大小,默认: 线程总数*4
|
||||
readTimeoutSeconds: TCP读取超时秒数, 默认为6秒, 为0表示无超时限制
|
||||
threads: 线程总数, 默认: <group>节点数*CPU核数*2
|
||||
bufferCapacity: ByteBuffer的初始化大小, 默认: 32K;
|
||||
bufferPoolSize: ByteBuffer池的大小,默认: 线程总数*4
|
||||
readTimeoutSeconds: TCP读取超时秒数, 默认为6秒, 为0表示无超时限制
|
||||
writeTimeoutSeconds: TCP写入超时秒数, 默认为6秒, 为0表示无超时限制
|
||||
strategy: 远程请求的负载均衡策略, 必须是org.redkale.net.TransportStrategy的实现类
|
||||
strategy: 远程请求的负载均衡策略, 必须是org.redkale.net.TransportStrategy的实现类
|
||||
-->
|
||||
<transport bufferCapacity="32K" bufferPoolSize="32" threads="32" readTimeoutSeconds="6" writeTimeoutSeconds="6"/>
|
||||
|
||||
<!--
|
||||
【节点全局唯一】
|
||||
自动扫描时排除部分包路径
|
||||
value: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开
|
||||
-->
|
||||
<excludelibs value="^.*mysql.*$;^.*kafka.*$"/>
|
||||
|
||||
<!--
|
||||
【节点全局唯一】
|
||||
第三方服务发现管理接口
|
||||
type: 类名,必须是org.redkale.cluster.ClusterAgent的子类
|
||||
waits: 注销服务后是否需要等待检查周期时间后再进行Service销毁,默认值为:false
|
||||
当一个Service进行服务注销后,不能立刻销毁Service,因为健康检测是有间隔时间差的,
|
||||
需要等待一个健康检测周期时间,让其他进程都更新完服务列表。
|
||||
如果使用MQ,可以设置为false,如果对服务健壮性要求高,建议设置为true
|
||||
protocols: 服务发现可以处理的协议, 默认值为: SNCP, 多个协议用分号;隔开
|
||||
ports: 服务发现可以处理的端口, 多个端口用分号;隔开
|
||||
ttls: 心跳频率,多少秒一次
|
||||
xxxx: 自定义的字段属性,例如:CacheClusterAgent有source字段; ConsulClusterAgent有apiurl字段;
|
||||
-->
|
||||
<cluster type="org.redkalex.cluster.consul.ConsulClusterAgent" waits="false" protocols="SNCP" ports="7070;7071" xxx="xxx" />
|
||||
|
||||
<!--
|
||||
MQ管理接口配置
|
||||
不同MQ节点所配置的MQ集群不能重复。
|
||||
MQ跟着协议走,所以mq的属性值需要被赋值在rest节点上, 由于SncpServlet是自动生成的,故SNCP协议下,mq属性值被赋值在service/services节点上
|
||||
name: 服务的名称,用于监控识别,多个mq节点时只能有一个name为空的节点,mq.name不能重复,命名规则: 字母、数字、下划线
|
||||
type: 实现类名,必须是org.redkale.mq.MessageAgent的子类
|
||||
coder: MessageRecord的解析器类,必须是org.redkale.mq.MessageCoder<MessageRecord>的实现类,
|
||||
可对数据包进行加密解密,默认值:org.redkale.mq.MessageRecordCoder
|
||||
MQ节点下的子节点配置没有固定格式, 根据MessageAgent实现方的定义来配置
|
||||
-->
|
||||
<mq name="" type="org.redkalex.mq.kafka.KafkaMessageAgent">
|
||||
<servers value="127.0.0.1:9101"/>
|
||||
<consumer>
|
||||
<property name="xxxxxx" value="XXXXXXXX"/>
|
||||
</consumer>
|
||||
<producer>
|
||||
<property name="xxxxxx" value="XXXXXXXX"/>
|
||||
</producer>
|
||||
</mq>
|
||||
|
||||
<!--
|
||||
一个组包含多个node, 同一Service服务可以由多个进程提供,这些进程称为一个GROUP,且同一GROUP内的进程必须在同一机房或局域网内
|
||||
一个group节点对应一个 Transport 对象。
|
||||
name: 服务组ID,长度不能超过11个字节. 默认为空字符串。 注意: name不能包含$符号。
|
||||
protocol:值范围:UDP TCP, 默认TCP
|
||||
subprotocol: 子协议,预留字段。默认值为空
|
||||
name: 服务组ID,长度不能超过11个字节. 默认为空字符串。 注意: name不能包含$符号。
|
||||
protocol: 值范围:UDP TCP, 默认TCP
|
||||
注意: 一个node只能所属一个group。只要存在protocol=SNCP的Server节点信息, 就必须有group节点信息。
|
||||
-->
|
||||
<group name="" protocol="TCP">
|
||||
@@ -60,18 +111,7 @@
|
||||
-->
|
||||
<node addr="127.0.0.1" port="7070"/>
|
||||
</group>
|
||||
|
||||
<!--
|
||||
全局的数据源设置, 可以是CacheSource、DataSource, JDBC的DataSource通常通过persistence.xml配置,此处多用于CacheSource的配置
|
||||
name: 资源名,用于依赖注入。
|
||||
value:类名,必须是CacheSource或DataSource的子类,且必须实现Service接口。如果是DataSource.class,系统自动映射成DataJdbcSource.class
|
||||
groups: 指定groups。
|
||||
xxx: 其他属性与子节点通过Service.init方法传入的AnyValue获取。
|
||||
-->
|
||||
<source name="redis" value="org.redkalex.cache.RedisCacheSource" xxx="16">
|
||||
<node addr="127.0.0.1" port="7070"/>
|
||||
</source>
|
||||
|
||||
|
||||
<!--
|
||||
Application启动的监听事件,可配置多个节点
|
||||
value: 类名,必须是ApplicationListener的子类
|
||||
@@ -83,22 +123,21 @@
|
||||
全局的参数配置, 可以通过@Resource(name="property.xxxxxx") 进行注入<property>的信息, 被注解的字段类型只能是String、primitive class
|
||||
如果name是system.property.开头的值将会在进程启动时进行System.setProperty("yyyy", "YYYYYY")操作。
|
||||
如果name是mimetype.property.开头的值将会在进程启动时进行MimeType.add("yyyy", "YYYYYY")操作。
|
||||
先加载子节点property,再加载load文件, 最后加载agent的实现子类。
|
||||
agent: 实现类名,必须是org.redkale.boot.PropertiesAgent的子类
|
||||
load: 加载文件,多个用;隔开。
|
||||
默认置入的system.property.的有:
|
||||
System.setProperty("net.transport.poolmaxconns", "100");
|
||||
System.setProperty("net.transport.pinginterval", "30");
|
||||
System.setProperty("net.transport.checkinterval", "30");
|
||||
System.setProperty("convert.json.tiny", "true");
|
||||
System.setProperty("convert.bson.tiny", "true");
|
||||
System.setProperty("convert.json.pool.size", "128");
|
||||
System.setProperty("convert.bson.pool.size", "128");
|
||||
System.setProperty("convert.json.writer.buffer.defsize", "4096");
|
||||
System.setProperty("convert.bson.writer.buffer.defsize", "4096");
|
||||
System.setProperty("redkale.net.transport.poolmaxconns", "100");
|
||||
System.setProperty("redkale.net.transport.pinginterval", "30");
|
||||
System.setProperty("redkale.net.transport.checkinterval", "30");
|
||||
System.setProperty("redkale.convert.tiny", "true");
|
||||
System.setProperty("redkale.convert.pool.size", "128");
|
||||
System.setProperty("redkale.convert.writer.buffer.defsize", "4096");
|
||||
|
||||
<properties>节点下也可包含非<property>节点.
|
||||
非<property>其节点可以通过@Resource(name="properties.xxxxxx")进行注入, 被注解的字段类型只能是AnyValue、AnyValue[]
|
||||
-->
|
||||
<properties load="config.properties">
|
||||
<properties load="config.properties" agent="">
|
||||
<property name="system.property.yyyy" value="YYYYYY"/>
|
||||
<property name="xxxxxx" value="XXXXXXXX"/>
|
||||
<property name="xxxxxx" value="XXXXXXXX"/>
|
||||
@@ -107,50 +146,60 @@
|
||||
|
||||
</resources>
|
||||
<!--
|
||||
protocol: required server所启动的协议,Redkale内置的有HTTP、SNCP、WATCH。协议均使用TCP实现; WATCH服务只能存在一个。
|
||||
name: 服务的名称,用于监控识别,一个配置文件中的server.name不能重复,命名规则: 字母、数字、下划线
|
||||
host: 服务所占address , 默认: 0.0.0.0
|
||||
port: required 服务所占端口
|
||||
root: 如果是web类型服务,则包含页面 默认:{APP_HOME}/root
|
||||
lib: server额外的class目录, 默认为${APP_HOME}/libs/*;
|
||||
excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开
|
||||
charset: 文本编码, 默认: UTF-8
|
||||
backlog: 默认10K
|
||||
threads: 线程数, 默认: CPU核数*2,最小8个
|
||||
maxconns:最大连接数, 小于1表示无限制, 默认: 0
|
||||
maxbody: request.body最大值, 默认: 64K
|
||||
bufferCapacity: ByteBuffer的初始化大小, TCP默认: 32K; (HTTP 2.0、WebSocket,必须要16k以上); UDP默认: 1350B
|
||||
bufferPoolSize: ByteBuffer池的大小,默认: 线程数*4
|
||||
responsePoolSize: Response池的大小,默认: 线程数*2
|
||||
protocol: required server所启动的协议,Redkale内置的有HTTP、SNCP、WATCH。协议均使用TCP实现; WATCH服务只能存在一个。
|
||||
name: 服务的名称,用于监控识别,一个配置文件中的server.name不能重复,命名规则: 字母、数字、下划线
|
||||
host: 服务所占address , 默认: 0.0.0.0
|
||||
port: required 服务所占端口
|
||||
root: 如果是web类型服务,则包含页面 默认:{APP_HOME}/root
|
||||
lib: server额外的class目录, 默认为${APP_HOME}/libs/*;
|
||||
excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开
|
||||
charset: 文本编码, 默认: UTF-8
|
||||
backlog: 默认10K
|
||||
threads【已废弃】: 线程数, 默认: CPU核数*2,最小8个【已废弃 @since 2.3.0】
|
||||
maxconns: 最大连接数, 小于1表示无限制, 默认: 0
|
||||
maxbody: request.body最大值, 默认: 64K
|
||||
bufferCapacity: ByteBuffer的初始化大小, TCP默认: 32K; (HTTP 2.0、WebSocket,必须要16k以上); UDP默认: 1350B
|
||||
bufferPoolSize: ByteBuffer池的大小,默认: 线程数*4
|
||||
responsePoolSize: Response池的大小,默认: 1024
|
||||
aliveTimeoutSeconds: KeepAlive读操作超时秒数, 默认30, 0表示永久不超时; -1表示禁止KeepAlive
|
||||
readTimeoutSeconds: 读操作超时秒数, 默认0, 表示永久不超时
|
||||
writeTimeoutSeconds: 写操作超时秒数, 默认0, 表示永久不超时
|
||||
netimpl: ProtocolServer的实现类。TCP情况下值可以是aio或nio,默认值为aio;UDP情况下值可以是bio,默认值为bio;
|
||||
interceptor: 启动/关闭NodeServer时被调用的拦截器实现类,必须是org.redkale.boot.NodeInterceptor的子类,默认为null
|
||||
readTimeoutSeconds: 读操作超时秒数, 默认0, 表示永久不超时
|
||||
writeTimeoutSeconds: 写操作超时秒数, 默认0, 表示永久不超时
|
||||
interceptor: 启动/关闭NodeServer时被调用的拦截器实现类,必须是org.redkale.boot.NodeInterceptor的子类,默认为null
|
||||
-->
|
||||
<server protocol="HTTP" host="127.0.0.1" port="6060" root="root" lib="">
|
||||
|
||||
<!--
|
||||
【节点在<server>中唯一】
|
||||
value: 创建SSLContext的实现类, 可自定义,必须是org.redkale.net.SSLCreator的子类
|
||||
clientauth: true/false/want
|
||||
keystorepass: KEY密码
|
||||
keystorefile: KEY文件
|
||||
truststorepass: TRUST密码
|
||||
truststorefile: TRUST文件
|
||||
builder: 创建SSLContext的实现类, 可自定义,必须是org.redkale.net.SSLBuilder的子类
|
||||
sslProvider: java.security.Provider自定义的实现类,如第三方: org.conscrypt.OpenSSLProvider、org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||
jsseProvider: java.security.Provider自定义的实现类,如第三方: org.conscrypt.JSSEProvider、 org.bouncycastle.jce.provider.BouncyCastleJsseProvider
|
||||
protocol: TLS版本,默认值: TLS
|
||||
protocols: 设置setEnabledProtocols, 多个用,隔开 如: TLSv1.2,TLSv1.3
|
||||
clientAuth: WANT/NEED/NONE, 默认值: NONE
|
||||
ciphers: 设置setEnabledCipherSuites, 多个用,隔开 如: TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA256
|
||||
keystorePass: KEY密码
|
||||
keystoreFile: KEY文件 .jks
|
||||
keystoreType: KEY类型, 默认值为JKS
|
||||
keystoreAlgorithm: KEY文件的algorithm, 默认值为SunX509
|
||||
truststorePass: TRUST密码
|
||||
truststoreFile: TRUST文件
|
||||
truststoreType: TRUST类型, 默认值为JKS
|
||||
truststoreAlgorithm: TRUST文件的algorithm, 默认值为SunX509
|
||||
-->
|
||||
<ssl creator=""/>
|
||||
<ssl builder=""/>
|
||||
|
||||
<!--
|
||||
加载所有的Service服务;
|
||||
在同一个进程中同一个name同一类型的Service将共用同一个实例
|
||||
autoload="true" 默认值. 自动加载classpath下所有的Service类
|
||||
autoload="false" 需要显著的指定Service类
|
||||
mq: 所属的MQ管理器,当 protocol == SNCP 时该值才有效, 存在该属性表示Service的SNCP协议采用消息总线代理模式
|
||||
includes: 当autoload="true", 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
|
||||
excludes: 当autoload="true", 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
|
||||
groups: 所属组的节点,多个节点值用;隔开,如果配置文件中存在多个SNCP协议的Server节点,需要显式指定group属性.
|
||||
当 protocol == SNCP 时 group表示当前Server与哪些节点组关联。
|
||||
当 protocol != SNCP 时 group只能是空或者一个group的节点值,不能为多个节点值。
|
||||
groups: 所属组的节点,多个节点值用;隔开,如果配置文件中存在多个SNCP协议的Server节点,需要显式指定group属性.
|
||||
当 protocol == SNCP 时 group表示当前Server与哪些节点组关联。
|
||||
当 protocol != SNCP 时 group只能是空或者一个group的节点值,不能为多个节点值。
|
||||
特殊值"$cluster", 视为通过第三方服务注册发现管理工具来获取远程模式的ip端口信息
|
||||
-->
|
||||
<services autoload="true" includes="" excludes="">
|
||||
|
||||
@@ -158,6 +207,7 @@
|
||||
<service value="com.xxx.XXX1Service"/>
|
||||
<!--
|
||||
name: 显式指定name,覆盖默认的空字符串值。 注意: name不能包含$符号。
|
||||
mq: 所属的MQ管理器,当 protocol == SNCP 时该值才有效, 存在该属性表示Service的SNCP协议采用消息总线代理模式
|
||||
groups: 显式指定groups,覆盖<services>节点的groups默认值。
|
||||
ignore: 是否禁用, 默认为false。
|
||||
-->
|
||||
@@ -174,8 +224,8 @@
|
||||
加载所有的Filter服务;
|
||||
autoload="true" 默认值.
|
||||
autoload="false" 需要显著的指定Filter类
|
||||
includes: 当autoload="true", 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
|
||||
excludes: 当autoload="true", 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
|
||||
includes: 当autoload="true", 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
|
||||
excludes: 当autoload="true", 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
|
||||
-->
|
||||
<filters autoload="true" includes="" excludes="">
|
||||
|
||||
@@ -198,8 +248,9 @@
|
||||
<!--
|
||||
REST的核心配置项
|
||||
当Server为HTTP协议时, rest节点才有效。存在[rest]节点则Server启动时会加载REST服务, 节点可以多个,(WATCH协议不需要设置,系统会自动生成)
|
||||
path: servlet的ContextPath前缀 默认为空
|
||||
path: servlet的ContextPath前缀 默认为空 【注: 开启MQ时,该字段失效】
|
||||
base: REST服务的BaseServlet,必须是 org.redkale.net.http.HttpServlet 的子类,且子类必须标记@HttpUserType。
|
||||
mq: 所属的MQ管理器, 存在该属性表示RestService的请求来自于消息总线 【注: 开启MQ时,path字段失效】
|
||||
autoload:默认值"true" 默认值. 加载当前server所能使用的Servce对象;
|
||||
includes:当autoload="true", 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
|
||||
excludes:当autoload="true", 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
|
||||
@@ -222,10 +273,13 @@
|
||||
当Server为HTTP协议时, request节点才有效。
|
||||
remoteaddr 节点: 替换请求方节点的IP地址, 通常请求方是由nginx等web静态服务器转发过的则需要配置该节点。
|
||||
且value值只能是以request.headers.开头,表示从request.headers中获取对应的header值。
|
||||
locale value值必须是request.headers.或request.parameters.开头。
|
||||
例如下面例子获取request.getRemoteAddr()值,如果header存在X-RemoteAddress值则返回X-RemoteAddress值,不存在返回getRemoteAddress()。
|
||||
-->
|
||||
<request>
|
||||
<remoteaddr value="request.headers.X-RemoteAddress"/>
|
||||
<locale value="request.headers.locale" />
|
||||
<rpc authenticator="org.redkale.net.http.HttpRpcAuthenticator的实现类"/>
|
||||
</request>
|
||||
|
||||
<!--
|
||||
@@ -234,8 +288,8 @@
|
||||
contenttype: plain值为调用finish时的ContentType; 默认值: text/plain; charset=utf-8
|
||||
json值为调用finishJson时的ContentType; 默认值: application/json; charset=utf-8
|
||||
defcookie 节点: 当response里输出的cookie没有指定domain 和path时,使用该节点的默认值。
|
||||
如果addheader、setheader 的value值以request.parameters.开头则表示从request.parameters中获取对应的parameter值
|
||||
如果addheader、setheader 的value值以request.headers.开头则表示从request.headers中获取对应的header值
|
||||
addheader、setheader 的value值以request.parameters.开头则表示从request.parameters中获取对应的parameter值
|
||||
addheader、setheader 的value值以request.headers.开头则表示从request.headers中获取对应的header值
|
||||
例如下面例子是在Response输出header时添加两个header(一个addHeader, 一个setHeader)。
|
||||
options 节点: 设置了该节点且auto=true,当request的method=OPTIONS自动设置addheader、setheader并返回200状态码
|
||||
date 节点: 设置了该节点且period有值(单位:毫秒);返回response会包含Date头信息,默认为period=0
|
||||
@@ -244,7 +298,7 @@
|
||||
period>0表示定时获取时间; 设置1000表示每秒刷新Date时间
|
||||
-->
|
||||
<response>
|
||||
<contenttype plain="text/plain; charset=utf-8" json="application/json; charset=utf-8"/>
|
||||
<content-type plain="text/plain; charset=utf-8" json="application/json; charset=utf-8"/>
|
||||
<defcookie domain="" path=""/>
|
||||
<addheader name="Access-Control-Allow-Origin" value="request.headers.Origin" />
|
||||
<setheader name="Access-Control-Allow-Headers" value="request.headers.Access-Control-Request-Headers"/>
|
||||
@@ -256,28 +310,29 @@
|
||||
【节点在<server>中唯一】
|
||||
当Server为HTTP协议时,render才有效. 指定输出引擎的实现类
|
||||
value: 输出引擎的实现类, 必须是org.redkale.net.http.HttpRender的子类
|
||||
suffixs: 引擎文件名后缀,多个用;隔开,默认值为: .htel
|
||||
-->
|
||||
<render value="org.redkalex.htel.HttpTemplateRender"/>
|
||||
<render value="org.redkalex.htel.HttpTemplateRender" suffixs=".htel"/>
|
||||
<!--
|
||||
【节点在<server>中唯一】
|
||||
当Server为HTTP协议时,ResourceServlet才有效. 默认存在一个有默认属性的resource-servlet节点
|
||||
webroot: web资源的根目录, 默认取server节点中的root值
|
||||
servlet: 静态资源HttpServlet的实现,默认使用HttpResourceServlet
|
||||
index : 启始页,默认值:index.html
|
||||
index : 启始页,默认值:index.html
|
||||
-->
|
||||
<resource-servlet webroot="root" index="index.html">
|
||||
<!--
|
||||
【节点在<resource-servlet>中唯一】
|
||||
资源缓存的配置, 默认存在一个含默认属性的caches节点
|
||||
limit: 资源缓存最大容量, 默认: 0, 为0表示不缓存, 单位可以是B、K、M、G,不区分大小写
|
||||
limit: 资源缓存最大容量, 默认: 0, 为0表示不缓存, 单位可以是B、K、M、G,不区分大小写
|
||||
lengthmax: 可缓存的文件大小上限, 默认: 1M(超过1M的文件不会被缓存)
|
||||
watch: 是否监控缓存文件的变化, 默认为false,不监控
|
||||
watch: 是否监控缓存文件的变化, 默认为false,不监控
|
||||
-->
|
||||
<cache limit="0M" lengthmax="1M" watch="false"/>
|
||||
<!--
|
||||
支持类似nginx中的rewrite, 目前只支持静态资源对静态资源的跳转。
|
||||
type: 匹配的类型, 目前只支持location(匹配requestURI), 默认: location
|
||||
match: 匹配的正则表达式
|
||||
type: 匹配的类型, 目前只支持location(匹配requestURI), 默认: location
|
||||
match: 匹配的正则表达式
|
||||
forward: 需跳转后的资源链接
|
||||
例如下面例子是将/xxx-yyy.html的页面全部跳转到/xxx.html
|
||||
-->
|
||||
@@ -285,11 +340,11 @@
|
||||
</resource-servlet>
|
||||
<!--
|
||||
加载所有的Servlet服务;
|
||||
path: servlet的ContextPath前缀 默认为空
|
||||
path: servlet的ContextPath前缀 默认为空
|
||||
autoload="true" 默认值. 自动加载classpath下所有的Servlet类
|
||||
autoload="false" 需要显著的指定Service类
|
||||
includes: 当autoload="true", 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
|
||||
excludes: 当autoload="true", 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
|
||||
includes: 当autoload="true", 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
|
||||
excludes: 当autoload="true", 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
|
||||
-->
|
||||
<servlets path="/pipes" autoload="true" includes="" excludes="">
|
||||
<!--
|
||||
@@ -15,11 +15,16 @@ com.sun.level = INFO
|
||||
java.util.logging.FileHandler.limit = 20M
|
||||
java.util.logging.FileHandler.count = 100
|
||||
java.util.logging.FileHandler.encoding = UTF-8
|
||||
java.util.logging.FileHandler.pattern = ${APP_HOME}/logs-%m/log-%d.log
|
||||
java.util.logging.FileHandler.pattern = ${APP_HOME}/logs-%tY%tm/log-%tY%tm%td.log
|
||||
#java.util.logging.FileHandler.unusual \u5c5e\u6027\u8868\u793a\u5c06 WARNING\u3001SEVERE \u7ea7\u522b\u7684\u65e5\u5fd7\u590d\u5236\u5199\u5165\u5355\u72ec\u7684\u6587\u4ef6\u4e2d
|
||||
java.util.logging.FileHandler.unusual = ${APP_HOME}/logs-%m/log-warnerr-%d.log
|
||||
java.util.logging.FileHandler.unusual = ${APP_HOME}/logs-%tY%tm/log-warnerr-%tY%tm%td.log
|
||||
#\u9700\u8981\u5c4f\u853d\u6d88\u606f\u5185\u5bb9\u7684\u6b63\u5219\u8868\u8fbe\u5f0f
|
||||
java.util.logging.FileHandler.denyreg =
|
||||
java.util.logging.FileHandler.append = true
|
||||
|
||||
#java.util.logging.ConsoleHandler.level = FINE
|
||||
|
||||
#\u5c06\u65e5\u5fd7\u5199\u8fdbSearchSource, \u5fc5\u987b\u6307\u5b9asource\u8d44\u6e90\u540d\uff0c\u5728source.properties\u4e2d\u5b9a\u4e49
|
||||
#java.util.logging.SearchHandler.source = platfsearch
|
||||
#\u6307\u5b9a\u5199\u8fdbSearchSource\u7684\u8868\u540d\uff0c\u9ed8\u8ba4\u503c\u4e3alog-record
|
||||
#java.util.logging.SearchHandler.tag = log-${APP_NAME}-%tY%tm%td
|
||||
@@ -1,5 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- 其配置算是标准的JPA配置文件的缩略版 -->
|
||||
<!--
|
||||
【已废弃】,建议使用 source.properties
|
||||
其配置算是标准的JPA配置文件的缩略版
|
||||
-->
|
||||
<persistence>
|
||||
<!-- 系统基本库 -->
|
||||
<persistence-unit name="demouser">
|
||||
@@ -12,28 +15,18 @@
|
||||
是否开启缓存(标记为@Cacheable的Entity类),值目前只支持两种: ALL: 所有开启缓存。 NONE: 关闭所有缓存, 非NONE字样统一视为ALL
|
||||
-->
|
||||
<property name="javax.persistence.cachemode" value="ALL"/>
|
||||
|
||||
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/dbuser?characterEncoding=utf8"/>
|
||||
<!--
|
||||
javax.persistence.jdbc.driver在JPA的值是JDBC驱动,Redkale有所不同,值应该是javax.sql.DataSource的子类。
|
||||
为了兼容用户习惯,Redkale内置常见JDBC驱动到javax.sql.DataSource的映射关系:
|
||||
org.mariadb.jdbc.Driver —————— org.mariadb.jdbc.MySQLDataSource
|
||||
org.postgresql.Driver —————— org.postgresql.ds.PGConnectionPoolDataSource
|
||||
com.mysql.jdbc.Driver —————— com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource
|
||||
com.mysql.cj.jdbc.Driver —————— com.mysql.cj.jdbc.MysqlConnectionPoolDataSource
|
||||
oracle.jdbc.driver.OracleDriver —————— oracle.jdbc.pool.OracleConnectionPoolDataSource
|
||||
com.microsoft.sqlserver.jdbc.SQLServerDriver —————— com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource
|
||||
org.h2.Driver —————— org.h2.jdbcx.JdbcDataSource
|
||||
因此 com.mysql.jdbc.Driver 会被自动转换成 com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource
|
||||
并且如果JDBC驱动是以上几个版本,javax.persistence.jdbc.driver属性都可以省略,Redkale会根据javax.persistence.jdbc.url的值来识别驱动
|
||||
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
|
||||
<property name="javax.persistence.jdbc.source" value="com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource"/>
|
||||
是否自动建表当表不存在的时候, 目前只支持mysql、postgres, 默认为false
|
||||
-->
|
||||
<property name="javax.persistence.table.autoddl" value="false"/>
|
||||
|
||||
<!-- 多个URL用;隔开,如分布式SearchSource需要配多个URL -->
|
||||
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/dbuser?characterEncoding=utf8"/>
|
||||
<property name="javax.persistence.jdbc.user" value="root"/>
|
||||
<property name="javax.persistence.jdbc.password" value="123456"/>
|
||||
|
||||
<!-- 最大连接数,默认值:CPU数*16 -->
|
||||
<property name="javax.persistence.connections.limit" value="32"/>
|
||||
<!-- 最大连接数,默认值:CPU数 -->
|
||||
<property name="javax.persistence.connections.limit" value="12"/>
|
||||
|
||||
<!-- 包含的SQL模板,相当于反向LIKE,不同的JDBC驱动的SQL语句不一样,Redkale内置了MySQL的语句 -->
|
||||
<property name="javax.persistence.contain.sqltemplate" value="LOCATE(${keystr}, ${column}) > 0"/>
|
||||
@@ -41,14 +34,14 @@
|
||||
|
||||
<!-- 复制表结构的SQL模板,Redkale内置了MySQL的语句 -->
|
||||
<property name="javax.persistence.tablenotexist.sqlstates" value="42000;42S02"/>
|
||||
<property name="javax.persistence.tablecopy.sqltemplate" value="CREATE TABLE ${newtable} LIKE ${oldtable}"/>
|
||||
<property name="javax.persistence.tablecopy.sqltemplate" value="CREATE TABLE IF NOT EXISTS ${newtable} LIKE ${oldtable}"/>
|
||||
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
<!-- IM消息库 -->
|
||||
<persistence-unit name="demoim">
|
||||
<properties>
|
||||
<!-- jdbc:mysql://127.0.0.1:3306/dbim?autoReconnect=true&autoReconnectForPools=true&characterEncoding=utf8 -->
|
||||
<!-- jdbc:mysql://127.0.0.1:3306/dbim?allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&serverTimezone=UTC&characterEncoding=utf8 -->
|
||||
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/dbim?characterEncoding=utf8"/>
|
||||
<property name="javax.persistence.jdbc.user" value="root"/>
|
||||
<property name="javax.persistence.jdbc.password" value="123456"/>
|
||||
50
src/main/java/META-INF/source-template.properties
Normal file
50
src/main/java/META-INF/source-template.properties
Normal file
@@ -0,0 +1,50 @@
|
||||
|
||||
# CacheSource @Resource(name="usersession")
|
||||
# type\u53ef\u4ee5\u4e0d\u7528\u8bbe\u7f6e\uff0c\u6846\u67b6\u4f1a\u6839\u636eurl\u5224\u65ad\u4f7f\u7528\u54ea\u4e2aCacheSource\u5b9e\u73b0\u7c7b
|
||||
redkale.cachesource[usersession].type = org.redkalex.cache.redis.RedisCacheSource
|
||||
# \u6700\u5927\u8fde\u63a5\u6570
|
||||
redkale.cachesource[usersession].maxconns = 16
|
||||
# \u8282\u70b9\u5730\u5740
|
||||
redkale.cachesource[usersession].node[0].url = redis://127.0.0.1:6363
|
||||
# \u8282\u70b9\u5bc6\u7801
|
||||
redkale.cachesource[usersession].node[0].password = 12345678
|
||||
# \u8282\u70b9db
|
||||
redkale.cachesource[usersession].node[0].db = 0
|
||||
|
||||
#\u7b80\u5316\u5199\u6cd5: \u53ef\u4ee5\u4e0d\u7528.node[0], \u5c06\u53c2\u6570\u90fd\u5408\u5e76\u5230url\u4e2d
|
||||
redkale.cachesource[usersession].url = redis://user:123456@127.0.0.1:6363?db=0
|
||||
|
||||
|
||||
# DataSource @Resource(name="platf")
|
||||
# type\u53ef\u4ee5\u4e0d\u7528\u8bbe\u7f6e\uff0c\u6846\u67b6\u4f1a\u6839\u636eurl\u5224\u65ad\u4f7f\u7528\u54ea\u4e2aDataSource\u5b9e\u73b0\u7c7b\uff0c\u9ed8\u8ba4\u503c: org.redkale.source.DataJdbcSource
|
||||
redkale.datasource[platf].type = org.redkale.source.DataJdbcSource
|
||||
# \u662f\u5426\u5f00\u542f\u7f13\u5b58(\u6807\u8bb0\u4e3a@Cacheable\u7684Entity\u7c7b)\uff0c\u503c\u76ee\u524d\u53ea\u652f\u6301\u4e24\u79cd\uff1a ALL: \u6240\u6709\u5f00\u542f\u7f13\u5b58\u3002 NONE: \u5173\u95ed\u6240\u6709\u7f13\u5b58\uff0c \u975eNONE\u5b57\u6837\u7edf\u4e00\u89c6\u4e3aALL
|
||||
redkale.datasource[platf].cachemode = ALL
|
||||
# \u662f\u5426\u81ea\u52a8\u5efa\u8868\u5f53\u8868\u4e0d\u5b58\u5728\u7684\u65f6\u5019\uff0c \u76ee\u524d\u53ea\u652f\u6301mysql\u3001postgres\uff0c \u9ed8\u8ba4\u4e3afalse
|
||||
redkale.datasource[platf].table-autoddl = false
|
||||
# \u7528\u6237
|
||||
redkale.datasource[platf].user = root
|
||||
# \u5bc6\u7801
|
||||
redkale.datasource[platf].password = 12345678
|
||||
# \u591a\u4e2aURL\u7528;\u9694\u5f00\uff0c\u5982\u5206\u5e03\u5f0fSearchSource\u9700\u8981\u914d\u591a\u4e2aURL
|
||||
redkale.datasource[platf].url = jdbc:mysql://127.0.0.1:3306/platf?allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&serverTimezone=UTC&characterEncoding=utf8
|
||||
# \u6700\u5927\u8fde\u63a5\u6570\uff0c\u9ed8\u8ba4\u503c\uff1aCPU\u6570
|
||||
redkale.datasource[platf].maxconns = 16
|
||||
# \u5305\u542b\u7684SQL\u6a21\u677f\uff0c\u76f8\u5f53\u4e8e\u53cd\u5411LIKE\uff0c\u4e0d\u540c\u7684JDBC\u9a71\u52a8\u7684SQL\u8bed\u53e5\u4e0d\u4e00\u6837\uff0cRedkale\u5185\u7f6e\u4e86MySQL\u7684\u8bed\u53e5
|
||||
redkale.datasource[platf].contain-sqltemplate = LOCATE(${keystr}, ${column}) > 0
|
||||
# \u5305\u542b\u7684SQL\u6a21\u677f\uff0c\u76f8\u5f53\u4e8e\u53cd\u5411LIKE\uff0c\u4e0d\u540c\u7684JDBC\u9a71\u52a8\u7684SQL\u8bed\u53e5\u4e0d\u4e00\u6837\uff0cRedkale\u5185\u7f6e\u4e86MySQL\u7684\u8bed\u53e5
|
||||
redkale.datasource[platf].notcontain-sqltemplate = LOCATE(${keystr}, ${column}) = 0
|
||||
# \u590d\u5236\u8868\u7ed3\u6784\u7684SQL\u6a21\u677f\uff0cRedkale\u5185\u7f6e\u4e86MySQL\u7684\u8bed\u53e5
|
||||
redkale.datasource[platf].tablenotexist-sqlstates = 42000;42S02
|
||||
# \u590d\u5236\u8868\u7ed3\u6784\u7684SQL\u6a21\u677f\uff0cRedkale\u5185\u7f6e\u4e86MySQL\u7684\u8bed\u53e5
|
||||
redkale.datasource[platf].tablecopy-sqltemplate = CREATE TABLE IF NOT EXISTS ${newtable} LIKE ${oldtable}
|
||||
|
||||
|
||||
# DataSource \u8bfb\u5199\u5206\u79bb
|
||||
redkale.datasource[platf].read.url = jdbc:mysql://127.0.0.1:3306/platf_r?allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&serverTimezone=UTC&characterEncoding=utf8
|
||||
redkale.datasource[platf].read.user = root
|
||||
redkale.datasource[platf].read.password = 12345678
|
||||
|
||||
redkale.datasource[platf].write.url = jdbc:mysql://127.0.0.1:3306/platf_w?allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&serverTimezone=UTC&characterEncoding=utf8
|
||||
redkale.datasource[platf].write.user = root
|
||||
redkale.datasource[platf].write.password = 12345678
|
||||
@@ -23,11 +23,17 @@ import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 值越大,优先级越高
|
||||
*
|
||||
*
|
||||
* @since Common Annotations 1.2
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Priority {
|
||||
|
||||
/**
|
||||
* 优先级值
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int value();
|
||||
}
|
||||
83
src/main/java/javax/annotation/Resource.java
Normal file
83
src/main/java/javax/annotation/Resource.java
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package javax.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @since Common Annotations 1.0
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Resource {
|
||||
|
||||
// /**
|
||||
// * AuthenticationType
|
||||
// */
|
||||
// @Deprecated
|
||||
// public enum AuthenticationType {
|
||||
// /**
|
||||
// * @deprecated
|
||||
// */
|
||||
// CONTAINER,
|
||||
// /**
|
||||
// * @deprecated
|
||||
// */
|
||||
// APPLICATION
|
||||
// }
|
||||
|
||||
/**
|
||||
* 资源名称
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String name() default "";
|
||||
|
||||
/**
|
||||
* 依赖注入的类型
|
||||
*
|
||||
* @return Class
|
||||
*/
|
||||
public Class<?> type() default Object.class;
|
||||
//
|
||||
// /**
|
||||
// *
|
||||
// * @return AuthenticationType
|
||||
// */
|
||||
// @Deprecated
|
||||
// public AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
|
||||
//
|
||||
// /**
|
||||
// *
|
||||
// * @return boolean
|
||||
// */
|
||||
// @Deprecated
|
||||
// public boolean shareable() default true;
|
||||
//
|
||||
// /**
|
||||
// *
|
||||
// * @return String
|
||||
// */
|
||||
// @Deprecated
|
||||
// public String description() default "";
|
||||
//
|
||||
// /**
|
||||
// *
|
||||
// * @return String
|
||||
// */
|
||||
// @Deprecated
|
||||
// public String mappedName() default "";
|
||||
//
|
||||
// /**
|
||||
// *
|
||||
// * @return String
|
||||
// */
|
||||
// @Deprecated
|
||||
// public String lookup() default "";
|
||||
}
|
||||
@@ -51,4 +51,12 @@ public @interface Cacheable {
|
||||
* @return int
|
||||
*/
|
||||
int interval() default 0;
|
||||
|
||||
/**
|
||||
* DataSource是否直接返回对象的真实引用, 而不是copy一份
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
boolean direct() default false;
|
||||
|
||||
}
|
||||
@@ -81,12 +81,19 @@ public @interface Column {
|
||||
boolean unique() default false;
|
||||
|
||||
/**
|
||||
* (Optional) Whether the database column is nullable.
|
||||
* (Optional) Whether the database column is required.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
boolean nullable() default true;
|
||||
|
||||
/**
|
||||
* for OpenAPI Specification 3
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String example() default "";
|
||||
|
||||
/**
|
||||
* (Optional) Whether the column is included in SQL INSERT
|
||||
* statements generated by the persistence provider.
|
||||
@@ -109,11 +116,18 @@ public @interface Column {
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
@Deprecated
|
||||
String table() default "";
|
||||
|
||||
/**
|
||||
* (Optional) The column length. (Applies only if a
|
||||
* string-valued column is used.)
|
||||
* if type==String and length == 65535 then sqltype is TEXT <br>
|
||||
* if type==String and length <= 16777215 then sqltype is MEDIUMTEXT <br>
|
||||
* if type==String and length > 16777215 then sqltype is LONGTEXT <br>
|
||||
* if type==byte[] and length <= 65535 then sqltype is BLOB <br>
|
||||
* if type==byte[] and length <= 16777215 then sqltype is MEDIUMBLOB <br>
|
||||
* if type==byte[] and length > 16777215 then sqltype is LONGBLOB <br>
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@@ -15,9 +15,7 @@
|
||||
******************************************************************************/
|
||||
package javax.persistence;
|
||||
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.*;
|
||||
import static java.lang.annotation.ElementType.TYPE;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
@@ -27,6 +25,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
*
|
||||
* @since Java Persistence 1.0
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target(TYPE)
|
||||
@Retention(RUNTIME)
|
||||
@@ -82,6 +82,11 @@ public @interface Table {
|
||||
*/
|
||||
Index[] indexes() default {};
|
||||
|
||||
/**
|
||||
* comment
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String comment() default "";
|
||||
|
||||
}
|
||||
@@ -4,25 +4,29 @@
|
||||
*
|
||||
* @author zhangjx
|
||||
*
|
||||
module org.redkale {
|
||||
*/
|
||||
module redkale {
|
||||
|
||||
requires java.base;
|
||||
requires java.logging;
|
||||
requires java.xml;
|
||||
requires java.logging;
|
||||
requires java.net.http;
|
||||
requires java.sql;
|
||||
|
||||
requires jdk.unsupported; //sun.misc.Unsafe
|
||||
|
||||
exports javax.annotation;
|
||||
exports javax.persistence;
|
||||
|
||||
exports org.redkale.asm;
|
||||
exports org.redkale.boot;
|
||||
exports org.redkale.boot.watch;
|
||||
exports org.redkale.cluster;
|
||||
exports org.redkale.convert;
|
||||
exports org.redkale.convert.bson;
|
||||
exports org.redkale.convert.ext;
|
||||
exports org.redkale.convert.json;
|
||||
exports org.redkale.mq;
|
||||
exports org.redkale.net;
|
||||
exports org.redkale.net.client;
|
||||
exports org.redkale.net.http;
|
||||
exports org.redkale.net.sncp;
|
||||
exports org.redkale.service;
|
||||
@@ -30,7 +34,11 @@ module org.redkale {
|
||||
exports org.redkale.util;
|
||||
exports org.redkale.watch;
|
||||
|
||||
uses org.redkale.source.SourceLoader;
|
||||
uses org.redkale.util.ResourceInjectLoader;
|
||||
uses org.redkale.mq.MessageAgent;
|
||||
uses org.redkale.cluster.ClusterAgent;
|
||||
uses org.redkale.convert.ConvertProvider;
|
||||
uses org.redkale.source.CacheSourceProvider;
|
||||
uses org.redkale.source.DataSourceProvider;
|
||||
uses org.redkale.util.ResourceAnnotationProvider;
|
||||
|
||||
}
|
||||
*/
|
||||
@@ -60,8 +60,8 @@ package org.redkale.asm;
|
||||
|
||||
/**
|
||||
* A visitor to visit a Java annotation. The methods of this class must be
|
||||
* called in the following order: ( <tt>visit</tt> | <tt>visitEnum</tt> |
|
||||
* <tt>visitAnnotation</tt> | <tt>visitArray</tt> )* <tt>visitEnd</tt>.
|
||||
* called in the following order: ( <tt>visit</tt> | <tt>visitEnum</tt> |
|
||||
* <tt>visitAnnotation</tt> | <tt>visitArray</tt> )* <tt>visitEnd</tt>.
|
||||
*
|
||||
* @author Eric Bruneton
|
||||
* @author Eugene Kuleshov
|
||||
@@ -151,7 +151,7 @@ public abstract class AnnotationVisitor {
|
||||
* @param desc
|
||||
* the class descriptor of the nested annotation class.
|
||||
* @return a visitor to visit the actual nested annotation value, or
|
||||
* <tt>null</tt> if this visitor is not interested in visiting this
|
||||
* <tt>null</tt> if this visitor is not interested in visiting this
|
||||
* nested annotation. <i>The nested annotation value must be fully
|
||||
* visited before calling other methods on this annotation
|
||||
* visitor</i>.
|
||||
@@ -172,7 +172,7 @@ public abstract class AnnotationVisitor {
|
||||
* @param name
|
||||
* the value name.
|
||||
* @return a visitor to visit the actual array value elements, or
|
||||
* <tt>null</tt> if this visitor is not interested in visiting these
|
||||
* <tt>null</tt> if this visitor is not interested in visiting these
|
||||
* values. The 'name' parameters passed to the methods of this
|
||||
* visitor are ignored. <i>All the array values must be visited
|
||||
* before calling other methods on this annotation visitor</i>.
|
||||
@@ -77,7 +77,7 @@ final class AnnotationWriter extends AnnotationVisitor {
|
||||
private int size;
|
||||
|
||||
/**
|
||||
* <tt>true<tt> if values are named, <tt>false</tt> otherwise. Annotation
|
||||
* <tt>true<tt> if values are named, <tt>false</tt> otherwise. Annotation
|
||||
* writers used for annotation default and annotation arrays use unnamed
|
||||
* values.
|
||||
*/
|
||||
@@ -122,13 +122,13 @@ final class AnnotationWriter extends AnnotationVisitor {
|
||||
* @param cw
|
||||
* the class writer to which this annotation must be added.
|
||||
* @param named
|
||||
* <tt>true<tt> if values are named, <tt>false</tt> otherwise.
|
||||
* <tt>true<tt> if values are named, <tt>false</tt> otherwise.
|
||||
* @param bv
|
||||
* where the annotation values must be stored.
|
||||
* @param parent
|
||||
* where the number of annotation values must be stored.
|
||||
* @param offset
|
||||
* where in <tt>parent</tt> the number of annotation values must
|
||||
* where in <tt>parent</tt> the number of annotation values must
|
||||
* be stored.
|
||||
*/
|
||||
AnnotationWriter(final ClassWriter cw, final boolean named,
|
||||
@@ -354,7 +354,7 @@ final class AnnotationWriter extends AnnotationVisitor {
|
||||
* @param typePath
|
||||
* the path to the annotated type argument, wildcard bound, array
|
||||
* element type, or static inner type within 'typeRef'. May be
|
||||
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
|
||||
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
|
||||
* @param out
|
||||
* where the type reference and type path must be put.
|
||||
*/
|
||||
@@ -58,13 +58,15 @@
|
||||
*/
|
||||
package org.redkale.asm;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* A non standard class, field, method or code attribute.
|
||||
*
|
||||
* @author Eric Bruneton
|
||||
* @author Eugene Kuleshov
|
||||
*/
|
||||
class Attribute {
|
||||
public class Attribute {
|
||||
|
||||
/**
|
||||
* The type of this attribute.
|
||||
@@ -77,7 +79,7 @@ class Attribute {
|
||||
byte[] value;
|
||||
|
||||
/**
|
||||
* The next attribute in this attribute list. May be <tt>null</tt>.
|
||||
* The next attribute in this attribute list. May be <tt>null</tt>.
|
||||
*/
|
||||
Attribute next;
|
||||
|
||||
@@ -92,19 +94,19 @@ class Attribute {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <tt>true</tt> if this type of attribute is unknown. The default
|
||||
* implementation of this method always returns <tt>true</tt>.
|
||||
* Returns <tt>true</tt> if this type of attribute is unknown. The default
|
||||
* implementation of this method always returns <tt>true</tt>.
|
||||
*
|
||||
* @return <tt>true</tt> if this type of attribute is unknown.
|
||||
* @return <tt>true</tt> if this type of attribute is unknown.
|
||||
*/
|
||||
public boolean isUnknown() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <tt>true</tt> if this type of attribute is a code attribute.
|
||||
* Returns <tt>true</tt> if this type of attribute is a code attribute.
|
||||
*
|
||||
* @return <tt>true</tt> if this type of attribute is a code attribute.
|
||||
* @return <tt>true</tt> if this type of attribute is a code attribute.
|
||||
*/
|
||||
public boolean isCodeAttribute() {
|
||||
return false;
|
||||
@@ -113,7 +115,7 @@ class Attribute {
|
||||
/**
|
||||
* Returns the labels corresponding to this attribute.
|
||||
*
|
||||
* @return the labels corresponding to this attribute, or <tt>null</tt> if
|
||||
* @return the labels corresponding to this attribute, or <tt>null</tt> if
|
||||
* this attribute is not a code attribute that contains labels.
|
||||
*/
|
||||
protected Label[] getLabels() {
|
||||
@@ -123,7 +125,7 @@ class Attribute {
|
||||
/**
|
||||
* Reads a {@link #type type} attribute. This method must return a
|
||||
* <i>new</i> {@link Attribute} object, of type {@link #type type},
|
||||
* corresponding to the <tt>len</tt> bytes starting at the given offset, in
|
||||
* corresponding to the <tt>len</tt> bytes starting at the given offset, in
|
||||
* the given class reader.
|
||||
*
|
||||
* @param cr
|
||||
@@ -146,7 +148,7 @@ class Attribute {
|
||||
* containing the type and the length of the attribute, are not
|
||||
* taken into account here.
|
||||
* @param labels
|
||||
* the labels of the method's code, or <tt>null</tt> if the
|
||||
* the labels of the method's code, or <tt>null</tt> if the
|
||||
* attribute to be read is not a code attribute.
|
||||
* @return a <i>new</i> {@link Attribute} object corresponding to the given
|
||||
* bytes.
|
||||
@@ -169,11 +171,11 @@ class Attribute {
|
||||
* class the items that corresponds to this attribute.
|
||||
* @param code
|
||||
* the bytecode of the method corresponding to this code
|
||||
* attribute, or <tt>null</tt> if this attribute is not a code
|
||||
* attribute, or <tt>null</tt> if this attribute is not a code
|
||||
* attributes.
|
||||
* @param len
|
||||
* the length of the bytecode of the method corresponding to this
|
||||
* code attribute, or <tt>null</tt> if this attribute is not a
|
||||
* code attribute, or <tt>null</tt> if this attribute is not a
|
||||
* code attribute.
|
||||
* @param maxStack
|
||||
* the maximum stack size of the method corresponding to this
|
||||
@@ -216,11 +218,11 @@ class Attribute {
|
||||
* byte arrays, with the {@link #write write} method.
|
||||
* @param code
|
||||
* the bytecode of the method corresponding to these code
|
||||
* attributes, or <tt>null</tt> if these attributes are not code
|
||||
* attributes, or <tt>null</tt> if these attributes are not code
|
||||
* attributes.
|
||||
* @param len
|
||||
* the length of the bytecode of the method corresponding to
|
||||
* these code attributes, or <tt>null</tt> if these attributes
|
||||
* these code attributes, or <tt>null</tt> if these attributes
|
||||
* are not code attributes.
|
||||
* @param maxStack
|
||||
* the maximum stack size of the method corresponding to these
|
||||
@@ -254,11 +256,11 @@ class Attribute {
|
||||
* byte arrays, with the {@link #write write} method.
|
||||
* @param code
|
||||
* the bytecode of the method corresponding to these code
|
||||
* attributes, or <tt>null</tt> if these attributes are not code
|
||||
* attributes, or <tt>null</tt> if these attributes are not code
|
||||
* attributes.
|
||||
* @param len
|
||||
* the length of the bytecode of the method corresponding to
|
||||
* these code attributes, or <tt>null</tt> if these attributes
|
||||
* these code attributes, or <tt>null</tt> if these attributes
|
||||
* are not code attributes.
|
||||
* @param maxStack
|
||||
* the maximum stack size of the method corresponding to these
|
||||
@@ -281,4 +283,82 @@ class Attribute {
|
||||
attr = attr.next;
|
||||
}
|
||||
}
|
||||
|
||||
//The stuff below is temporary - once proper support for nestmate attribute has been added, it can be safely removed.
|
||||
//see also changes in ClassReader.accept.
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static class NestMembers extends Attribute {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public NestMembers() {
|
||||
super("NestMembers");
|
||||
}
|
||||
|
||||
byte[] bytes;
|
||||
String[] classes;
|
||||
|
||||
@Override
|
||||
protected Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) {
|
||||
int offset = off;
|
||||
NestMembers a = new NestMembers();
|
||||
int size = cr.readShort(off);
|
||||
a.classes = new String[size];
|
||||
off += 2;
|
||||
for (int i = 0; i < size ; i++) {
|
||||
a.classes[i] = cr.readClass(off, buf);
|
||||
off += 2;
|
||||
}
|
||||
a.bytes = Arrays.copyOfRange(cr.b, offset, offset + len);
|
||||
return a;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) {
|
||||
ByteVector v = new ByteVector(bytes.length);
|
||||
v.putShort(classes.length);
|
||||
for (String s : classes) {
|
||||
v.putShort(cw.newClass(s));
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static class NestHost extends Attribute {
|
||||
|
||||
byte[] bytes;
|
||||
String clazz;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public NestHost() {
|
||||
super("NestHost");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) {
|
||||
int offset = off;
|
||||
NestHost a = new NestHost();
|
||||
a.clazz = cr.readClass(off, buf);
|
||||
a.bytes = Arrays.copyOfRange(cr.b, offset, offset + len);
|
||||
return a;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) {
|
||||
ByteVector v = new ByteVector(bytes.length);
|
||||
v.putShort(cw.newClass(clazz));
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
static final Attribute[] DEFAULT_ATTRIBUTE_PROTOS = new Attribute[] {
|
||||
new NestMembers(),
|
||||
new NestHost()
|
||||
};
|
||||
}
|
||||
@@ -332,7 +332,7 @@ public class ByteVector {
|
||||
* automatically enlarged if necessary.
|
||||
*
|
||||
* @param b
|
||||
* an array of bytes. May be <tt>null</tt> to put <tt>len</tt>
|
||||
* an array of bytes. May be <tt>null</tt> to put <tt>len</tt>
|
||||
* null bytes into this byte vector.
|
||||
* @param off
|
||||
* index of the fist byte of b that must be copied.
|
||||
@@ -185,9 +185,9 @@ public class ClassReader {
|
||||
public ClassReader(final byte[] b, final int off, final int len) {
|
||||
this.b = b;
|
||||
// checks the class version
|
||||
if (readShort(off + 6) > Opcodes.V10) {
|
||||
//throw new IllegalArgumentException();
|
||||
}
|
||||
//if (readShort(off + 6) > Opcodes.V11) {
|
||||
// throw new IllegalArgumentException();
|
||||
//}
|
||||
// parses the constant pool
|
||||
items = new int[readUnsignedShort(off + 8)];
|
||||
int n = items.length;
|
||||
@@ -205,6 +205,10 @@ public class ClassReader {
|
||||
case ClassWriter.FLOAT:
|
||||
case ClassWriter.NAME_TYPE:
|
||||
case ClassWriter.INDY:
|
||||
// @@@ ClassWriter.CONDY
|
||||
// Enables MethodHandles.lookup().defineClass to function correctly
|
||||
// when it reads the class name
|
||||
case 17:
|
||||
size = 5;
|
||||
break;
|
||||
case ClassWriter.LONG:
|
||||
@@ -267,7 +271,7 @@ public class ClassReader {
|
||||
* {@link Type#getInternalName() getInternalName}). For interfaces, the
|
||||
* super class is {@link Object}.
|
||||
*
|
||||
* @return the internal name of super class, or <tt>null</tt> for
|
||||
* @return the internal name of super class, or <tt>null</tt> for
|
||||
* {@link Object} class.
|
||||
*
|
||||
* @see ClassVisitor#visit(int, int, String, String, String, String[])
|
||||
@@ -281,7 +285,7 @@ public class ClassReader {
|
||||
* {@link Type#getInternalName() getInternalName}).
|
||||
*
|
||||
* @return the array of internal names for all implemented interfaces or
|
||||
* <tt>null</tt>.
|
||||
* <tt>null</tt>.
|
||||
*
|
||||
* @see ClassVisitor#visit(int, int, String, String, String, String[])
|
||||
*/
|
||||
@@ -526,7 +530,7 @@ public class ClassReader {
|
||||
* , {@link #SKIP_FRAMES}, {@link #SKIP_CODE}.
|
||||
*/
|
||||
public void accept(final ClassVisitor classVisitor, final int flags) {
|
||||
accept(classVisitor, new Attribute[0], flags);
|
||||
accept(classVisitor, Attribute.DEFAULT_ATTRIBUTE_PROTOS, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1932,7 +1936,7 @@ public class ClassReader {
|
||||
* @param v
|
||||
* start offset in {@link #b b} of the annotations to be read.
|
||||
* @param visible
|
||||
* <tt>true</tt> if the annotations to be read are visible at
|
||||
* <tt>true</tt> if the annotations to be read are visible at
|
||||
* runtime.
|
||||
*/
|
||||
private void readParameterAnnotations(final MethodVisitor mv,
|
||||
@@ -2474,9 +2478,9 @@ public class ClassReader {
|
||||
* and the length of the attribute, are not taken into account
|
||||
* here.
|
||||
* @param labels
|
||||
* the labels of the method's code, or <tt>null</tt> if the
|
||||
* the labels of the method's code, or <tt>null</tt> if the
|
||||
* attribute to be read is not a code attribute.
|
||||
* @return the attribute that has been read, or <tt>null</tt> to skip this
|
||||
* @return the attribute that has been read, or <tt>null</tt> to skip this
|
||||
* attribute.
|
||||
*/
|
||||
private Attribute readAttribute(final Attribute[] attrs, final String type,
|
||||
@@ -60,11 +60,11 @@ package org.redkale.asm;
|
||||
|
||||
/**
|
||||
* A visitor to visit a Java class. The methods of this class must be called in
|
||||
* the following order: <tt>visit</tt> [ <tt>visitSource</tt> ] [
|
||||
* <tt>visitModule</tt> ][ <tt>visitOuterClass</tt> ] ( <tt>visitAnnotation</tt> |
|
||||
* <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* (
|
||||
* <tt>visitInnerClass</tt> | <tt>visitField</tt> | <tt>visitMethod</tt> )*
|
||||
* <tt>visitEnd</tt>.
|
||||
* the following order: <tt>visit</tt> [ <tt>visitSource</tt> ] [
|
||||
* <tt>visitModule</tt> ][ <tt>visitOuterClass</tt> ] ( <tt>visitAnnotation</tt> |
|
||||
* <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* (
|
||||
* <tt>visitInnerClass</tt> | <tt>visitField</tt> | <tt>visitMethod</tt> )*
|
||||
* <tt>visitEnd</tt>.
|
||||
*
|
||||
* @author Eric Bruneton
|
||||
*/
|
||||
@@ -120,18 +120,18 @@ public abstract class ClassVisitor {
|
||||
* the internal name of the class (see
|
||||
* {@link Type#getInternalName() getInternalName}).
|
||||
* @param signature
|
||||
* the signature of this class. May be <tt>null</tt> if the class
|
||||
* the signature of this class. May be <tt>null</tt> if the class
|
||||
* is not a generic one, and does not extend or implement generic
|
||||
* classes or interfaces.
|
||||
* @param superName
|
||||
* the internal of name of the super class (see
|
||||
* {@link Type#getInternalName() getInternalName}). For
|
||||
* interfaces, the super class is {@link Object}. May be
|
||||
* <tt>null</tt>, but only for the {@link Object} class.
|
||||
* <tt>null</tt>, but only for the {@link Object} class.
|
||||
* @param interfaces
|
||||
* the internal names of the class's interfaces (see
|
||||
* {@link Type#getInternalName() getInternalName}). May be
|
||||
* <tt>null</tt>.
|
||||
* <tt>null</tt>.
|
||||
*/
|
||||
public void visit(int version, int access, String name, String signature,
|
||||
String superName, String[] interfaces) {
|
||||
@@ -145,11 +145,11 @@ public abstract class ClassVisitor {
|
||||
*
|
||||
* @param source
|
||||
* the name of the source file from which the class was compiled.
|
||||
* May be <tt>null</tt>.
|
||||
* May be <tt>null</tt>.
|
||||
* @param debug
|
||||
* additional debug information to compute the correspondance
|
||||
* between source and compiled elements of the class. May be
|
||||
* <tt>null</tt>.
|
||||
* <tt>null</tt>.
|
||||
*/
|
||||
public void visitSource(String source, String debug) {
|
||||
if (cv != null) {
|
||||
@@ -166,7 +166,7 @@ public abstract class ClassVisitor {
|
||||
* and {@code ACC_MANDATED}.
|
||||
* @param version
|
||||
* module version or null.
|
||||
* @return a visitor to visit the module values, or <tt>null</tt> if
|
||||
* @return a visitor to visit the module values, or <tt>null</tt> if
|
||||
* this visitor is not interested in visiting this module.
|
||||
*/
|
||||
public ModuleVisitor visitModule(String name, int access, String version) {
|
||||
@@ -187,11 +187,11 @@ public abstract class ClassVisitor {
|
||||
* internal name of the enclosing class of the class.
|
||||
* @param name
|
||||
* the name of the method that contains the class, or
|
||||
* <tt>null</tt> if the class is not enclosed in a method of its
|
||||
* <tt>null</tt> if the class is not enclosed in a method of its
|
||||
* enclosing class.
|
||||
* @param desc
|
||||
* the descriptor of the method that contains the class, or
|
||||
* <tt>null</tt> if the class is not enclosed in a method of its
|
||||
* <tt>null</tt> if the class is not enclosed in a method of its
|
||||
* enclosing class.
|
||||
*/
|
||||
public void visitOuterClass(String owner, String name, String desc) {
|
||||
@@ -206,8 +206,8 @@ public abstract class ClassVisitor {
|
||||
* @param desc
|
||||
* the class descriptor of the annotation class.
|
||||
* @param visible
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
||||
* this visitor is not interested in visiting this annotation.
|
||||
*/
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
@@ -231,12 +231,12 @@ public abstract class ClassVisitor {
|
||||
* @param typePath
|
||||
* the path to the annotated type argument, wildcard bound, array
|
||||
* element type, or static inner type within 'typeRef'. May be
|
||||
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
|
||||
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
|
||||
* @param desc
|
||||
* the class descriptor of the annotation class.
|
||||
* @param visible
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
||||
* this visitor is not interested in visiting this annotation.
|
||||
*/
|
||||
public AnnotationVisitor visitTypeAnnotation(int typeRef,
|
||||
@@ -269,10 +269,10 @@ public abstract class ClassVisitor {
|
||||
* @param outerName
|
||||
* the internal name of the class to which the inner class
|
||||
* belongs (see {@link Type#getInternalName() getInternalName}).
|
||||
* May be <tt>null</tt> for not member classes.
|
||||
* May be <tt>null</tt> for not member classes.
|
||||
* @param innerName
|
||||
* the (simple) name of the inner class inside its enclosing
|
||||
* class. May be <tt>null</tt> for anonymous inner classes.
|
||||
* class. May be <tt>null</tt> for anonymous inner classes.
|
||||
* @param access
|
||||
* the access flags of the inner class as originally declared in
|
||||
* the enclosing class.
|
||||
@@ -295,20 +295,20 @@ public abstract class ClassVisitor {
|
||||
* @param desc
|
||||
* the field's descriptor (see {@link Type Type}).
|
||||
* @param signature
|
||||
* the field's signature. May be <tt>null</tt> if the field's
|
||||
* the field's signature. May be <tt>null</tt> if the field's
|
||||
* type does not use generic types.
|
||||
* @param value
|
||||
* the field's initial value. This parameter, which may be
|
||||
* <tt>null</tt> if the field does not have an initial value,
|
||||
* <tt>null</tt> if the field does not have an initial value,
|
||||
* must be an {@link Integer}, a {@link Float}, a {@link Long}, a
|
||||
* {@link Double} or a {@link String} (for <tt>int</tt>,
|
||||
* <tt>float</tt>, <tt>long</tt> or <tt>String</tt> fields
|
||||
* {@link Double} or a {@link String} (for <tt>int</tt>,
|
||||
* <tt>float</tt>, <tt>long</tt> or <tt>String</tt> fields
|
||||
* respectively). <i>This parameter is only used for static
|
||||
* fields</i>. Its value is ignored for non static fields, which
|
||||
* must be initialized through bytecode instructions in
|
||||
* constructors or methods.
|
||||
* @return a visitor to visit field annotations and attributes, or
|
||||
* <tt>null</tt> if this class visitor is not interested in visiting
|
||||
* <tt>null</tt> if this class visitor is not interested in visiting
|
||||
* these annotations and attributes.
|
||||
*/
|
||||
public FieldVisitor visitField(int access, String name, String desc,
|
||||
@@ -321,7 +321,7 @@ public abstract class ClassVisitor {
|
||||
|
||||
/**
|
||||
* Visits a method of the class. This method <i>must</i> return a new
|
||||
* {@link MethodVisitor} instance (or <tt>null</tt>) each time it is called,
|
||||
* {@link MethodVisitor} instance (or <tt>null</tt>) each time it is called,
|
||||
* i.e., it should not return a previously returned visitor.
|
||||
*
|
||||
* @param access
|
||||
@@ -333,14 +333,14 @@ public abstract class ClassVisitor {
|
||||
* @param desc
|
||||
* the method's descriptor (see {@link Type Type}).
|
||||
* @param signature
|
||||
* the method's signature. May be <tt>null</tt> if the method
|
||||
* the method's signature. May be <tt>null</tt> if the method
|
||||
* parameters, return type and exceptions do not use generic
|
||||
* types.
|
||||
* @param exceptions
|
||||
* the internal names of the method's exception classes (see
|
||||
* {@link Type#getInternalName() getInternalName}). May be
|
||||
* <tt>null</tt>.
|
||||
* @return an object to visit the byte code of the method, or <tt>null</tt>
|
||||
* <tt>null</tt>.
|
||||
* @return an object to visit the byte code of the method, or <tt>null</tt>
|
||||
* if this class visitor is not interested in visiting the code of
|
||||
* this method.
|
||||
*/
|
||||
@@ -391,8 +391,8 @@ public class ClassWriter extends ClassVisitor {
|
||||
* A type table used to temporarily store internal names that will not
|
||||
* necessarily be stored in the constant pool. This type table is used by
|
||||
* the control flow and data flow analysis algorithm used to compute stack
|
||||
* map frames from scratch. This array associates to each index <tt>i</tt>
|
||||
* the Item whose index is <tt>i</tt>. All Item objects stored in this array
|
||||
* map frames from scratch. This array associates to each index <tt>i</tt>
|
||||
* the Item whose index is <tt>i</tt>. All Item objects stored in this array
|
||||
* are also stored in the {@link #items} hash table. These two arrays allow
|
||||
* to retrieve an Item from its index or, conversely, to get the index of an
|
||||
* Item from its value. Each Item stores an internal name in its
|
||||
@@ -556,7 +556,7 @@ public class ClassWriter extends ClassVisitor {
|
||||
private int compute;
|
||||
|
||||
/**
|
||||
* <tt>true</tt> if some methods have wide forward jumps using ASM pseudo
|
||||
* <tt>true</tt> if some methods have wide forward jumps using ASM pseudo
|
||||
* instructions, which need to be expanded into sequences of standard
|
||||
* bytecode instructions. In this case the class is re-read and re-written
|
||||
* with a ClassReader -> ClassWriter chain to perform this transformation.
|
||||
@@ -1486,7 +1486,7 @@ public class ClassWriter extends ClassVisitor {
|
||||
* @param desc
|
||||
* the method's descriptor.
|
||||
* @param itf
|
||||
* <tt>true</tt> if <tt>owner</tt> is an interface.
|
||||
* <tt>true</tt> if <tt>owner</tt> is an interface.
|
||||
* @return a new or already existing method reference item.
|
||||
*/
|
||||
Item newMethodItem(final String owner, final String name,
|
||||
@@ -1515,7 +1515,7 @@ public class ClassWriter extends ClassVisitor {
|
||||
* @param desc
|
||||
* the method's descriptor.
|
||||
* @param itf
|
||||
* <tt>true</tt> if <tt>owner</tt> is an interface.
|
||||
* <tt>true</tt> if <tt>owner</tt> is an interface.
|
||||
* @return the index of a new or already existing method reference item.
|
||||
*/
|
||||
public int newMethod(final String owner, final String name,
|
||||
@@ -1778,7 +1778,7 @@ public class ClassWriter extends ClassVisitor {
|
||||
* @param key
|
||||
* a constant pool item.
|
||||
* @return the constant pool's hash table item which is equal to the given
|
||||
* item, or <tt>null</tt> if there is no such item.
|
||||
* item, or <tt>null</tt> if there is no such item.
|
||||
*/
|
||||
private Item get(final Item key) {
|
||||
Item i = items[key.hashCode % items.length];
|
||||
@@ -60,8 +60,8 @@ package org.redkale.asm;
|
||||
|
||||
/**
|
||||
* A visitor to visit a Java field. The methods of this class must be called in
|
||||
* the following order: ( <tt>visitAnnotation</tt> |
|
||||
* <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* <tt>visitEnd</tt>.
|
||||
* the following order: ( <tt>visitAnnotation</tt> |
|
||||
* <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* <tt>visitEnd</tt>.
|
||||
*
|
||||
* @author Eric Bruneton
|
||||
*/
|
||||
@@ -111,8 +111,8 @@ public abstract class FieldVisitor {
|
||||
* @param desc
|
||||
* the class descriptor of the annotation class.
|
||||
* @param visible
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
||||
* this visitor is not interested in visiting this annotation.
|
||||
*/
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
@@ -132,12 +132,12 @@ public abstract class FieldVisitor {
|
||||
* @param typePath
|
||||
* the path to the annotated type argument, wildcard bound, array
|
||||
* element type, or static inner type within 'typeRef'. May be
|
||||
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
|
||||
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
|
||||
* @param desc
|
||||
* the class descriptor of the annotation class.
|
||||
* @param visible
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
||||
* this visitor is not interested in visiting this annotation.
|
||||
*/
|
||||
public AnnotationVisitor visitTypeAnnotation(int typeRef,
|
||||
@@ -100,28 +100,28 @@ final class FieldWriter extends FieldVisitor {
|
||||
private int value;
|
||||
|
||||
/**
|
||||
* The runtime visible annotations of this field. May be <tt>null</tt>.
|
||||
* The runtime visible annotations of this field. May be <tt>null</tt>.
|
||||
*/
|
||||
private AnnotationWriter anns;
|
||||
|
||||
/**
|
||||
* The runtime invisible annotations of this field. May be <tt>null</tt>.
|
||||
* The runtime invisible annotations of this field. May be <tt>null</tt>.
|
||||
*/
|
||||
private AnnotationWriter ianns;
|
||||
|
||||
/**
|
||||
* The runtime visible type annotations of this field. May be <tt>null</tt>.
|
||||
* The runtime visible type annotations of this field. May be <tt>null</tt>.
|
||||
*/
|
||||
private AnnotationWriter tanns;
|
||||
|
||||
/**
|
||||
* The runtime invisible type annotations of this field. May be
|
||||
* <tt>null</tt>.
|
||||
* <tt>null</tt>.
|
||||
*/
|
||||
private AnnotationWriter itanns;
|
||||
|
||||
/**
|
||||
* The non standard attributes of this field. May be <tt>null</tt>.
|
||||
* The non standard attributes of this field. May be <tt>null</tt>.
|
||||
*/
|
||||
private Attribute attrs;
|
||||
|
||||
@@ -141,9 +141,9 @@ final class FieldWriter extends FieldVisitor {
|
||||
* @param desc
|
||||
* the field's descriptor (see {@link Type}).
|
||||
* @param signature
|
||||
* the field's signature. May be <tt>null</tt>.
|
||||
* the field's signature. May be <tt>null</tt>.
|
||||
* @param value
|
||||
* the field's constant value. May be <tt>null</tt>.
|
||||
* the field's constant value. May be <tt>null</tt>.
|
||||
*/
|
||||
FieldWriter(final ClassWriter cw, final int access, final String name,
|
||||
final String desc, final String signature, final Object value) {
|
||||
@@ -1403,7 +1403,7 @@ class Frame {
|
||||
|
||||
/**
|
||||
* Merges the input frame of the given basic block with the input and output
|
||||
* frames of this basic block. Returns <tt>true</tt> if the input frame of
|
||||
* frames of this basic block. Returns <tt>true</tt> if the input frame of
|
||||
* the given label has been changed by this operation.
|
||||
*
|
||||
* @param cw
|
||||
@@ -1413,7 +1413,7 @@ class Frame {
|
||||
* @param edge
|
||||
* the kind of the {@link Edge} between this label and 'label'.
|
||||
* See {@link Edge#info}.
|
||||
* @return <tt>true</tt> if the input frame of the given label has been
|
||||
* @return <tt>true</tt> if the input frame of the given label has been
|
||||
* changed by this operation.
|
||||
*/
|
||||
final boolean merge(final ClassWriter cw, final Frame frame, final int edge) {
|
||||
@@ -1511,7 +1511,7 @@ class Frame {
|
||||
|
||||
/**
|
||||
* Merges the type at the given index in the given type array with the given
|
||||
* type. Returns <tt>true</tt> if the type array has been modified by this
|
||||
* type. Returns <tt>true</tt> if the type array has been modified by this
|
||||
* operation.
|
||||
*
|
||||
* @param cw
|
||||
@@ -1522,7 +1522,7 @@ class Frame {
|
||||
* an array of types.
|
||||
* @param index
|
||||
* the index of the type that must be merged in 'types'.
|
||||
* @return <tt>true</tt> if the type array has been modified by this
|
||||
* @return <tt>true</tt> if the type array has been modified by this
|
||||
* operation.
|
||||
*/
|
||||
private static boolean merge(final ClassWriter cw, int t,
|
||||
@@ -99,7 +99,6 @@ public final class Handle {
|
||||
*/
|
||||
final boolean itf;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new field or method handle.
|
||||
*
|
||||
@@ -82,7 +82,7 @@ class Handler {
|
||||
|
||||
/**
|
||||
* Internal name of the type of exceptions handled by this handler, or
|
||||
* <tt>null</tt> to catch any exceptions.
|
||||
* <tt>null</tt> to catch any exceptions.
|
||||
*/
|
||||
String desc;
|
||||
|
||||
@@ -306,8 +306,8 @@ final class Item {
|
||||
* @param i
|
||||
* the item to be compared to this one. Both items must have the
|
||||
* same {@link #type}.
|
||||
* @return <tt>true</tt> if the given item if equal to this one,
|
||||
* <tt>false</tt> otherwise.
|
||||
* @return <tt>true</tt> if the given item if equal to this one,
|
||||
* <tt>false</tt> otherwise.
|
||||
*/
|
||||
boolean isEqualTo(final Item i) {
|
||||
switch (type) {
|
||||
@@ -325,8 +325,8 @@ public class Label {
|
||||
* the position of first byte of the bytecode instruction that
|
||||
* contains this label.
|
||||
* @param wideOffset
|
||||
* <tt>true</tt> if the reference must be stored in 4 bytes, or
|
||||
* <tt>false</tt> if it must be stored with 2 bytes.
|
||||
* <tt>true</tt> if the reference must be stored in 4 bytes, or
|
||||
* <tt>false</tt> if it must be stored with 2 bytes.
|
||||
* @throws IllegalArgumentException
|
||||
* if this label has not been created by the given code writer.
|
||||
*/
|
||||
@@ -389,7 +389,7 @@ public class Label {
|
||||
* the position of this label in the bytecode.
|
||||
* @param data
|
||||
* the bytecode of the method.
|
||||
* @return <tt>true</tt> if a blank that was left for this label was too
|
||||
* @return <tt>true</tt> if a blank that was left for this label was too
|
||||
* small to store the offset. In such a case the corresponding jump
|
||||
* instruction is replaced with a pseudo instruction (using unused
|
||||
* opcodes) using an unsigned two bytes offset. These pseudo
|
||||
@@ -5,7 +5,10 @@
|
||||
*/
|
||||
package org.redkale.asm;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import static org.redkale.asm.Opcodes.*;
|
||||
|
||||
/**
|
||||
* MethodVisitor 的调试类
|
||||
@@ -54,11 +57,20 @@ public class MethodDebugVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param visitor MethodVisitor
|
||||
*/
|
||||
public MethodDebugVisitor(MethodVisitor visitor) {
|
||||
//super(Opcodes.ASM5, visitor);
|
||||
this.visitor = visitor;
|
||||
}
|
||||
|
||||
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
|
||||
visitor.visitTryCatchBlock(start, end, handler, type);
|
||||
if (debug) System.out.println("mv.visitTryCatchBlock(label0, label1, label2, \"" + type + "\");");
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitParameterAnnotation(int i, String string, boolean bln) {
|
||||
AnnotationVisitor av = visitor.visitParameterAnnotation(i, string, bln);
|
||||
if (debug) System.out.println("mv.visitParameterAnnotation(" + i + ", \"" + string + "\", " + bln + ");");
|
||||
@@ -189,4 +201,74 @@ public class MethodDebugVisitor {
|
||||
visitor.visitEnd();
|
||||
if (debug) System.out.println("mv.visitEnd();\r\n\r\n\r\n");
|
||||
}
|
||||
|
||||
public static void pushInt(MethodDebugVisitor mv, int num) {
|
||||
if (num < 6) {
|
||||
mv.visitInsn(ICONST_0 + num);
|
||||
} else if (num <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(BIPUSH, num);
|
||||
} else if (num <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(SIPUSH, num);
|
||||
} else {
|
||||
mv.visitLdcInsn(num);
|
||||
}
|
||||
}
|
||||
|
||||
public static void pushInt(MethodVisitor mv, int num) {
|
||||
if (num < 6) {
|
||||
mv.visitInsn(ICONST_0 + num);
|
||||
} else if (num <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(BIPUSH, num);
|
||||
} else if (num <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(SIPUSH, num);
|
||||
} else {
|
||||
mv.visitLdcInsn(num);
|
||||
}
|
||||
}
|
||||
|
||||
public static void visitAnnotation(final AnnotationVisitor av, final Annotation ann) {
|
||||
try {
|
||||
for (Method anm : ann.annotationType().getMethods()) {
|
||||
final String mname = anm.getName();
|
||||
if ("equals".equals(mname) || "hashCode".equals(mname) || "toString".equals(mname) || "annotationType".equals(mname)) continue;
|
||||
final Object r = anm.invoke(ann);
|
||||
if (r instanceof String[]) {
|
||||
AnnotationVisitor av1 = av.visitArray(mname);
|
||||
for (String item : (String[]) r) {
|
||||
av1.visit(null, item);
|
||||
}
|
||||
av1.visitEnd();
|
||||
} else if (r instanceof Class[]) {
|
||||
AnnotationVisitor av1 = av.visitArray(mname);
|
||||
for (Class item : (Class[]) r) {
|
||||
av1.visit(null, Type.getType(item));
|
||||
}
|
||||
av1.visitEnd();
|
||||
} else if (r instanceof Enum[]) {
|
||||
AnnotationVisitor av1 = av.visitArray(mname);
|
||||
for (Enum item : (Enum[]) r) {
|
||||
av1.visitEnum(null, Type.getDescriptor(item.getClass()), ((Enum) item).name());
|
||||
}
|
||||
av1.visitEnd();
|
||||
} else if (r instanceof Annotation[]) {
|
||||
AnnotationVisitor av1 = av.visitArray(mname);
|
||||
for (Annotation item : (Annotation[]) r) {
|
||||
visitAnnotation(av1.visitAnnotation(null, Type.getDescriptor(((Annotation) item).annotationType())), item);
|
||||
}
|
||||
av1.visitEnd();
|
||||
} else if (r instanceof Class) {
|
||||
av.visit(mname, Type.getType((Class) r));
|
||||
} else if (r instanceof Enum) {
|
||||
av.visitEnum(mname, Type.getDescriptor(r.getClass()), ((Enum) r).name());
|
||||
} else if (r instanceof Annotation) {
|
||||
visitAnnotation(av.visitAnnotation(null, Type.getDescriptor(((Annotation) r).annotationType())), (Annotation) r);
|
||||
} else {
|
||||
av.visit(mname, r);
|
||||
}
|
||||
}
|
||||
av.visitEnd();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,24 +60,24 @@ package org.redkale.asm;
|
||||
|
||||
/**
|
||||
* A visitor to visit a Java method. The methods of this class must be called in
|
||||
* the following order: ( <tt>visitParameter</tt> )* [
|
||||
* <tt>visitAnnotationDefault</tt> ] ( <tt>visitAnnotation</tt> |
|
||||
* <tt>visitParameterAnnotation</tt> <tt>visitTypeAnnotation</tt> |
|
||||
* <tt>visitAttribute</tt> )* [ <tt>visitCode</tt> ( <tt>visitFrame</tt> |
|
||||
* <tt>visit<i>X</i>Insn</tt> | <tt>visitLabel</tt> |
|
||||
* <tt>visitInsnAnnotation</tt> | <tt>visitTryCatchBlock</tt> |
|
||||
* <tt>visitTryCatchAnnotation</tt> | <tt>visitLocalVariable</tt> |
|
||||
* <tt>visitLocalVariableAnnotation</tt> | <tt>visitLineNumber</tt> )*
|
||||
* <tt>visitMaxs</tt> ] <tt>visitEnd</tt>. In addition, the
|
||||
* <tt>visit<i>X</i>Insn</tt> and <tt>visitLabel</tt> methods must be called in
|
||||
* the following order: ( <tt>visitParameter</tt> )* [
|
||||
* <tt>visitAnnotationDefault</tt> ] ( <tt>visitAnnotation</tt> |
|
||||
* <tt>visitParameterAnnotation</tt> <tt>visitTypeAnnotation</tt> |
|
||||
* <tt>visitAttribute</tt> )* [ <tt>visitCode</tt> ( <tt>visitFrame</tt> |
|
||||
* <tt>visit<i>X</i>Insn</tt> | <tt>visitLabel</tt> |
|
||||
* <tt>visitInsnAnnotation</tt> | <tt>visitTryCatchBlock</tt> |
|
||||
* <tt>visitTryCatchAnnotation</tt> | <tt>visitLocalVariable</tt> |
|
||||
* <tt>visitLocalVariableAnnotation</tt> | <tt>visitLineNumber</tt> )*
|
||||
* <tt>visitMaxs</tt> ] <tt>visitEnd</tt>. In addition, the
|
||||
* <tt>visit<i>X</i>Insn</tt> and <tt>visitLabel</tt> methods must be called in
|
||||
* the sequential order of the bytecode instructions of the visited code,
|
||||
* <tt>visitInsnAnnotation</tt> must be called <i>after</i> the annotated
|
||||
* instruction, <tt>visitTryCatchBlock</tt> must be called <i>before</i> the
|
||||
* <tt>visitInsnAnnotation</tt> must be called <i>after</i> the annotated
|
||||
* instruction, <tt>visitTryCatchBlock</tt> must be called <i>before</i> the
|
||||
* labels passed as arguments have been visited,
|
||||
* <tt>visitTryCatchBlockAnnotation</tt> must be called <i>after</i> the
|
||||
* <tt>visitTryCatchBlockAnnotation</tt> must be called <i>after</i> the
|
||||
* corresponding try catch block has been visited, and the
|
||||
* <tt>visitLocalVariable</tt>, <tt>visitLocalVariableAnnotation</tt> and
|
||||
* <tt>visitLineNumber</tt> methods must be called <i>after</i> the labels
|
||||
* <tt>visitLocalVariable</tt>, <tt>visitLocalVariableAnnotation</tt> and
|
||||
* <tt>visitLineNumber</tt> methods must be called <i>after</i> the labels
|
||||
* passed as arguments have been visited.
|
||||
*
|
||||
* @author Eric Bruneton
|
||||
@@ -132,8 +132,8 @@ public abstract class MethodVisitor {
|
||||
* @param name
|
||||
* parameter name or null if none is provided.
|
||||
* @param access
|
||||
* the parameter's access flags, only <tt>ACC_FINAL</tt>,
|
||||
* <tt>ACC_SYNTHETIC</tt> or/and <tt>ACC_MANDATED</tt> are
|
||||
* the parameter's access flags, only <tt>ACC_FINAL</tt>,
|
||||
* <tt>ACC_SYNTHETIC</tt> or/and <tt>ACC_MANDATED</tt> are
|
||||
* allowed (see {@link Opcodes}).
|
||||
*/
|
||||
public void visitParameter(String name, int access) {
|
||||
@@ -146,7 +146,7 @@ public abstract class MethodVisitor {
|
||||
* Visits the default value of this annotation interface method.
|
||||
*
|
||||
* @return a visitor to the visit the actual default value of this
|
||||
* annotation interface method, or <tt>null</tt> if this visitor is
|
||||
* annotation interface method, or <tt>null</tt> if this visitor is
|
||||
* not interested in visiting this default value. The 'name'
|
||||
* parameters passed to the methods of this annotation visitor are
|
||||
* ignored. Moreover, exacly one visit method must be called on this
|
||||
@@ -165,8 +165,8 @@ public abstract class MethodVisitor {
|
||||
* @param desc
|
||||
* the class descriptor of the annotation class.
|
||||
* @param visible
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
||||
* this visitor is not interested in visiting this annotation.
|
||||
*/
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
@@ -193,12 +193,12 @@ public abstract class MethodVisitor {
|
||||
* @param typePath
|
||||
* the path to the annotated type argument, wildcard bound, array
|
||||
* element type, or static inner type within 'typeRef'. May be
|
||||
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
|
||||
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
|
||||
* @param desc
|
||||
* the class descriptor of the annotation class.
|
||||
* @param visible
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
||||
* this visitor is not interested in visiting this annotation.
|
||||
*/
|
||||
public AnnotationVisitor visitTypeAnnotation(int typeRef,
|
||||
@@ -217,8 +217,8 @@ public abstract class MethodVisitor {
|
||||
* @param desc
|
||||
* the class descriptor of the annotation class.
|
||||
* @param visible
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
||||
* this visitor is not interested in visiting this annotation.
|
||||
*/
|
||||
public AnnotationVisitor visitParameterAnnotation(int parameter,
|
||||
@@ -444,7 +444,6 @@ public abstract class MethodVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Visits a method instruction. A method instruction is an instruction that
|
||||
* invokes a method.
|
||||
@@ -569,7 +568,7 @@ public abstract class MethodVisitor {
|
||||
* the constant to be loaded on the stack. This parameter must be
|
||||
* a non null {@link Integer}, a {@link Float}, a {@link Long}, a
|
||||
* {@link Double}, a {@link String}, a {@link Type} of OBJECT or
|
||||
* ARRAY sort for <tt>.class</tt> constants, for classes whose
|
||||
* ARRAY sort for <tt>.class</tt> constants, for classes whose
|
||||
* version is 49.0, a {@link Type} of METHOD sort or a
|
||||
* {@link Handle} for MethodType and MethodHandle constants, for
|
||||
* classes whose version is 51.0.
|
||||
@@ -604,8 +603,8 @@ public abstract class MethodVisitor {
|
||||
* @param dflt
|
||||
* beginning of the default handler block.
|
||||
* @param labels
|
||||
* beginnings of the handler blocks. <tt>labels[i]</tt> is the
|
||||
* beginning of the handler block for the <tt>min + i</tt> key.
|
||||
* beginnings of the handler blocks. <tt>labels[i]</tt> is the
|
||||
* beginning of the handler block for the <tt>min + i</tt> key.
|
||||
*/
|
||||
public void visitTableSwitchInsn(int min, int max, Label dflt,
|
||||
Label... labels) {
|
||||
@@ -622,8 +621,8 @@ public abstract class MethodVisitor {
|
||||
* @param keys
|
||||
* the values of the keys.
|
||||
* @param labels
|
||||
* beginnings of the handler blocks. <tt>labels[i]</tt> is the
|
||||
* beginning of the handler block for the <tt>keys[i]</tt> key.
|
||||
* beginnings of the handler blocks. <tt>labels[i]</tt> is the
|
||||
* beginning of the handler block for the <tt>keys[i]</tt> key.
|
||||
*/
|
||||
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
|
||||
if (mv != null) {
|
||||
@@ -668,12 +667,12 @@ public abstract class MethodVisitor {
|
||||
* @param typePath
|
||||
* the path to the annotated type argument, wildcard bound, array
|
||||
* element type, or static inner type within 'typeRef'. May be
|
||||
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
|
||||
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
|
||||
* @param desc
|
||||
* the class descriptor of the annotation class.
|
||||
* @param visible
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
||||
* this visitor is not interested in visiting this annotation.
|
||||
*/
|
||||
public AnnotationVisitor visitInsnAnnotation(int typeRef,
|
||||
@@ -699,7 +698,7 @@ public abstract class MethodVisitor {
|
||||
* beginning of the exception handler's code.
|
||||
* @param type
|
||||
* internal name of the type of exceptions handled by the
|
||||
* handler, or <tt>null</tt> to catch any exceptions (for
|
||||
* handler, or <tt>null</tt> to catch any exceptions (for
|
||||
* "finally" blocks).
|
||||
* @throws IllegalArgumentException
|
||||
* if one of the labels has already been visited by this visitor
|
||||
@@ -725,12 +724,12 @@ public abstract class MethodVisitor {
|
||||
* @param typePath
|
||||
* the path to the annotated type argument, wildcard bound, array
|
||||
* element type, or static inner type within 'typeRef'. May be
|
||||
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
|
||||
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
|
||||
* @param desc
|
||||
* the class descriptor of the annotation class.
|
||||
* @param visible
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
||||
* this visitor is not interested in visiting this annotation.
|
||||
*/
|
||||
public AnnotationVisitor visitTryCatchAnnotation(int typeRef,
|
||||
@@ -750,7 +749,7 @@ public abstract class MethodVisitor {
|
||||
* the type descriptor of this local variable.
|
||||
* @param signature
|
||||
* the type signature of this local variable. May be
|
||||
* <tt>null</tt> if the local variable type does not use generic
|
||||
* <tt>null</tt> if the local variable type does not use generic
|
||||
* types.
|
||||
* @param start
|
||||
* the first instruction corresponding to the scope of this local
|
||||
@@ -782,7 +781,7 @@ public abstract class MethodVisitor {
|
||||
* @param typePath
|
||||
* the path to the annotated type argument, wildcard bound, array
|
||||
* element type, or static inner type within 'typeRef'. May be
|
||||
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
|
||||
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
|
||||
* @param start
|
||||
* the fist instructions corresponding to the continuous ranges
|
||||
* that make the scope of this local variable (inclusive).
|
||||
@@ -796,8 +795,8 @@ public abstract class MethodVisitor {
|
||||
* @param desc
|
||||
* the class descriptor of the annotation class.
|
||||
* @param visible
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
||||
* this visitor is not interested in visiting this annotation.
|
||||
*/
|
||||
public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
|
||||
@@ -819,7 +818,7 @@ public abstract class MethodVisitor {
|
||||
* @param start
|
||||
* the first instruction corresponding to this line number.
|
||||
* @throws IllegalArgumentException
|
||||
* if <tt>start</tt> has not already been visited by this
|
||||
* if <tt>start</tt> has not already been visited by this
|
||||
* visitor (by the {@link #visitLabel visitLabel} method).
|
||||
*/
|
||||
public void visitLineNumber(int line, Label start) {
|
||||
@@ -218,41 +218,41 @@ class MethodWriter extends MethodVisitor {
|
||||
int[] exceptions;
|
||||
|
||||
/**
|
||||
* The annotation default attribute of this method. May be <tt>null</tt>.
|
||||
* The annotation default attribute of this method. May be <tt>null</tt>.
|
||||
*/
|
||||
private ByteVector annd;
|
||||
|
||||
/**
|
||||
* The runtime visible annotations of this method. May be <tt>null</tt>.
|
||||
* The runtime visible annotations of this method. May be <tt>null</tt>.
|
||||
*/
|
||||
private AnnotationWriter anns;
|
||||
|
||||
/**
|
||||
* The runtime invisible annotations of this method. May be <tt>null</tt>.
|
||||
* The runtime invisible annotations of this method. May be <tt>null</tt>.
|
||||
*/
|
||||
private AnnotationWriter ianns;
|
||||
|
||||
/**
|
||||
* The runtime visible type annotations of this method. May be <tt>null</tt>
|
||||
* The runtime visible type annotations of this method. May be <tt>null</tt>
|
||||
* .
|
||||
*/
|
||||
private AnnotationWriter tanns;
|
||||
|
||||
/**
|
||||
* The runtime invisible type annotations of this method. May be
|
||||
* <tt>null</tt>.
|
||||
* <tt>null</tt>.
|
||||
*/
|
||||
private AnnotationWriter itanns;
|
||||
|
||||
/**
|
||||
* The runtime visible parameter annotations of this method. May be
|
||||
* <tt>null</tt>.
|
||||
* <tt>null</tt>.
|
||||
*/
|
||||
private AnnotationWriter[] panns;
|
||||
|
||||
/**
|
||||
* The runtime invisible parameter annotations of this method. May be
|
||||
* <tt>null</tt>.
|
||||
* <tt>null</tt>.
|
||||
*/
|
||||
private AnnotationWriter[] ipanns;
|
||||
|
||||
@@ -381,12 +381,12 @@ class MethodWriter extends MethodVisitor {
|
||||
private int lastCodeOffset;
|
||||
|
||||
/**
|
||||
* The runtime visible type annotations of the code. May be <tt>null</tt>.
|
||||
* The runtime visible type annotations of the code. May be <tt>null</tt>.
|
||||
*/
|
||||
private AnnotationWriter ctanns;
|
||||
|
||||
/**
|
||||
* The runtime invisible type annotations of the code. May be <tt>null</tt>.
|
||||
* The runtime invisible type annotations of the code. May be <tt>null</tt>.
|
||||
*/
|
||||
private AnnotationWriter ictanns;
|
||||
|
||||
@@ -446,7 +446,7 @@ class MethodWriter extends MethodVisitor {
|
||||
* is relative to the beginning of the current basic block, i.e., the true
|
||||
* stack size after the last visited instruction is equal to the
|
||||
* {@link Label#inputStackTop beginStackSize} of the current basic block
|
||||
* plus <tt>stackSize</tt>.
|
||||
* plus <tt>stackSize</tt>.
|
||||
*/
|
||||
private int stackSize;
|
||||
|
||||
@@ -455,7 +455,7 @@ class MethodWriter extends MethodVisitor {
|
||||
* This size is relative to the beginning of the current basic block, i.e.,
|
||||
* the true maximum stack size after the last visited instruction is equal
|
||||
* to the {@link Label#inputStackTop beginStackSize} of the current basic
|
||||
* block plus <tt>stackSize</tt>.
|
||||
* block plus <tt>stackSize</tt>.
|
||||
*/
|
||||
private int maxStackSize;
|
||||
|
||||
@@ -475,10 +475,10 @@ class MethodWriter extends MethodVisitor {
|
||||
* @param desc
|
||||
* the method's descriptor (see {@link Type}).
|
||||
* @param signature
|
||||
* the method's signature. May be <tt>null</tt>.
|
||||
* the method's signature. May be <tt>null</tt>.
|
||||
* @param exceptions
|
||||
* the internal names of the method's exceptions. May be
|
||||
* <tt>null</tt>.
|
||||
* <tt>null</tt>.
|
||||
* @param compute
|
||||
* Indicates what must be automatically computed (see #compute).
|
||||
*/
|
||||
@@ -60,9 +60,9 @@ package org.redkale.asm;
|
||||
|
||||
/**
|
||||
* A visitor to visit a Java module. The methods of this class must be called in
|
||||
* the following order: <tt>visitMainClass</tt> | ( <tt>visitPackage</tt> |
|
||||
* <tt>visitRequire</tt> | <tt>visitExport</tt> | <tt>visitOpen</tt> |
|
||||
* <tt>visitUse</tt> | <tt>visitProvide</tt> )* <tt>visitEnd</tt>.
|
||||
* the following order: <tt>visitMainClass</tt> | ( <tt>visitPackage</tt> |
|
||||
* <tt>visitRequire</tt> | <tt>visitExport</tt> | <tt>visitOpen</tt> |
|
||||
* <tt>visitUse</tt> | <tt>visitProvide</tt> )* <tt>visitEnd</tt>.
|
||||
*
|
||||
* The methods {@link #visitRequire(String, int, String)}, {@link #visitExport(String, int, String...)},
|
||||
* {@link #visitOpen(String, int, String...)} and {@link #visitPackage(String)}
|
||||
@@ -157,7 +157,7 @@ public abstract class ModuleVisitor {
|
||||
* {@code ACC_MANDATED}.
|
||||
* @param modules the qualified names of the modules that can access to
|
||||
* the public classes of the exported package or
|
||||
* <tt>null</tt>.
|
||||
* <tt>null</tt>.
|
||||
*/
|
||||
public void visitExport(String packaze, int access, String... modules) {
|
||||
if (mv != null) {
|
||||
@@ -174,7 +174,7 @@ public abstract class ModuleVisitor {
|
||||
* {@code ACC_MANDATED}.
|
||||
* @param modules the qualified names of the modules that can use deep
|
||||
* reflection to the classes of the open package or
|
||||
* <tt>null</tt>.
|
||||
* <tt>null</tt>.
|
||||
*/
|
||||
public void visitOpen(String packaze, int access, String... modules) {
|
||||
if (mv != null) {
|
||||
@@ -73,19 +73,23 @@ package org.redkale.asm;
|
||||
public interface Opcodes {
|
||||
|
||||
// ASM API versions
|
||||
|
||||
int ASM4 = 4 << 16 | 0 << 8 | 0;
|
||||
int ASM5 = 5 << 16 | 0 << 8 | 0;
|
||||
int ASM6 = 6 << 16 | 0 << 8 | 0;
|
||||
int ASM4 = 4 << 16 | 0 << 8;
|
||||
int ASM5 = 5 << 16 | 0 << 8;
|
||||
int ASM6 = 6 << 16 | 0 << 8;
|
||||
|
||||
// versions
|
||||
|
||||
int V1_1 = 3 << 16 | 45;
|
||||
int V1_2 = 0 << 16 | 46;
|
||||
int V1_3 = 0 << 16 | 47;
|
||||
int V1_4 = 0 << 16 | 48;
|
||||
int V1_5 = 0 << 16 | 49;
|
||||
int V1_6 = 0 << 16 | 50;
|
||||
int V1_7 = 0 << 16 | 51;
|
||||
int V1_8 = 0 << 16 | 52;
|
||||
int V9 = 0 << 16 | 53;
|
||||
int V10 = 0 << 16 | 54;
|
||||
int V11 = 0 << 16 | 55;
|
||||
|
||||
// access flags
|
||||
|
||||
@@ -71,47 +71,47 @@ import java.lang.reflect.Method;
|
||||
public class Type {
|
||||
|
||||
/**
|
||||
* The sort of the <tt>void</tt> type. See {@link #getSort getSort}.
|
||||
* The sort of the <tt>void</tt> type. See {@link #getSort getSort}.
|
||||
*/
|
||||
public static final int VOID = 0;
|
||||
|
||||
/**
|
||||
* The sort of the <tt>boolean</tt> type. See {@link #getSort getSort}.
|
||||
* The sort of the <tt>boolean</tt> type. See {@link #getSort getSort}.
|
||||
*/
|
||||
public static final int BOOLEAN = 1;
|
||||
|
||||
/**
|
||||
* The sort of the <tt>char</tt> type. See {@link #getSort getSort}.
|
||||
* The sort of the <tt>char</tt> type. See {@link #getSort getSort}.
|
||||
*/
|
||||
public static final int CHAR = 2;
|
||||
|
||||
/**
|
||||
* The sort of the <tt>byte</tt> type. See {@link #getSort getSort}.
|
||||
* The sort of the <tt>byte</tt> type. See {@link #getSort getSort}.
|
||||
*/
|
||||
public static final int BYTE = 3;
|
||||
|
||||
/**
|
||||
* The sort of the <tt>short</tt> type. See {@link #getSort getSort}.
|
||||
* The sort of the <tt>short</tt> type. See {@link #getSort getSort}.
|
||||
*/
|
||||
public static final int SHORT = 4;
|
||||
|
||||
/**
|
||||
* The sort of the <tt>int</tt> type. See {@link #getSort getSort}.
|
||||
* The sort of the <tt>int</tt> type. See {@link #getSort getSort}.
|
||||
*/
|
||||
public static final int INT = 5;
|
||||
|
||||
/**
|
||||
* The sort of the <tt>float</tt> type. See {@link #getSort getSort}.
|
||||
* The sort of the <tt>float</tt> type. See {@link #getSort getSort}.
|
||||
*/
|
||||
public static final int FLOAT = 6;
|
||||
|
||||
/**
|
||||
* The sort of the <tt>long</tt> type. See {@link #getSort getSort}.
|
||||
* The sort of the <tt>long</tt> type. See {@link #getSort getSort}.
|
||||
*/
|
||||
public static final int LONG = 7;
|
||||
|
||||
/**
|
||||
* The sort of the <tt>double</tt> type. See {@link #getSort getSort}.
|
||||
* The sort of the <tt>double</tt> type. See {@link #getSort getSort}.
|
||||
*/
|
||||
public static final int DOUBLE = 8;
|
||||
|
||||
@@ -131,55 +131,55 @@ public class Type {
|
||||
public static final int METHOD = 11;
|
||||
|
||||
/**
|
||||
* The <tt>void</tt> type.
|
||||
* The <tt>void</tt> type.
|
||||
*/
|
||||
public static final Type VOID_TYPE = new Type(VOID, null, ('V' << 24)
|
||||
| (5 << 16) | (0 << 8) | 0, 1);
|
||||
|
||||
/**
|
||||
* The <tt>boolean</tt> type.
|
||||
* The <tt>boolean</tt> type.
|
||||
*/
|
||||
public static final Type BOOLEAN_TYPE = new Type(BOOLEAN, null, ('Z' << 24)
|
||||
| (0 << 16) | (5 << 8) | 1, 1);
|
||||
|
||||
/**
|
||||
* The <tt>char</tt> type.
|
||||
* The <tt>char</tt> type.
|
||||
*/
|
||||
public static final Type CHAR_TYPE = new Type(CHAR, null, ('C' << 24)
|
||||
| (0 << 16) | (6 << 8) | 1, 1);
|
||||
|
||||
/**
|
||||
* The <tt>byte</tt> type.
|
||||
* The <tt>byte</tt> type.
|
||||
*/
|
||||
public static final Type BYTE_TYPE = new Type(BYTE, null, ('B' << 24)
|
||||
| (0 << 16) | (5 << 8) | 1, 1);
|
||||
|
||||
/**
|
||||
* The <tt>short</tt> type.
|
||||
* The <tt>short</tt> type.
|
||||
*/
|
||||
public static final Type SHORT_TYPE = new Type(SHORT, null, ('S' << 24)
|
||||
| (0 << 16) | (7 << 8) | 1, 1);
|
||||
|
||||
/**
|
||||
* The <tt>int</tt> type.
|
||||
* The <tt>int</tt> type.
|
||||
*/
|
||||
public static final Type INT_TYPE = new Type(INT, null, ('I' << 24)
|
||||
| (0 << 16) | (0 << 8) | 1, 1);
|
||||
|
||||
/**
|
||||
* The <tt>float</tt> type.
|
||||
* The <tt>float</tt> type.
|
||||
*/
|
||||
public static final Type FLOAT_TYPE = new Type(FLOAT, null, ('F' << 24)
|
||||
| (2 << 16) | (2 << 8) | 1, 1);
|
||||
|
||||
/**
|
||||
* The <tt>long</tt> type.
|
||||
* The <tt>long</tt> type.
|
||||
*/
|
||||
public static final Type LONG_TYPE = new Type(LONG, null, ('J' << 24)
|
||||
| (1 << 16) | (1 << 8) | 2, 1);
|
||||
|
||||
/**
|
||||
* The <tt>double</tt> type.
|
||||
* The <tt>double</tt> type.
|
||||
*/
|
||||
public static final Type DOUBLE_TYPE = new Type(DOUBLE, null, ('D' << 24)
|
||||
| (3 << 16) | (3 << 8) | 2, 1);
|
||||
@@ -439,8 +439,8 @@ public class Type {
|
||||
* @return the size of the arguments of the method (plus one for the
|
||||
* implicit this argument), argSize, and the size of its return
|
||||
* value, retSize, packed into a single int i =
|
||||
* <tt>(argSize << 2) | retSize</tt> (argSize is therefore equal to
|
||||
* <tt>i >> 2</tt>, and retSize to <tt>i & 0x03</tt>).
|
||||
* <tt>(argSize << 2) | retSize</tt> (argSize is therefore equal to
|
||||
* <tt>i >> 2</tt>, and retSize to <tt>i & 0x03</tt>).
|
||||
*/
|
||||
public static int getArgumentsAndReturnSizes(final String desc) {
|
||||
int n = 1;
|
||||
@@ -645,9 +645,9 @@ public class Type {
|
||||
* @return the size of the arguments (plus one for the implicit this
|
||||
* argument), argSize, and the size of the return value, retSize,
|
||||
* packed into a single
|
||||
* int i = <tt>(argSize << 2) | retSize</tt>
|
||||
* (argSize is therefore equal to <tt>i >> 2</tt>,
|
||||
* and retSize to <tt>i & 0x03</tt>).
|
||||
* int i = <tt>(argSize << 2) | retSize</tt>
|
||||
* (argSize is therefore equal to <tt>i >> 2</tt>,
|
||||
* and retSize to <tt>i & 0x03</tt>).
|
||||
*/
|
||||
public int getArgumentsAndReturnSizes() {
|
||||
return getArgumentsAndReturnSizes(getDescriptor());
|
||||
@@ -838,8 +838,8 @@ public class Type {
|
||||
* Returns the size of values of this type. This method must not be used for
|
||||
* method types.
|
||||
*
|
||||
* @return the size of values of this type, i.e., 2 for <tt>long</tt> and
|
||||
* <tt>double</tt>, 0 for <tt>void</tt> and 1 otherwise.
|
||||
* @return the size of values of this type, i.e., 2 for <tt>long</tt> and
|
||||
* <tt>double</tt>, 0 for <tt>void</tt> and 1 otherwise.
|
||||
*/
|
||||
public int getSize() {
|
||||
// the size is in byte 0 of 'off' for primitive types (buf == null)
|
||||
@@ -855,8 +855,8 @@ public class Type {
|
||||
* ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG,
|
||||
* ISHL, ISHR, IUSHR, IAND, IOR, IXOR and IRETURN.
|
||||
* @return an opcode that is similar to the given opcode, but adapted to
|
||||
* this Java type. For example, if this type is <tt>float</tt> and
|
||||
* <tt>opcode</tt> is IRETURN, this method returns FRETURN.
|
||||
* this Java type. For example, if this type is <tt>float</tt> and
|
||||
* <tt>opcode</tt> is IRETURN, this method returns FRETURN.
|
||||
*/
|
||||
public int getOpcode(final int opcode) {
|
||||
if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) {
|
||||
@@ -879,7 +879,7 @@ public class Type {
|
||||
*
|
||||
* @param o
|
||||
* the object to be compared to this type.
|
||||
* @return <tt>true</tt> if the given object is equal to this type.
|
||||
* @return <tt>true</tt> if the given object is equal to this type.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
@@ -161,7 +161,7 @@ public class TypePath {
|
||||
* @return the corresponding TypePath object, or null if the path is empty.
|
||||
*/
|
||||
public static TypePath fromString(final String typePath) {
|
||||
if (typePath == null || typePath.length() == 0) {
|
||||
if (typePath == null || typePath.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
int n = typePath.length();
|
||||
656
src/main/java/org/redkale/boot/ApiDocCommand.java
Normal file
656
src/main/java/org/redkale/boot/ApiDocCommand.java
Normal file
@@ -0,0 +1,656 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.boot;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.math.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.*;
|
||||
import java.util.logging.*;
|
||||
import javax.persistence.*;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.convert.json.*;
|
||||
import org.redkale.mq.MessageMultiConsumer;
|
||||
import org.redkale.net.http.*;
|
||||
import org.redkale.service.RetResult;
|
||||
import org.redkale.source.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* API接口文档生成类,作用:生成Application实例中所有HttpServer的可用HttpServlet的API接口方法 <br>
|
||||
* 继承 HttpBaseServlet 是为了获取 HttpMapping 信息 <br>
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public final class ApiDocCommand {
|
||||
|
||||
private static final java.lang.reflect.Type TYPE_RETRESULT_OBJECT = new TypeToken<RetResult<Object>>() {
|
||||
}.getType();
|
||||
|
||||
private static final java.lang.reflect.Type TYPE_RETRESULT_STRING = new TypeToken<RetResult<String>>() {
|
||||
}.getType();
|
||||
|
||||
private static final java.lang.reflect.Type TYPE_RETRESULT_INTEGER = new TypeToken<RetResult<Integer>>() {
|
||||
}.getType();
|
||||
|
||||
private static final java.lang.reflect.Type TYPE_RETRESULT_LONG = new TypeToken<RetResult<Long>>() {
|
||||
}.getType();
|
||||
|
||||
private final Application app; //Application全局对象
|
||||
|
||||
public ApiDocCommand(Application app) {
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
public String command(String cmd, String[] params) throws Exception {
|
||||
//是否跳过RPC接口
|
||||
boolean skipRPC = true;
|
||||
String apiHost = "http://localhost";
|
||||
|
||||
if (params != null && params.length > 0) {
|
||||
for (String param : params) {
|
||||
if (param == null) continue;
|
||||
param = param.toLowerCase();
|
||||
if (param.startsWith("--api-skiprpc=")) {
|
||||
skipRPC = "true".equalsIgnoreCase(param.substring("--api-skiprpc=".length()));
|
||||
} else if (param.startsWith("--api-host=")) {
|
||||
apiHost = param.substring("--api-host=".length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<Map> serverList = new ArrayList<>();
|
||||
Field __prefix = HttpServlet.class.getDeclaredField("_prefix");
|
||||
__prefix.setAccessible(true);
|
||||
Map<String, Map<String, Map<String, Object>>> typesMap = new LinkedHashMap<>();
|
||||
//https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md
|
||||
Map<String, Object> swaggerPathsMap = new LinkedHashMap<>();
|
||||
List<Map> swaggerServers = new ArrayList<>();
|
||||
List<Map> swaggerTags = new ArrayList<>();
|
||||
Map<String, Map<String, Object>> swaggerComponentsMap = new LinkedHashMap<>();
|
||||
for (NodeServer node : app.servers) {
|
||||
if (!(node instanceof NodeHttpServer)) continue;
|
||||
final Map<String, Object> map = new LinkedHashMap<>();
|
||||
serverList.add(map);
|
||||
HttpServer server = node.getServer();
|
||||
map.put("address", server.getSocketAddress());
|
||||
swaggerServers.add(Utility.ofMap("url", apiHost + ":" + server.getSocketAddress().getPort()));
|
||||
List<Map<String, Object>> servletsList = new ArrayList<>();
|
||||
map.put("servlets", servletsList);
|
||||
String plainContentType = server.getResponseConfig() == null ? "application/json" : server.getResponseConfig().plainContentType;
|
||||
if (plainContentType == null || plainContentType.isEmpty()) plainContentType = "application/json";
|
||||
if (plainContentType.indexOf(';') > 0) plainContentType = plainContentType.substring(0, plainContentType.indexOf(';'));
|
||||
|
||||
for (HttpServlet servlet : server.getDispatcherServlet().getServlets()) {
|
||||
if (!(servlet instanceof HttpServlet)) continue;
|
||||
if (servlet instanceof WebSocketServlet) continue;
|
||||
if (servlet.getClass().getAnnotation(MessageMultiConsumer.class) != null) {
|
||||
node.logger.log(Level.INFO, servlet + " be skipped because has @MessageMultiConsumer");
|
||||
continue;
|
||||
}
|
||||
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
|
||||
if (ws == null) {
|
||||
node.logger.log(Level.WARNING, servlet + " not found @WebServlet");
|
||||
continue;
|
||||
}
|
||||
if (ws.name().isEmpty()) {
|
||||
node.logger.log(Level.INFO, servlet + " be skipped because @WebServlet.name is empty");
|
||||
continue;
|
||||
}
|
||||
final String tag = ws.name().isEmpty() ? servlet.getClass().getSimpleName().replace("Servlet", "").toLowerCase() : ws.name();
|
||||
final Map<String, Object> servletMap = new LinkedHashMap<>();
|
||||
String prefix = (String) __prefix.get(servlet);
|
||||
String[] urlregs = ws.value();
|
||||
if (prefix != null && !prefix.isEmpty()) {
|
||||
for (int i = 0; i < urlregs.length; i++) {
|
||||
urlregs[i] = prefix + urlregs[i];
|
||||
}
|
||||
}
|
||||
servletMap.put("urlregs", urlregs);
|
||||
servletMap.put("moduleid", ws.moduleid());
|
||||
servletMap.put("name", ws.name());
|
||||
servletMap.put("comment", ws.comment());
|
||||
|
||||
List<Map> mappingsList = new ArrayList<>();
|
||||
servletMap.put("mappings", mappingsList);
|
||||
final Class selfClz = servlet.getClass();
|
||||
Class clz = servlet.getClass();
|
||||
HashSet<String> actionUrls = new HashSet<>();
|
||||
do {
|
||||
if (Modifier.isAbstract(clz.getModifiers())) break;
|
||||
for (Method method : clz.getMethods()) {
|
||||
if (method.getParameterCount() != 2) continue;
|
||||
HttpMapping action = method.getAnnotation(HttpMapping.class);
|
||||
if (action == null) continue;
|
||||
if (!action.inherited() && selfClz != clz) continue; //忽略不被继承的方法
|
||||
if (actionUrls.contains(action.url())) continue;
|
||||
if (HttpScope.class.isAssignableFrom(action.result())) continue; //忽略模板引擎的方法
|
||||
if (action.rpconly() && skipRPC) continue; //不生成RPC接口
|
||||
|
||||
final List<Map<String, Object>> swaggerParamsList = new ArrayList<>();
|
||||
|
||||
final Map<String, Object> mappingMap = new LinkedHashMap<>();
|
||||
mappingMap.put("url", prefix + action.url());
|
||||
actionUrls.add(action.url());
|
||||
mappingMap.put("auth", action.auth());
|
||||
mappingMap.put("actionid", action.actionid());
|
||||
mappingMap.put("comment", action.comment());
|
||||
List<Map> paramsList = new ArrayList<>();
|
||||
mappingMap.put("params", paramsList);
|
||||
List<String> results = new ArrayList<>();
|
||||
Type resultType = action.result();
|
||||
if (!action.resultref().isEmpty()) {
|
||||
Field f = servlet.getClass().getDeclaredField(action.resultref());
|
||||
f.setAccessible(true);
|
||||
resultType = (Type) f.get(servlet);
|
||||
}
|
||||
// for (final Class rtype : action.results()) {
|
||||
// results.add(rtype.getName());
|
||||
// if (typesMap.containsKey(rtype.getName())) continue;
|
||||
// if (rtype.getName().startsWith("java.")) continue;
|
||||
// if (rtype.getName().startsWith("javax.")) continue;
|
||||
// final boolean filter = FilterBean.class.isAssignableFrom(rtype);
|
||||
// final Map<String, Map<String, Object>> typeMap = new LinkedHashMap<>();
|
||||
// Class loop = rtype;
|
||||
// do {
|
||||
// if (loop == null || loop.isInterface()) break;
|
||||
// for (Field field : loop.getDeclaredFields()) {
|
||||
// if (Modifier.isFinal(field.getModifiers())) continue;
|
||||
// if (Modifier.isStatic(field.getModifiers())) continue;
|
||||
//
|
||||
// Map<String, Object> fieldmap = new LinkedHashMap<>();
|
||||
// fieldmap.put("type", field.getType().isArray() ? (field.getType().getComponentType().getName() + "[]") : field.getGenericType().getTypeName());
|
||||
//
|
||||
// Comment comment = field.getAnnotation(Comment.class);
|
||||
// Column col = field.getAnnotation(Column.class);
|
||||
// FilterColumn fc = field.getAnnotation(FilterColumn.class);
|
||||
// if (comment != null) {
|
||||
// fieldmap.put("comment", comment.value());
|
||||
// } else if (col != null) {
|
||||
// fieldmap.put("comment", col.comment());
|
||||
// } else if (fc != null) {
|
||||
// fieldmap.put("comment", fc.comment());
|
||||
// }
|
||||
// fieldmap.put("primary", !filter && (field.getAnnotation(Id.class) != null));
|
||||
// fieldmap.put("updatable", (filter || col == null || col.updatable()));
|
||||
// if (servlet.getClass().getAnnotation(Rest.RestDyn.class) != null) {
|
||||
// if (field.getAnnotation(RestAddress.class) != null) continue;
|
||||
// }
|
||||
//
|
||||
// typeMap.put(field.getName(), fieldmap);
|
||||
// }
|
||||
// } while ((loop = loop.getSuperclass()) != Object.class);
|
||||
// typesMap.put(rtype.getName(), typeMap);
|
||||
// }
|
||||
mappingMap.put("results", results);
|
||||
boolean hasbodyparam = false;
|
||||
Map<String, Object> swaggerRequestBody = new LinkedHashMap<>();
|
||||
for (HttpParam param : method.getAnnotationsByType(HttpParam.class)) {
|
||||
final Map<String, Object> oldapisParamMap = new LinkedHashMap<>();
|
||||
final boolean isarray = param.type().isArray();
|
||||
final Class ptype = isarray ? param.type().getComponentType() : param.type();
|
||||
oldapisParamMap.put("name", param.name());
|
||||
oldapisParamMap.put("radix", param.radix());
|
||||
oldapisParamMap.put("type", ptype.getName() + (isarray ? "[]" : ""));
|
||||
oldapisParamMap.put("style", param.style());
|
||||
oldapisParamMap.put("comment", param.comment());
|
||||
oldapisParamMap.put("required", param.required());
|
||||
paramsList.add(oldapisParamMap);
|
||||
{
|
||||
final Map<String, Object> paramSchemaMap = new LinkedHashMap<>();
|
||||
Type paramGenericType = param.type();
|
||||
if (!param.typeref().isEmpty()) {
|
||||
Field f = servlet.getClass().getDeclaredField(param.typeref());
|
||||
f.setAccessible(true);
|
||||
paramGenericType = (Type) f.get(servlet);
|
||||
}
|
||||
simpleSchemaType(null, node.getLogger(), swaggerComponentsMap, param.type(), paramGenericType, paramSchemaMap, true);
|
||||
if (param.style() == HttpParam.HttpParameterStyle.BODY) {
|
||||
swaggerRequestBody.put("description", param.comment());
|
||||
swaggerRequestBody.put("content", Utility.ofMap(plainContentType, Utility.ofMap("schema", paramSchemaMap)));
|
||||
} else {
|
||||
final Map<String, Object> swaggerParamMap = new LinkedHashMap<>();
|
||||
swaggerParamMap.put("name", param.name());
|
||||
swaggerParamMap.put("in", param.style().name().toLowerCase());
|
||||
swaggerParamMap.put("description", param.comment());
|
||||
swaggerParamMap.put("required", param.required());
|
||||
if (param.deprecated()) {
|
||||
swaggerParamMap.put("deprecated", param.deprecated());
|
||||
}
|
||||
//https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameterStyle
|
||||
swaggerParamMap.put("style", param.style() == HttpParam.HttpParameterStyle.HEADER || param.name().indexOf('#') == 0 ? "simple" : "form");
|
||||
swaggerParamMap.put("explode", true);
|
||||
swaggerParamMap.put("schema", paramSchemaMap);
|
||||
Object example = formatExample(null, param.example(), param.type(), paramGenericType);
|
||||
if (example != null) {
|
||||
swaggerParamMap.put("example", example);
|
||||
} else if (!param.example().isEmpty()) {
|
||||
swaggerParamMap.put("example", param.example());
|
||||
}
|
||||
swaggerParamsList.add(swaggerParamMap);
|
||||
}
|
||||
}
|
||||
if (param.style() == HttpParam.HttpParameterStyle.BODY) hasbodyparam = true;
|
||||
if (ptype.isPrimitive() || ptype == String.class) continue;
|
||||
if (typesMap.containsKey(ptype.getName())) continue;
|
||||
if (ptype.getName().startsWith("java.")) continue;
|
||||
if (ptype.getName().startsWith("javax.")) continue;
|
||||
|
||||
final Map<String, Map<String, Object>> typeMap = new LinkedHashMap<>();
|
||||
Class loop = ptype;
|
||||
final boolean filter = FilterBean.class.isAssignableFrom(loop);
|
||||
do {
|
||||
if (loop == null || loop.isInterface()) break;
|
||||
for (Field field : loop.getDeclaredFields()) {
|
||||
if (Modifier.isFinal(field.getModifiers())) continue;
|
||||
if (Modifier.isStatic(field.getModifiers())) continue;
|
||||
|
||||
Map<String, Object> fieldmap = new LinkedHashMap<>();
|
||||
fieldmap.put("type", field.getType().isArray() ? (field.getType().getComponentType().getName() + "[]") : field.getGenericType().getTypeName());
|
||||
|
||||
Column col = field.getAnnotation(Column.class);
|
||||
FilterColumn fc = field.getAnnotation(FilterColumn.class);
|
||||
Comment comment = field.getAnnotation(Comment.class);
|
||||
if (comment != null) {
|
||||
fieldmap.put("comment", comment.value());
|
||||
} else if (col != null) {
|
||||
fieldmap.put("comment", col.comment());
|
||||
} else if (fc != null) {
|
||||
fieldmap.put("comment", fc.comment());
|
||||
}
|
||||
fieldmap.put("primary", !filter && (field.getAnnotation(Id.class) != null));
|
||||
fieldmap.put("updatable", (filter || col == null || col.updatable()));
|
||||
|
||||
if (servlet.getClass().getAnnotation(Rest.RestDyn.class) != null) {
|
||||
if (field.getAnnotation(RestAddress.class) != null) continue;
|
||||
}
|
||||
|
||||
typeMap.put(field.getName(), fieldmap);
|
||||
}
|
||||
} while ((loop = loop.getSuperclass()) != Object.class);
|
||||
|
||||
typesMap.put(ptype.getName(), typeMap);
|
||||
}
|
||||
mappingMap.put("result", action.result().getSimpleName().replace("void", "Object"));
|
||||
mappingsList.add(mappingMap);
|
||||
|
||||
final Map<String, Object> swaggerOperatMap = new LinkedHashMap<>();
|
||||
swaggerOperatMap.put("tags", new String[]{tag});
|
||||
swaggerOperatMap.put("operationId", action.name());
|
||||
if (method.getAnnotation(Deprecated.class) != null) {
|
||||
swaggerOperatMap.put("deprecated", true);
|
||||
}
|
||||
Map<String, Object> respSchemaMap = new LinkedHashMap<>();
|
||||
JsonFactory returnFactory = Rest.createJsonFactory(false, method.getAnnotationsByType(RestConvert.class), method.getAnnotationsByType(RestConvertCoder.class));
|
||||
simpleSchemaType(returnFactory, node.getLogger(), swaggerComponentsMap, action.result(), resultType, respSchemaMap, true);
|
||||
|
||||
Map<String, Object> respMap = new LinkedHashMap<>();
|
||||
respMap.put("schema", respSchemaMap);
|
||||
Object example = formatExample(returnFactory, action.example(), action.result(), resultType);
|
||||
if (example != null) respSchemaMap.put("example", example);
|
||||
if (!swaggerRequestBody.isEmpty()) swaggerOperatMap.put("requestBody", swaggerRequestBody);
|
||||
swaggerOperatMap.put("parameters", swaggerParamsList);
|
||||
String actiondesc = action.comment();
|
||||
if (action.rpconly()) actiondesc = "[Only for RPC API] " + actiondesc;
|
||||
swaggerOperatMap.put("responses", Utility.ofMap("200", Utility.ofMap("description", actiondesc, "content", Utility.ofMap("application/json", respMap))));
|
||||
|
||||
String m = action.methods() == null || action.methods().length == 0 ? null : action.methods()[0].toLowerCase();
|
||||
if (m == null) {
|
||||
m = hasbodyparam || TYPE_RETRESULT_STRING.equals(resultType) || TYPE_RETRESULT_INTEGER.equals(resultType)
|
||||
|| TYPE_RETRESULT_LONG.equals(resultType) || action.name().contains("create") || action.name().contains("insert")
|
||||
|| action.name().contains("update") || action.name().contains("delete") || action.name().contains("send") ? "post" : "get";
|
||||
}
|
||||
swaggerPathsMap.put(prefix + action.url(), Utility.ofMap("description", action.comment(), m, swaggerOperatMap));
|
||||
}
|
||||
} while ((clz = clz.getSuperclass()) != HttpServlet.class);
|
||||
mappingsList.sort((o1, o2) -> ((String) o1.get("url")).compareTo((String) o2.get("url")));
|
||||
servletsList.add(servletMap);
|
||||
if (!actionUrls.isEmpty()) swaggerTags.add(Utility.ofMap("name", tag, "description", ws.comment()));
|
||||
}
|
||||
servletsList.sort((o1, o2) -> {
|
||||
String[] urlregs1 = (String[]) o1.get("urlregs");
|
||||
String[] urlregs2 = (String[]) o2.get("urlregs");
|
||||
return urlregs1.length > 0 ? (urlregs2.length > 0 ? urlregs1[0].compareTo(urlregs2[0]) : 1) : -1;
|
||||
});
|
||||
}
|
||||
{ // https://github.com/OAI/OpenAPI-Specification
|
||||
Map<String, Object> swaggerResultMap = new LinkedHashMap<>();
|
||||
swaggerResultMap.put("openapi", "3.0.0");
|
||||
Map<String, Object> infomap = new LinkedHashMap<>();
|
||||
infomap.put("title", "Redkale generate apidoc");
|
||||
infomap.put("version", "1.0.0");
|
||||
swaggerResultMap.put("info", infomap);
|
||||
swaggerResultMap.put("servers", swaggerServers);
|
||||
swaggerResultMap.put("paths", swaggerPathsMap);
|
||||
swaggerResultMap.put("tags", swaggerTags);
|
||||
if (!swaggerComponentsMap.isEmpty()) swaggerResultMap.put("components", Utility.ofMap("schemas", swaggerComponentsMap));
|
||||
final FileOutputStream out = new FileOutputStream(new File(app.getHome(), "openapi-doc.json"));
|
||||
out.write(JsonConvert.root().convertTo(swaggerResultMap).getBytes(StandardCharsets.UTF_8));
|
||||
out.close();
|
||||
}
|
||||
{
|
||||
Map<String, Object> oldapisResultMap = new LinkedHashMap<>();
|
||||
oldapisResultMap.put("servers", serverList);
|
||||
oldapisResultMap.put("types", typesMap);
|
||||
final String json = JsonConvert.root().convertTo(oldapisResultMap);
|
||||
final FileOutputStream out = new FileOutputStream(new File(app.getHome(), "apidoc.json"));
|
||||
out.write(json.getBytes(StandardCharsets.UTF_8));
|
||||
out.close();
|
||||
File doctemplate = new File(app.getConfPath().toString(), "apidoc-template.html");
|
||||
InputStream in = null;
|
||||
if (doctemplate.isFile() && doctemplate.canRead()) {
|
||||
in = new FileInputStream(doctemplate);
|
||||
}
|
||||
if (in != null) {
|
||||
String content = Utility.read(in).replace("'${content}'", json);
|
||||
in.close();
|
||||
FileOutputStream outhtml = new FileOutputStream(new File(app.getHome(), "apidoc.html"));
|
||||
outhtml.write(content.getBytes(StandardCharsets.UTF_8));
|
||||
outhtml.close();
|
||||
}
|
||||
}
|
||||
return "apidoc success";
|
||||
}
|
||||
|
||||
private static void simpleSchemaType(JsonFactory factory, Logger logger, Map<String, Map<String, Object>> componentsMap, Class type, Type genericType, Map<String, Object> schemaMap, boolean recursive) {
|
||||
if (type == int.class || type == Integer.class || type == AtomicInteger.class) {
|
||||
schemaMap.put("type", "integer");
|
||||
schemaMap.put("format", "int32");
|
||||
} else if (type == long.class || type == Long.class
|
||||
|| type == AtomicLong.class || type == LongAdder.class || type == BigInteger.class) {
|
||||
schemaMap.put("type", "integer");
|
||||
schemaMap.put("format", "int64");
|
||||
} else if (type == float.class || type == Float.class) {
|
||||
schemaMap.put("type", "number");
|
||||
schemaMap.put("format", "float");
|
||||
} else if (type == double.class || type == Double.class || type == BigDecimal.class) {
|
||||
schemaMap.put("type", "number");
|
||||
schemaMap.put("format", "double");
|
||||
} else if (type == boolean.class || type == Boolean.class || type == AtomicBoolean.class) {
|
||||
schemaMap.put("type", "boolean");
|
||||
} else if (type.isPrimitive() || Number.class.isAssignableFrom(type)) {
|
||||
schemaMap.put("type", "number");
|
||||
} else if (type == String.class || CharSequence.class.isAssignableFrom(type)) {
|
||||
schemaMap.put("type", "string");
|
||||
} else if (recursive && (type.isArray() || Collection.class.isAssignableFrom(type))) {
|
||||
schemaMap.put("type", "array");
|
||||
Map<String, Object> sbumap = new LinkedHashMap<>();
|
||||
if (type.isArray()) {
|
||||
simpleSchemaType(factory, logger, componentsMap, type.getComponentType(), type.getComponentType(), sbumap, false);
|
||||
} else if (genericType instanceof ParameterizedType) {
|
||||
Type subpt = ((ParameterizedType) genericType).getActualTypeArguments()[0];
|
||||
if (subpt instanceof Class) {
|
||||
simpleSchemaType(factory, logger, componentsMap, (Class) subpt, subpt, sbumap, false);
|
||||
} else if (subpt instanceof ParameterizedType && ((ParameterizedType) subpt).getOwnerType() instanceof Class) {
|
||||
simpleSchemaType(factory, logger, componentsMap, (Class) ((ParameterizedType) subpt).getOwnerType(), subpt, sbumap, false);
|
||||
} else {
|
||||
sbumap.put("type", "object");
|
||||
}
|
||||
} else {
|
||||
sbumap.put("type", "object");
|
||||
}
|
||||
schemaMap.put("items", sbumap);
|
||||
} else if (!type.getName().startsWith("java.") && !type.getName().startsWith("javax.")) {
|
||||
String ct = simpleComponentType(factory, logger, componentsMap, type, genericType);
|
||||
if (ct == null) {
|
||||
schemaMap.put("type", "object");
|
||||
} else {
|
||||
schemaMap.put("$ref", "#/components/schemas/" + ct);
|
||||
}
|
||||
} else {
|
||||
schemaMap.put("type", "object");
|
||||
}
|
||||
}
|
||||
|
||||
private static String simpleComponentType(JsonFactory factory, Logger logger, Map<String, Map<String, Object>> componentsMap, Class type, Type genericType) {
|
||||
try {
|
||||
Set<Type> types = new HashSet<>();
|
||||
Encodeable encodeable = JsonFactory.root().loadEncoder(genericType);
|
||||
String ct = componentKey(factory, logger, types, componentsMap, null, encodeable, true);
|
||||
if (ct == null || ct.length() == 0) return null;
|
||||
if (componentsMap.containsKey(ct)) return ct;
|
||||
Map<String, Object> cmap = new LinkedHashMap<>();
|
||||
componentsMap.put(ct, cmap); //必须在调用simpleSchemaType之前put,不然嵌套情况下死循环
|
||||
|
||||
cmap.put("type", "object");
|
||||
List<String> requireds = new ArrayList<>();
|
||||
Map<String, Object> properties = new LinkedHashMap<>();
|
||||
if (encodeable instanceof ObjectEncoder) {
|
||||
for (EnMember member : ((ObjectEncoder) encodeable).getMembers()) {
|
||||
Map<String, Object> schemaMap = new LinkedHashMap<>();
|
||||
simpleSchemaType(factory, logger, componentsMap, TypeToken.typeToClassOrElse(member.getEncoder().getType(), Object.class), member.getEncoder().getType(), schemaMap, true);
|
||||
String desc = "";
|
||||
if (member.getField() != null) {
|
||||
Column col = member.getField().getAnnotation(Column.class);
|
||||
if (col == null) {
|
||||
FilterColumn fcol = member.getField().getAnnotation(FilterColumn.class);
|
||||
if (fcol != null) {
|
||||
desc = fcol.comment();
|
||||
if (fcol.required()) requireds.add(member.getAttribute().field());
|
||||
}
|
||||
} else {
|
||||
desc = col.comment();
|
||||
if (!col.nullable()) requireds.add(member.getAttribute().field());
|
||||
}
|
||||
if (desc.isEmpty() && member.getField().getAnnotation(Comment.class) != null) {
|
||||
desc = member.getField().getAnnotation(Comment.class).value();
|
||||
}
|
||||
} else if (member.getMethod() != null) {
|
||||
Column col = member.getMethod().getAnnotation(Column.class);
|
||||
if (col == null) {
|
||||
FilterColumn fcol = member.getMethod().getAnnotation(FilterColumn.class);
|
||||
if (fcol != null) {
|
||||
desc = fcol.comment();
|
||||
if (fcol.required()) requireds.add(member.getAttribute().field());
|
||||
}
|
||||
} else {
|
||||
desc = col.comment();
|
||||
if (!col.nullable()) requireds.add(member.getAttribute().field());
|
||||
}
|
||||
if (desc.isEmpty() && member.getMethod().getAnnotation(Comment.class) != null) {
|
||||
desc = member.getMethod().getAnnotation(Comment.class).value();
|
||||
}
|
||||
}
|
||||
if (!desc.isEmpty()) schemaMap.put("description", desc);
|
||||
properties.put(member.getAttribute().field(), schemaMap);
|
||||
}
|
||||
}
|
||||
if (!requireds.isEmpty()) cmap.put("required", requireds);
|
||||
cmap.put("properties", properties);
|
||||
return ct;
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, genericType + " generate component info error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String componentKey(JsonFactory factory, Logger logger, Set<Type> types, Map<String, Map<String, Object>> componentsMap, EnMember field, Encodeable encodeable, boolean first) {
|
||||
if (encodeable instanceof ObjectEncoder) {
|
||||
if (types.contains(encodeable.getType())) return "";
|
||||
types.add(encodeable.getType());
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(((ObjectEncoder) encodeable).getTypeClass().getSimpleName());
|
||||
for (EnMember member : ((ObjectEncoder) encodeable).getMembers()) {
|
||||
if (member.getEncoder() instanceof ArrayEncoder
|
||||
|| member.getEncoder() instanceof CollectionEncoder) {
|
||||
String subsb = componentKey(factory, logger, types, componentsMap, member, member.getEncoder(), false);
|
||||
if (subsb == null) return null;
|
||||
AccessibleObject real = member.getField() == null ? member.getMethod() : member.getField();
|
||||
if (real == null) continue;
|
||||
Class cz = real instanceof Field ? ((Field) real).getType() : ((Method) real).getReturnType();
|
||||
Type ct = real instanceof Field ? ((Field) real).getGenericType() : ((Method) real).getGenericReturnType();
|
||||
if (cz == ct) continue;
|
||||
if (field == null && encodeable.getType() instanceof Class) continue;
|
||||
if (sb.length() > 0 && subsb.length() > 0) sb.append("_");
|
||||
sb.append(subsb);
|
||||
} else if (member.getEncoder() instanceof ObjectEncoder || member.getEncoder() instanceof SimpledCoder) {
|
||||
AccessibleObject real = member.getField() == null ? member.getMethod() : member.getField();
|
||||
if (real == null) continue;
|
||||
if (types.contains(member.getEncoder().getType())) continue;
|
||||
types.add(member.getEncoder().getType());
|
||||
if (member.getEncoder() instanceof SimpledCoder) {
|
||||
simpleSchemaType(factory, logger, componentsMap, ((SimpledCoder) member.getEncoder()).getType(), ((SimpledCoder) member.getEncoder()).getType(), new LinkedHashMap<>(), true);
|
||||
} else {
|
||||
simpleSchemaType(factory, logger, componentsMap, ((ObjectEncoder) member.getEncoder()).getTypeClass(), ((ObjectEncoder) member.getEncoder()).getType(), new LinkedHashMap<>(), true);
|
||||
}
|
||||
Class cz = real instanceof Field ? ((Field) real).getType() : ((Method) real).getReturnType();
|
||||
Type ct = real instanceof Field ? ((Field) real).getGenericType() : ((Method) real).getGenericReturnType();
|
||||
if (cz == ct) continue;
|
||||
String subsb = componentKey(factory, logger, types, componentsMap, member, member.getEncoder(), false);
|
||||
if (subsb == null) return null;
|
||||
if (field == null && member.getEncoder().getType() instanceof Class) continue;
|
||||
if (sb.length() > 0 && subsb.length() > 0) sb.append("_");
|
||||
sb.append(subsb);
|
||||
} else if (member.getEncoder() instanceof MapEncoder) {
|
||||
continue;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
} else if (encodeable instanceof ArrayEncoder || encodeable instanceof CollectionEncoder) {
|
||||
final boolean array = (encodeable instanceof ArrayEncoder);
|
||||
Encodeable subEncodeable = array ? ((ArrayEncoder) encodeable).getComponentEncoder() : ((CollectionEncoder) encodeable).getComponentEncoder();
|
||||
if (subEncodeable instanceof SimpledCoder && field != null) return "";
|
||||
final String sb = componentKey(factory, logger, types, componentsMap, null, subEncodeable, false);
|
||||
if (sb == null || sb.isEmpty()) return sb;
|
||||
if (field != null && field.getField() != null && field.getField().getDeclaringClass() == Sheet.class) {
|
||||
return sb;
|
||||
}
|
||||
return sb + (array ? "_Array" : "_Collection");
|
||||
} else if (encodeable instanceof SimpledCoder) {
|
||||
Class stype = ((SimpledCoder) encodeable).getType();
|
||||
if (stype.isPrimitive() || stype == Boolean.class || Number.class.isAssignableFrom(stype) || CharSequence.class.isAssignableFrom(stype)) {
|
||||
return stype.getSimpleName();
|
||||
}
|
||||
return "";
|
||||
} else if (encodeable instanceof MapEncoder) {
|
||||
return first ? null : "";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Object formatExample(JsonFactory factory, String example, Class type, Type genericType) {
|
||||
if (example != null && !example.isEmpty()) return example;
|
||||
JsonFactory jsonFactory = factory == null || factory == JsonFactory.root() ? exampleFactory : factory;
|
||||
if (type == Flipper.class) {
|
||||
return new Flipper();
|
||||
} else if (TYPE_RETRESULT_OBJECT.equals(genericType)) {
|
||||
return RetResult.success();
|
||||
} else if (TYPE_RETRESULT_STRING.equals(genericType)) {
|
||||
return RetResult.success();
|
||||
} else if (TYPE_RETRESULT_INTEGER.equals(genericType)) {
|
||||
return RetResult.success(0);
|
||||
} else if (TYPE_RETRESULT_LONG.equals(genericType)) {
|
||||
return RetResult.success(0L);
|
||||
} else if (type == boolean.class || type == Boolean.class) {
|
||||
return true;
|
||||
} else if (type.isPrimitive()) {
|
||||
return 0;
|
||||
} else if (type == boolean[].class || type == Boolean[].class) {
|
||||
return new boolean[]{true, false};
|
||||
} else if (type == byte[].class || type == Byte[].class) {
|
||||
return new byte[]{0, 0};
|
||||
} else if (type == char[].class || type == Character[].class) {
|
||||
return new char[]{'a', 'b'};
|
||||
} else if (type == short[].class || type == Short[].class) {
|
||||
return new short[]{0, 0};
|
||||
} else if (type == int[].class || type == Integer[].class) {
|
||||
return new int[]{0, 0};
|
||||
} else if (type == long[].class || type == Long[].class) {
|
||||
return new long[]{0, 0};
|
||||
} else if (type == float[].class || type == Float[].class) {
|
||||
return new float[]{0, 0};
|
||||
} else if (type == double[].class || type == Double[].class) {
|
||||
return new double[]{0, 0};
|
||||
} else if (Number.class.isAssignableFrom(type)) {
|
||||
return 0;
|
||||
} else if (CharSequence.class.isAssignableFrom(type)) {
|
||||
return "";
|
||||
} else if (CompletableFuture.class.isAssignableFrom(type)) {
|
||||
if (genericType instanceof ParameterizedType) {
|
||||
try {
|
||||
ParameterizedType pt = (ParameterizedType) genericType;
|
||||
Type valType = pt.getActualTypeArguments()[0];
|
||||
return formatExample(factory, example, valType instanceof ParameterizedType ? (Class) ((ParameterizedType) valType).getRawType() : ((Class) valType), valType);
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
}
|
||||
} else if (Sheet.class.isAssignableFrom(type)) { //要在Collection前面
|
||||
if (genericType instanceof ParameterizedType) {
|
||||
try {
|
||||
ParameterizedType pt = (ParameterizedType) genericType;
|
||||
Type valType = pt.getActualTypeArguments()[0];
|
||||
Class valClass = valType instanceof ParameterizedType ? (Class) ((ParameterizedType) valType).getRawType() : (Class) valType;
|
||||
Object val = formatExample(factory, example, valClass, valType);
|
||||
return new StringWrapper(jsonFactory.getConvert().convertTo(jsonFactory.getConvert().convertFrom(genericType, "{'rows':[" + val + "," + val + "]}")));
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
}
|
||||
} else if (type.isArray()) {
|
||||
try {
|
||||
Object val = formatExample(factory, example, type.getComponentType(), type.getComponentType());
|
||||
return new StringWrapper(jsonFactory.getConvert().convertTo(jsonFactory.getConvert().convertFrom(genericType, "[" + val + "," + val + "]")));
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
} else if (Collection.class.isAssignableFrom(type)) {
|
||||
if (genericType instanceof ParameterizedType) {
|
||||
try {
|
||||
ParameterizedType pt = (ParameterizedType) genericType;
|
||||
Type valType = pt.getActualTypeArguments()[0];
|
||||
Class valClass = valType instanceof ParameterizedType ? (Class) ((ParameterizedType) valType).getRawType() : (Class) valType;
|
||||
Object val = formatExample(factory, example, valClass, valType);
|
||||
return new StringWrapper(jsonFactory.getConvert().convertTo(jsonFactory.getConvert().convertFrom(genericType, "[" + val + "," + val + "]")));
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
}
|
||||
} else if (type == RetResult.class) {
|
||||
if (genericType instanceof ParameterizedType) {
|
||||
try {
|
||||
ParameterizedType pt = (ParameterizedType) genericType;
|
||||
Type valType = pt.getActualTypeArguments()[0];
|
||||
Class valClass = valType instanceof ParameterizedType ? (Class) ((ParameterizedType) valType).getRawType() : (Class) valType;
|
||||
Object val = formatExample(factory, example, valClass, valType);
|
||||
return new StringWrapper(jsonFactory.getConvert().convertTo(jsonFactory.getConvert().convertFrom(genericType, "{'result':" + val + "}")));
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
}
|
||||
} else if (type != void.class) {
|
||||
try {
|
||||
Decodeable decoder = jsonFactory.loadDecoder(genericType);
|
||||
if (decoder instanceof ObjectDecoder) {
|
||||
StringBuilder json = new StringBuilder();
|
||||
json.append("{");
|
||||
int index = 0;
|
||||
for (DeMember member : ((ObjectDecoder) decoder).getMembers()) {
|
||||
if (!(member.getDecoder() instanceof ObjectDecoder)) continue;
|
||||
if (index > 0) json.append(",");
|
||||
json.append('"').append(member.getAttribute().field()).append("\":{}");
|
||||
index++;
|
||||
}
|
||||
json.append("}");
|
||||
Object val = jsonFactory.getConvert().convertFrom(genericType, json.toString());
|
||||
return new StringWrapper(jsonFactory.getConvert().convertTo(val));
|
||||
}
|
||||
Creator creator = Creator.create(type);
|
||||
return new StringWrapper(jsonFactory.getConvert().convertTo(creator.create()));
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
}
|
||||
return example;
|
||||
}
|
||||
|
||||
private static final JsonFactory exampleFactory = JsonFactory.create().tiny(false);
|
||||
|
||||
}
|
||||
1915
src/main/java/org/redkale/boot/Application.java
Normal file
1915
src/main/java/org/redkale/boot/Application.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -35,6 +35,30 @@ public interface ApplicationListener {
|
||||
default void preStart(Application application) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Application 在运行start后调用
|
||||
*
|
||||
* @param application Application
|
||||
*/
|
||||
default void postStart(Application application) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Application 在运行Compile前调用
|
||||
*
|
||||
* @param application Application
|
||||
*/
|
||||
default void preCompile(Application application) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Application 在运行Compile后调用
|
||||
*
|
||||
* @param application Application
|
||||
*/
|
||||
default void postCompile(Application application) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Application 在运行shutdown前调用
|
||||
*
|
||||
@@ -9,12 +9,14 @@ import java.io.*;
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.jar.*;
|
||||
import java.util.logging.*;
|
||||
import java.util.regex.*;
|
||||
import javax.annotation.Priority;
|
||||
import org.redkale.util.*;
|
||||
import org.redkale.util.AnyValue.DefaultAnyValue;
|
||||
|
||||
@@ -63,11 +65,11 @@ public final class ClassFilter<T> {
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
public ClassFilter(ClassLoader classLoader, Class<? extends Annotation> annotationClass, Class superClass, Class[] excludeSuperClasses) {
|
||||
public ClassFilter(RedkaleClassLoader classLoader, Class<? extends Annotation> annotationClass, Class superClass, Class[] excludeSuperClasses) {
|
||||
this(classLoader, annotationClass, superClass, excludeSuperClasses, null);
|
||||
}
|
||||
|
||||
public ClassFilter(ClassLoader classLoader, Class<? extends Annotation> annotationClass, Class superClass, Class[] excludeSuperClasses, AnyValue conf) {
|
||||
public ClassFilter(RedkaleClassLoader classLoader, Class<? extends Annotation> annotationClass, Class superClass, Class[] excludeSuperClasses, AnyValue conf) {
|
||||
this.annotationClass = annotationClass;
|
||||
this.superClass = superClass;
|
||||
this.excludeSuperClasses = excludeSuperClasses;
|
||||
@@ -75,8 +77,8 @@ public final class ClassFilter<T> {
|
||||
this.classLoader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
|
||||
}
|
||||
|
||||
public static ClassFilter create(Class[] excludeSuperClasses, String includeregs, String excluderegs, Set<String> includeValues, Set<String> excludeValues) {
|
||||
ClassFilter filter = new ClassFilter(null, null, null, excludeSuperClasses);
|
||||
public static ClassFilter create(RedkaleClassLoader classLoader, Class[] excludeSuperClasses, String includeregs, String excluderegs, Set<String> includeValues, Set<String> excludeValues) {
|
||||
ClassFilter filter = new ClassFilter(classLoader, null, null, excludeSuperClasses);
|
||||
filter.setIncludePatterns(includeregs == null ? null : includeregs.split(";"));
|
||||
filter.setExcludePatterns(excluderegs == null ? null : excluderegs.split(";"));
|
||||
filter.setPrivilegeIncludes(includeValues);
|
||||
@@ -102,11 +104,12 @@ public final class ClassFilter<T> {
|
||||
* @return Set<FilterEntry<T>>
|
||||
*/
|
||||
public final Set<FilterEntry<T>> getFilterEntrys() {
|
||||
HashSet<FilterEntry<T>> set = new HashSet<>();
|
||||
set.addAll(entrys);
|
||||
if (ors != null) ors.forEach(f -> set.addAll(f.getFilterEntrys()));
|
||||
if (ands != null) ands.forEach(f -> set.addAll(f.getFilterEntrys()));
|
||||
return set;
|
||||
List<FilterEntry<T>> list = new ArrayList<>();
|
||||
list.addAll(entrys);
|
||||
if (ors != null) ors.forEach(f -> list.addAll(f.getFilterEntrys()));
|
||||
if (ands != null) ands.forEach(f -> list.addAll(f.getFilterEntrys()));
|
||||
Collections.sort(list);
|
||||
return new LinkedHashSet<>(list);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,11 +118,12 @@ public final class ClassFilter<T> {
|
||||
* @return Set<FilterEntry<T>>
|
||||
*/
|
||||
public final Set<FilterEntry<T>> getFilterExpectEntrys() {
|
||||
HashSet<FilterEntry<T>> set = new HashSet<>();
|
||||
set.addAll(expectEntrys);
|
||||
if (ors != null) ors.forEach(f -> set.addAll(f.getFilterExpectEntrys()));
|
||||
if (ands != null) ands.forEach(f -> set.addAll(f.getFilterExpectEntrys()));
|
||||
return set;
|
||||
List<FilterEntry<T>> list = new ArrayList<>();
|
||||
list.addAll(expectEntrys);
|
||||
if (ors != null) ors.forEach(f -> list.addAll(f.getFilterExpectEntrys()));
|
||||
if (ands != null) ands.forEach(f -> list.addAll(f.getFilterExpectEntrys()));
|
||||
Collections.sort(list);
|
||||
return new LinkedHashSet<>(list);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,7 +132,7 @@ public final class ClassFilter<T> {
|
||||
* @return Set<FilterEntry<T>>
|
||||
*/
|
||||
public final Set<FilterEntry<T>> getAllFilterEntrys() {
|
||||
HashSet<FilterEntry<T>> rs = new HashSet<>();
|
||||
HashSet<FilterEntry<T>> rs = new LinkedHashSet<>();
|
||||
rs.addAll(getFilterEntrys());
|
||||
rs.addAll(getFilterExpectEntrys());
|
||||
return rs;
|
||||
@@ -175,8 +179,9 @@ public final class ClassFilter<T> {
|
||||
}
|
||||
if (!r && ors != null) {
|
||||
for (ClassFilter filter : ors) {
|
||||
if (filter.accept(property, clazzname)) {
|
||||
if (filter.accept(filter.conf, clazzname)) {
|
||||
cf = filter;
|
||||
property = cf.conf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -189,11 +194,11 @@ public final class ClassFilter<T> {
|
||||
if (property == null) {
|
||||
property = cf.conf;
|
||||
} else if (property instanceof DefaultAnyValue) {
|
||||
((DefaultAnyValue) property).addAll(cf.conf);
|
||||
((DefaultAnyValue) property).addAllStringSet(cf.conf);
|
||||
} else {
|
||||
DefaultAnyValue dav = new DefaultAnyValue();
|
||||
dav.addAll(property);
|
||||
dav.addAll(cf.conf);
|
||||
dav.addAllStringSet(property);
|
||||
dav.addAllStringSet(cf.conf);
|
||||
property = dav;
|
||||
}
|
||||
}
|
||||
@@ -207,7 +212,9 @@ public final class ClassFilter<T> {
|
||||
} catch (Throwable cfe) {
|
||||
if (finest && !clazzname.startsWith("sun.") && !clazzname.startsWith("javax.")
|
||||
&& !clazzname.startsWith("com.sun.") && !clazzname.startsWith("jdk.") && !clazzname.startsWith("META-INF")
|
||||
&& (!(cfe instanceof NoClassDefFoundError) || (cfe instanceof UnsupportedClassVersionError) || ((NoClassDefFoundError) cfe).getMessage().startsWith("java.lang.NoClassDefFoundError: java"))) {
|
||||
&& !clazzname.startsWith("com.mysql.") && !clazzname.startsWith("com.microsoft.") && !clazzname.startsWith("freemarker.")
|
||||
&& !clazzname.startsWith("org.redkale") && (clazzname.contains("Service") || clazzname.contains("Servlet"))) {
|
||||
//&& (!(cfe instanceof NoClassDefFoundError) || (cfe instanceof UnsupportedClassVersionError) || ((NoClassDefFoundError) cfe).getMessage().startsWith("java.lang.NoClassDefFoundError: java"))) {
|
||||
logger.log(Level.FINEST, ClassFilter.class.getSimpleName() + " filter error for class: " + clazzname + (url == null ? "" : (" in " + url)), cfe);
|
||||
}
|
||||
}
|
||||
@@ -241,7 +248,7 @@ public final class ClassFilter<T> {
|
||||
}
|
||||
if (!r && ors != null) {
|
||||
for (ClassFilter filter : ors) {
|
||||
if (filter.accept(property, classname)) return true;
|
||||
if (filter.accept(filter.conf, classname)) return true;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
@@ -380,7 +387,7 @@ public final class ClassFilter<T> {
|
||||
*
|
||||
* @param <T> 泛型
|
||||
*/
|
||||
public static final class FilterEntry<T> {
|
||||
public static final class FilterEntry<T> implements Comparable<FilterEntry<T>> {
|
||||
|
||||
private final HashSet<String> groups = new LinkedHashSet<>();
|
||||
|
||||
@@ -405,17 +412,25 @@ public final class ClassFilter<T> {
|
||||
str = str.trim();
|
||||
if (str.endsWith(";")) str = str.substring(0, str.length() - 1);
|
||||
}
|
||||
if (str != null) groups.addAll(Arrays.asList(str.split(";")));
|
||||
if (str != null) this.groups.addAll(Arrays.asList(str.split(";")));
|
||||
this.property = property;
|
||||
this.autoload = autoload;
|
||||
this.expect = expect;
|
||||
this.name = property == null ? "" : property.getValue("name", "");
|
||||
}
|
||||
|
||||
@Override //@Priority值越大,优先级越高, 需要排前面
|
||||
public int compareTo(FilterEntry o) {
|
||||
if (!(o instanceof FilterEntry)) return 1;
|
||||
Priority p1 = this.type.getAnnotation(Priority.class);
|
||||
Priority p2 = ((FilterEntry<T>) o).type.getAnnotation(Priority.class);
|
||||
return (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + "[thread=" + Thread.currentThread().getName()
|
||||
+ ", type=" + this.type.getSimpleName() + ", name=" + name + ", groups=" + groups + "]";
|
||||
+ ", type=" + this.type.getSimpleName() + ", name=" + name + ", groups=" + this.groups + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -442,6 +457,10 @@ public final class ClassFilter<T> {
|
||||
return property;
|
||||
}
|
||||
|
||||
public boolean containsGroup(String group) {
|
||||
return groups != null && groups.contains(group);
|
||||
}
|
||||
|
||||
public boolean isEmptyGroups() {
|
||||
return groups == null || groups.isEmpty();
|
||||
}
|
||||
@@ -457,6 +476,7 @@ public final class ClassFilter<T> {
|
||||
public boolean isExpect() {
|
||||
return expect;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -476,24 +496,24 @@ public final class ClassFilter<T> {
|
||||
* 加载当前线程的classpath扫描所有class进行过滤
|
||||
*
|
||||
* @param excludeFile 不需要扫描的文件夹, 可以为null
|
||||
* @param loader RedkaleClassloader, 不可为null
|
||||
* @param excludeRegs 包含此关键字的文件将被跳过, 可以为null
|
||||
* @param filters 过滤器
|
||||
*
|
||||
* @throws IOException 异常
|
||||
*/
|
||||
public static void load(final File excludeFile, final String[] excludeRegs, final ClassFilter... filters) throws IOException {
|
||||
RedkaleClassLoader loader = (RedkaleClassLoader) Thread.currentThread().getContextClassLoader();
|
||||
public static void load(final File excludeFile, RedkaleClassLoader loader, final String[] excludeRegs, final ClassFilter... filters) throws IOException {
|
||||
List<URL> urlfiles = new ArrayList<>(2);
|
||||
List<URL> urljares = new ArrayList<>(2);
|
||||
final URL exurl = excludeFile != null ? excludeFile.toURI().toURL() : null;
|
||||
final Pattern[] excludePatterns = toPattern(excludeRegs);
|
||||
for (URL url : loader.getAllURLs()) {
|
||||
if (exurl != null && exurl.sameFile(url)) continue;
|
||||
if (excludePatterns != null) {
|
||||
if (excludePatterns != null && url != RedkaleClassLoader.URL_NONE) {
|
||||
boolean skip = false;
|
||||
for (Pattern p : excludePatterns) {
|
||||
if (p.matcher(url.toString()).matches()) {
|
||||
skip = false;
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -512,7 +532,7 @@ public final class ClassFilter<T> {
|
||||
Set<String> classes = cache.get(url);
|
||||
if (classes == null) {
|
||||
classes = new LinkedHashSet<>();
|
||||
try (JarFile jar = new JarFile(URLDecoder.decode(url.getFile(), "UTF-8"))) {
|
||||
try (JarFile jar = new JarFile(URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8))) {
|
||||
Enumeration<JarEntry> it = jar.entries();
|
||||
while (it.hasMoreElements()) {
|
||||
String entryname = it.nextElement().getName().replace('/', '.');
|
||||
@@ -520,6 +540,7 @@ public final class ClassFilter<T> {
|
||||
String classname = entryname.substring(0, entryname.length() - 6);
|
||||
if (classname.startsWith("javax.") || classname.startsWith("com.sun.")) continue;
|
||||
//常见的jar跳过
|
||||
if (classname.startsWith("com.redkaledyn.")) break; //redkale动态生成的类
|
||||
if (classname.startsWith("com.mysql.")) break;
|
||||
if (classname.startsWith("org.mariadb.")) break;
|
||||
if (classname.startsWith("oracle.jdbc.")) break;
|
||||
@@ -546,17 +567,27 @@ public final class ClassFilter<T> {
|
||||
Set<String> classes = cache.get(url);
|
||||
if (classes == null) {
|
||||
classes = new LinkedHashSet<>();
|
||||
files.clear();
|
||||
File root = new File(url.getFile());
|
||||
String rootpath = root.getPath();
|
||||
loadClassFiles(excludeFile, root, files);
|
||||
for (File f : files) {
|
||||
String classname = f.getPath().substring(rootpath.length() + 1, f.getPath().length() - 6).replace(File.separatorChar, '.');
|
||||
if (classname.startsWith("javax.") || classname.startsWith("com.sun.")) continue;
|
||||
classes.add(classname);
|
||||
if (debug) debugstr.append(classname).append("\r\n");
|
||||
for (final ClassFilter filter : filters) {
|
||||
if (filter != null) filter.filter(null, classname, url);
|
||||
final Set<String> cs = classes;
|
||||
if (url == RedkaleClassLoader.URL_NONE) loader.forEachCacheClass(v -> cs.add(v));
|
||||
if (cs.isEmpty()) {
|
||||
files.clear();
|
||||
File root = new File(url.getFile());
|
||||
String rootpath = root.getPath();
|
||||
loadClassFiles(excludeFile, root, files);
|
||||
for (File f : files) {
|
||||
String classname = f.getPath().substring(rootpath.length() + 1, f.getPath().length() - 6).replace(File.separatorChar, '.');
|
||||
if (classname.startsWith("javax.") || classname.startsWith("com.sun.")) continue;
|
||||
classes.add(classname);
|
||||
if (debug) debugstr.append(classname).append("\r\n");
|
||||
for (final ClassFilter filter : filters) {
|
||||
if (filter != null) filter.filter(null, classname, url);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (String classname : classes) {
|
||||
for (final ClassFilter filter : filters) {
|
||||
if (filter != null) filter.filter(null, classname, url);
|
||||
}
|
||||
}
|
||||
}
|
||||
cache.put(url, classes);
|
||||
20
src/main/java/org/redkale/boot/LoggingBaseHandler.java
Normal file
20
src/main/java/org/redkale/boot/LoggingBaseHandler.java
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
*/
|
||||
package org.redkale.boot;
|
||||
|
||||
import java.util.logging.Handler;
|
||||
|
||||
/**
|
||||
* Handler基类
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @since 2.7.0
|
||||
*/
|
||||
public abstract class LoggingBaseHandler extends Handler {
|
||||
|
||||
protected Application currentApplication() {
|
||||
return Application.currentApplication; //不能直接暴露外界访问
|
||||
}
|
||||
}
|
||||
@@ -5,16 +5,18 @@
|
||||
*/
|
||||
package org.redkale.boot;
|
||||
|
||||
import org.redkale.util.RedkaleClassLoader;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.*;
|
||||
import static java.nio.file.StandardCopyOption.*;
|
||||
import java.time.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
import java.util.logging.*;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.regex.Pattern;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* 自定义的日志输出类
|
||||
@@ -24,12 +26,17 @@ import java.util.regex.Pattern;
|
||||
* @author zhangjx
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class LogFileHandler extends Handler {
|
||||
public class LoggingFileHandler extends LoggingBaseHandler {
|
||||
|
||||
//public static final String FORMATTER_FORMAT = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL %4$s %2$s%n%5$s%6$s%n";
|
||||
public static final String FORMATTER_FORMAT = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL %4$s %2$s\r\n%5$s%6$s\r\n";
|
||||
|
||||
static boolean traceflag = false; //防止设置system.property前调用Traces类导致enable提前初始化
|
||||
|
||||
/**
|
||||
* SNCP的日志输出Handler
|
||||
*/
|
||||
public static class SncpLogFileHandler extends LogFileHandler {
|
||||
public static class LoggingSncpFileHandler extends LoggingFileHandler {
|
||||
|
||||
@Override
|
||||
public String getPrefix() {
|
||||
@@ -37,28 +44,71 @@ public class LogFileHandler extends Handler {
|
||||
}
|
||||
}
|
||||
|
||||
public static class LoggingConsoleHandler extends ConsoleHandler {
|
||||
|
||||
private Pattern denyreg;
|
||||
|
||||
public LoggingConsoleHandler() {
|
||||
super();
|
||||
setFormatter(new LoggingFormater());
|
||||
configure();
|
||||
}
|
||||
|
||||
private void configure() {
|
||||
LogManager manager = LogManager.getLogManager();
|
||||
String denyregstr = manager.getProperty("java.util.logging.ConsoleHandler.denyreg");
|
||||
try {
|
||||
if (denyregstr != null && !denyregstr.trim().isEmpty()) {
|
||||
denyreg = Pattern.compile(denyregstr);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(LogRecord log) {
|
||||
if (denyreg != null && denyreg.matcher(log.getMessage()).find()) return;
|
||||
if (traceflag && Traces.enable()) {
|
||||
String traceid = Traces.currTraceid();
|
||||
if (traceid == null || traceid.isEmpty()) {
|
||||
traceid = "[TID:N/A] ";
|
||||
} else {
|
||||
traceid = "[TID:" + traceid + "] ";
|
||||
}
|
||||
if (log.getMessage() == null) {
|
||||
log.setMessage(traceid);
|
||||
} else {
|
||||
log.setMessage(traceid + log.getMessage());
|
||||
}
|
||||
}
|
||||
super.publish(log);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认的日志时间格式化类
|
||||
* 与SimpleFormatter的区别在于level不使用本地化
|
||||
*
|
||||
*/
|
||||
public static class LoggingFormater extends Formatter {
|
||||
|
||||
private static final String format = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL %4$s %2$s\r\n%5$s%6$s\r\n";
|
||||
|
||||
@Override
|
||||
public String format(LogRecord record) {
|
||||
public String format(LogRecord log) {
|
||||
if (log.getThrown() == null && log.getMessage() != null && log.getMessage().startsWith("------")) {
|
||||
return formatMessage(log) + "\r\n";
|
||||
}
|
||||
String source;
|
||||
if (record.getSourceClassName() != null) {
|
||||
source = record.getSourceClassName();
|
||||
if (record.getSourceMethodName() != null) {
|
||||
source += " " + record.getSourceMethodName();
|
||||
if (log.getSourceClassName() != null) {
|
||||
source = log.getSourceClassName();
|
||||
if (log.getSourceMethodName() != null) {
|
||||
source += " " + log.getSourceMethodName();
|
||||
}
|
||||
} else {
|
||||
source = record.getLoggerName();
|
||||
source = log.getLoggerName();
|
||||
}
|
||||
String message = formatMessage(record);
|
||||
String message = formatMessage(log);
|
||||
String throwable = "";
|
||||
if (record.getThrown() != null) {
|
||||
if (log.getThrown() != null) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw) {
|
||||
@Override
|
||||
@@ -67,26 +117,46 @@ public class LogFileHandler extends Handler {
|
||||
}
|
||||
};
|
||||
pw.println();
|
||||
record.getThrown().printStackTrace(pw);
|
||||
log.getThrown().printStackTrace(pw);
|
||||
pw.close();
|
||||
throwable = sw.toString();
|
||||
}
|
||||
return String.format(format,
|
||||
return String.format(FORMATTER_FORMAT,
|
||||
System.currentTimeMillis(),
|
||||
source,
|
||||
record.getLoggerName(),
|
||||
record.getLevel().getName(),
|
||||
log.getLoggerName(),
|
||||
log.getLevel().getName(),
|
||||
message,
|
||||
throwable);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected final LinkedBlockingQueue<LogRecord> records = new LinkedBlockingQueue();
|
||||
public static void initDebugLogConfig() {
|
||||
try {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
final PrintStream ps = new PrintStream(out);
|
||||
ps.println("handlers = java.util.logging.ConsoleHandler");
|
||||
ps.println(".level = FINEST");
|
||||
ps.println("jdk.level = INFO");
|
||||
ps.println("sun.level = INFO");
|
||||
ps.println("com.sun.level = INFO");
|
||||
ps.println("javax.level = INFO");
|
||||
ps.println("java.util.logging.ConsoleHandler.level = FINEST");
|
||||
ps.println("java.util.logging.ConsoleHandler.formatter = " + LoggingFileHandler.LoggingFormater.class.getName());
|
||||
LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(out.toByteArray()));
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
private String pattern;
|
||||
protected final LinkedBlockingQueue<LogRecord> logqueue = new LinkedBlockingQueue();
|
||||
|
||||
private String unusual; //不为null表示将 WARNING、SEVERE 级别的日志写入单独的文件中
|
||||
protected String pattern;
|
||||
|
||||
protected String patternDateFormat; //需要时间格式化
|
||||
|
||||
protected String unusual; //不为null表示将 WARNING、SEVERE 级别的日志写入单独的文件中
|
||||
|
||||
protected String unusualDateFormat; //需要时间格式化
|
||||
|
||||
private int limit; //文件大小限制
|
||||
|
||||
@@ -98,9 +168,9 @@ public class LogFileHandler extends Handler {
|
||||
|
||||
private long tomorrow;
|
||||
|
||||
private boolean append;
|
||||
protected boolean append;
|
||||
|
||||
private Pattern denyreg;
|
||||
protected Pattern denyreg;
|
||||
|
||||
private final AtomicLong loglength = new AtomicLong();
|
||||
|
||||
@@ -114,7 +184,7 @@ public class LogFileHandler extends Handler {
|
||||
|
||||
private OutputStream logunusualstream;
|
||||
|
||||
public LogFileHandler() {
|
||||
public LoggingFileHandler() {
|
||||
updateTomorrow();
|
||||
configure();
|
||||
open();
|
||||
@@ -144,9 +214,9 @@ public class LogFileHandler extends Handler {
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
LogRecord record = records.take();
|
||||
LogRecord log = logqueue.take();
|
||||
final boolean bigger = (limit > 0 && limit <= loglength.get());
|
||||
final boolean changeday = tomorrow <= record.getMillis();
|
||||
final boolean changeday = tomorrow <= log.getMillis();
|
||||
if (bigger || changeday) {
|
||||
updateTomorrow();
|
||||
if (logstream != null) {
|
||||
@@ -178,27 +248,25 @@ public class LogFileHandler extends Handler {
|
||||
}
|
||||
if (logstream == null) {
|
||||
logindex.incrementAndGet();
|
||||
java.time.LocalDate date = LocalDate.now();
|
||||
logfile = new File(pattern.replace("%m", String.valueOf((date.getYear() * 100 + date.getMonthValue()))).replace("%d", String.valueOf((date.getYear() * 10000 + date.getMonthValue() * 100 + date.getDayOfMonth()))));
|
||||
logfile = new File(patternDateFormat == null ? pattern : Utility.formatTime(patternDateFormat, -1, System.currentTimeMillis()));
|
||||
logfile.getParentFile().mkdirs();
|
||||
loglength.set(logfile.length());
|
||||
logstream = new FileOutputStream(logfile, append);
|
||||
}
|
||||
if (unusual != null && logunusualstream == null) {
|
||||
logunusualindex.incrementAndGet();
|
||||
java.time.LocalDate date = LocalDate.now();
|
||||
logunusualfile = new File(unusual.replace("%m", String.valueOf((date.getYear() * 100 + date.getMonthValue()))).replace("%d", String.valueOf((date.getYear() * 10000 + date.getMonthValue() * 100 + date.getDayOfMonth()))));
|
||||
logunusualfile = new File(unusualDateFormat == null ? unusual : Utility.formatTime(unusualDateFormat, -1, System.currentTimeMillis()));
|
||||
logunusualfile.getParentFile().mkdirs();
|
||||
logunusuallength.set(logunusualfile.length());
|
||||
logunusualstream = new FileOutputStream(logunusualfile, append);
|
||||
}
|
||||
//----------------------写日志-------------------------
|
||||
String message = getFormatter().format(record);
|
||||
String message = getFormatter().format(log);
|
||||
String encoding = getEncoding();
|
||||
byte[] bytes = encoding == null ? message.getBytes() : message.getBytes(encoding);
|
||||
logstream.write(bytes);
|
||||
loglength.addAndGet(bytes.length);
|
||||
if (unusual != null && (record.getLevel() == Level.WARNING || record.getLevel() == Level.SEVERE)) {
|
||||
if (unusual != null && (log.getLevel() == Level.WARNING || log.getLevel() == Level.SEVERE)) {
|
||||
logunusualstream.write(bytes);
|
||||
logunusuallength.addAndGet(bytes.length);
|
||||
}
|
||||
@@ -218,10 +286,10 @@ public class LogFileHandler extends Handler {
|
||||
|
||||
private void configure() {
|
||||
LogManager manager = LogManager.getLogManager();
|
||||
String cname = LogFileHandler.class.getName();
|
||||
String cname = LoggingFileHandler.class.getName();
|
||||
this.pattern = manager.getProperty(cname + ".pattern");
|
||||
if (this.pattern == null) {
|
||||
this.pattern = "logs-%m/" + getPrefix() + "log-%d.log";
|
||||
this.pattern = "logs-%tm/" + getPrefix() + "log-%td.log";
|
||||
} else {
|
||||
int pos = this.pattern.lastIndexOf('/');
|
||||
if (pos > 0) {
|
||||
@@ -230,6 +298,10 @@ public class LogFileHandler extends Handler {
|
||||
this.pattern = getPrefix() + this.pattern;
|
||||
}
|
||||
}
|
||||
if (this.pattern != null && this.pattern.contains("%")) { //需要时间格式化
|
||||
this.patternDateFormat = this.pattern;
|
||||
Utility.formatTime(this.patternDateFormat, -1, System.currentTimeMillis()); //测试时间格式是否正确
|
||||
}
|
||||
String unusualstr = manager.getProperty(cname + ".unusual");
|
||||
if (unusualstr != null) {
|
||||
int pos = unusualstr.lastIndexOf('/');
|
||||
@@ -239,6 +311,10 @@ public class LogFileHandler extends Handler {
|
||||
this.unusual = getPrefix() + unusualstr;
|
||||
}
|
||||
}
|
||||
if (this.unusual != null && this.unusual.contains("%")) { //需要时间格式化
|
||||
this.unusualDateFormat = this.unusual;
|
||||
Utility.formatTime(this.unusualDateFormat, -1, System.currentTimeMillis()); //测试时间格式是否正确
|
||||
}
|
||||
String limitstr = manager.getProperty(cname + ".limit");
|
||||
try {
|
||||
if (limitstr != null) {
|
||||
@@ -280,6 +356,7 @@ public class LogFileHandler extends Handler {
|
||||
try {
|
||||
if (filterstr != null) {
|
||||
Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(filterstr);
|
||||
RedkaleClassLoader.putReflectionDeclaredConstructors(clz, clz.getName());
|
||||
setFilter((Filter) clz.getDeclaredConstructor().newInstance());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -288,6 +365,7 @@ public class LogFileHandler extends Handler {
|
||||
try {
|
||||
if (formatterstr != null) {
|
||||
Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(formatterstr);
|
||||
RedkaleClassLoader.putReflectionDeclaredConstructors(clz, clz.getName());
|
||||
setFormatter((Formatter) clz.getDeclaredConstructor().newInstance());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -310,21 +388,35 @@ public class LogFileHandler extends Handler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(LogRecord record) {
|
||||
final String sourceClassName = record.getSourceClassName();
|
||||
public void publish(LogRecord log) {
|
||||
if (!isLoggable(log)) return;
|
||||
final String sourceClassName = log.getSourceClassName();
|
||||
if (sourceClassName == null || true) {
|
||||
StackTraceElement[] ses = new Throwable().getStackTrace();
|
||||
for (int i = 2; i < ses.length; i++) {
|
||||
if (ses[i].getClassName().startsWith("java.util.logging")) continue;
|
||||
record.setSourceClassName('[' + Thread.currentThread().getName() + "] " + ses[i].getClassName());
|
||||
record.setSourceMethodName(ses[i].getMethodName());
|
||||
log.setSourceClassName('[' + Thread.currentThread().getName() + "] " + ses[i].getClassName());
|
||||
log.setSourceMethodName(ses[i].getMethodName());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
record.setSourceClassName('[' + Thread.currentThread().getName() + "] " + sourceClassName);
|
||||
log.setSourceClassName('[' + Thread.currentThread().getName() + "] " + sourceClassName);
|
||||
}
|
||||
if (denyreg != null && denyreg.matcher(record.getMessage()).find()) return;
|
||||
records.offer(record);
|
||||
if (denyreg != null && denyreg.matcher(log.getMessage()).find()) return;
|
||||
if (traceflag && Traces.enable()) {
|
||||
String traceid = Traces.currTraceid();
|
||||
if (traceid == null || traceid.isEmpty()) {
|
||||
traceid = "[TID:N/A] ";
|
||||
} else {
|
||||
traceid = "[TID:" + traceid + "] ";
|
||||
}
|
||||
if (log.getMessage() == null) {
|
||||
log.setMessage(traceid);
|
||||
} else {
|
||||
log.setMessage(traceid + log.getMessage());
|
||||
}
|
||||
}
|
||||
logqueue.offer(log);
|
||||
}
|
||||
|
||||
@Override
|
||||
303
src/main/java/org/redkale/boot/LoggingSearchHandler.java
Normal file
303
src/main/java/org/redkale/boot/LoggingSearchHandler.java
Normal file
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
*/
|
||||
package org.redkale.boot;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.*;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.persistence.*;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
import org.redkale.source.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* 基于SearchSource的日志输出类
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @since 2.7.0
|
||||
*/
|
||||
public class LoggingSearchHandler extends LoggingBaseHandler {
|
||||
|
||||
protected static final String DEFAULT_TABLE_NAME = "log-record";
|
||||
|
||||
protected final LinkedBlockingQueue<SearchLogRecord> logqueue = new LinkedBlockingQueue();
|
||||
|
||||
protected final AtomicInteger retryCount = new AtomicInteger(3);
|
||||
|
||||
protected String tag = DEFAULT_TABLE_NAME; //用于表前缀, 默认是
|
||||
|
||||
protected String tagDateFormat; //需要时间格式化
|
||||
|
||||
protected String pattern;
|
||||
|
||||
protected Pattern denyreg;
|
||||
|
||||
protected String sourceResourceName;
|
||||
|
||||
protected SearchSource source;
|
||||
|
||||
public LoggingSearchHandler() {
|
||||
configure();
|
||||
open();
|
||||
}
|
||||
|
||||
private void open() {
|
||||
final String name = "Redkale-" + getClass().getSimpleName() + "-Thread";
|
||||
final int batchSize = 100; //批量最多100条
|
||||
final List<SearchLogRecord> logList = new ArrayList<>();
|
||||
final SimpleFormatter formatter = new SimpleFormatter();
|
||||
final PrintStream outStream = System.out;
|
||||
new Thread() {
|
||||
{
|
||||
setName(name);
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
SearchLogRecord log = logqueue.take();
|
||||
while (source == null && retryCount.get() > 0) initSource();
|
||||
//----------------------写日志-------------------------
|
||||
if (source == null) { //source加载失败
|
||||
outStream.print(formatter.format(log.rawLog));
|
||||
} else {
|
||||
logList.add(log);
|
||||
int size = batchSize;
|
||||
while (--size > 0) {
|
||||
log = logqueue.poll();
|
||||
if (log == null) break;
|
||||
logList.add(log);
|
||||
}
|
||||
source.insert(logList);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ErrorManager err = getErrorManager();
|
||||
if (err != null) err.error(null, e, ErrorManager.WRITE_FAILURE);
|
||||
} finally {
|
||||
logList.clear();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
private synchronized void initSource() {
|
||||
if (retryCount.get() < 1) return;
|
||||
try {
|
||||
Utility.sleep(3000); //如果SearchSource自身在打印日志,需要停顿一点时间让SearchSource初始化完成
|
||||
Application application = currentApplication();
|
||||
this.source = (SearchSource) application.loadDataSource(sourceResourceName, false);
|
||||
if (retryCount.get() == 1 && this.source == null) System.err.println("ERROR: not load logging.source(" + sourceResourceName + ")");
|
||||
} catch (Exception t) {
|
||||
ErrorManager err = getErrorManager();
|
||||
if (err != null) err.error(null, t, ErrorManager.WRITE_FAILURE);
|
||||
} finally {
|
||||
retryCount.decrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checkTagName(String name) { //只能是字母、数字、短横、点、%、$和下划线
|
||||
if (name.isEmpty()) return false;
|
||||
for (char ch : name.toCharArray()) {
|
||||
if (ch >= '0' && ch <= '9') continue;
|
||||
if (ch >= 'a' && ch <= 'z') continue;
|
||||
if (ch >= 'A' && ch <= 'Z') continue;
|
||||
if (ch == '_' || ch == '-' || ch == '%' || ch == '$' || ch == '.') continue;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void configure() {
|
||||
LogManager manager = LogManager.getLogManager();
|
||||
String cname = getClass().getName();
|
||||
this.sourceResourceName = manager.getProperty(cname + ".source");
|
||||
if (this.sourceResourceName == null || this.sourceResourceName.isEmpty()) {
|
||||
throw new RuntimeException("not found logging.property " + cname + ".source");
|
||||
}
|
||||
String tagstr = manager.getProperty(cname + ".tag");
|
||||
if (tagstr != null && !tagstr.isEmpty()) {
|
||||
if (!checkTagName(tagstr.replaceAll("\\$\\{.+\\}", ""))) throw new RuntimeException("found illegal logging.property " + cname + ".tag = " + tagstr);
|
||||
this.tag = tagstr;
|
||||
if (tagstr.contains("%")) {
|
||||
this.tagDateFormat = this.tag;
|
||||
Utility.formatTime(this.tagDateFormat, -1, System.currentTimeMillis()); //测试时间格式是否正确
|
||||
}
|
||||
}
|
||||
|
||||
String levelstr = manager.getProperty(cname + ".level");
|
||||
try {
|
||||
if (levelstr != null) {
|
||||
Level l = Level.parse(levelstr);
|
||||
setLevel(l != null ? l : Level.ALL);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
String filterstr = manager.getProperty(cname + ".filter");
|
||||
try {
|
||||
if (filterstr != null) {
|
||||
Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(filterstr);
|
||||
RedkaleClassLoader.putReflectionDeclaredConstructors(clz, clz.getName());
|
||||
setFilter((Filter) clz.getDeclaredConstructor().newInstance());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
String formatterstr = manager.getProperty(cname + ".formatter");
|
||||
try {
|
||||
if (formatterstr != null) {
|
||||
Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(formatterstr);
|
||||
RedkaleClassLoader.putReflectionDeclaredConstructors(clz, clz.getName());
|
||||
setFormatter((Formatter) clz.getDeclaredConstructor().newInstance());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
if (getFormatter() == null) setFormatter(new SimpleFormatter());
|
||||
|
||||
String encodingstr = manager.getProperty(cname + ".encoding");
|
||||
try {
|
||||
if (encodingstr != null) setEncoding(encodingstr);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
String denyregstr = manager.getProperty(cname + ".denyreg");
|
||||
try {
|
||||
if (denyregstr != null && !denyregstr.trim().isEmpty()) {
|
||||
denyreg = Pattern.compile(denyregstr);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(LogRecord log) {
|
||||
if (!isLoggable(log)) return;
|
||||
final String sourceClassName = log.getSourceClassName();
|
||||
if (sourceClassName == null || true) {
|
||||
StackTraceElement[] ses = new Throwable().getStackTrace();
|
||||
for (int i = 2; i < ses.length; i++) {
|
||||
if (ses[i].getClassName().startsWith("java.util.logging")) continue;
|
||||
log.setSourceClassName(ses[i].getClassName());
|
||||
log.setSourceMethodName(ses[i].getMethodName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (denyreg != null && denyreg.matcher(log.getMessage()).find()) return;
|
||||
String rawTag = tagDateFormat == null ? tag : Utility.formatTime(tagDateFormat, -1, log.getInstant().toEpochMilli());
|
||||
logqueue.offer(new SearchLogRecord(rawTag, log));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SecurityException {
|
||||
}
|
||||
|
||||
@Entity
|
||||
@Table(name = DEFAULT_TABLE_NAME)
|
||||
@DistributeTable(strategy = SearchLogRecord.TableStrategy.class)
|
||||
public static class SearchLogRecord {
|
||||
|
||||
@Id
|
||||
@ConvertColumn(index = 1)
|
||||
@SearchColumn(options = "false")
|
||||
public String logid;
|
||||
|
||||
@ConvertColumn(index = 2)
|
||||
@SearchColumn(options = "false")
|
||||
public String level;
|
||||
|
||||
@ConvertColumn(index = 3)
|
||||
@SearchColumn(date = true)
|
||||
public long timestamp;
|
||||
|
||||
@ConvertColumn(index = 4)
|
||||
@SearchColumn(options = "false")
|
||||
public String traceid;
|
||||
|
||||
@ConvertColumn(index = 5)
|
||||
public String threadName;
|
||||
|
||||
@ConvertColumn(index = 6)
|
||||
@SearchColumn(text = true, options = "offsets")
|
||||
public String loggerName;
|
||||
|
||||
@ConvertColumn(index = 7)
|
||||
@SearchColumn(text = true, options = "offsets")
|
||||
public String methodName;
|
||||
|
||||
@ConvertColumn(index = 8)
|
||||
@SearchColumn(text = true, options = "offsets") //, analyzer = "ik_max_word"
|
||||
public String message; //log.message +"\r\n"+ log.thrown
|
||||
|
||||
@Transient
|
||||
@ConvertDisabled
|
||||
LogRecord rawLog;
|
||||
|
||||
@Transient
|
||||
@ConvertDisabled
|
||||
String rawTag;
|
||||
|
||||
public SearchLogRecord() {
|
||||
}
|
||||
|
||||
protected SearchLogRecord(String tag, LogRecord log) {
|
||||
this.rawLog = log;
|
||||
this.rawTag = tag;
|
||||
this.threadName = Thread.currentThread().getName();
|
||||
this.traceid = LoggingFileHandler.traceflag ? Traces.currTraceid() : null;
|
||||
String msg = log.getMessage();
|
||||
if (log.getThrown() != null) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
pw.println();
|
||||
log.getThrown().printStackTrace(pw);
|
||||
pw.close();
|
||||
String throwable = sw.toString();
|
||||
this.message = (msg != null && !msg.isEmpty()) ? (msg + "\r\n" + throwable) : throwable;
|
||||
} else {
|
||||
this.message = msg;
|
||||
}
|
||||
this.level = log.getLevel().toString();
|
||||
this.loggerName = log.getLoggerName();
|
||||
this.methodName = log.getSourceClassName() + " " + log.getSourceMethodName();
|
||||
this.timestamp = log.getInstant().toEpochMilli();
|
||||
this.logid = Utility.format36time(timestamp) + "_" + Utility.uuid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonConvert.root().convertTo(this);
|
||||
}
|
||||
|
||||
public static class TableStrategy implements DistributeTableStrategy<SearchLogRecord> {
|
||||
|
||||
@Override
|
||||
public String getTable(String table, SearchLogRecord bean) {
|
||||
return bean.rawTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTable(String table, Serializable primary) {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTable(String table, FilterNode node) {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,358 +1,446 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.boot;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.logging.Level;
|
||||
import javax.annotation.*;
|
||||
import static org.redkale.boot.Application.RESNAME_SNCP_ADDR;
|
||||
import org.redkale.boot.ClassFilter.FilterEntry;
|
||||
import org.redkale.net.*;
|
||||
import org.redkale.net.http.*;
|
||||
import org.redkale.net.sncp.Sncp;
|
||||
import org.redkale.service.*;
|
||||
import org.redkale.util.AnyValue.DefaultAnyValue;
|
||||
import org.redkale.util.*;
|
||||
import org.redkale.watch.*;
|
||||
|
||||
/**
|
||||
* HTTP Server节点的配置Server
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@NodeProtocol({"HTTP"})
|
||||
public class NodeHttpServer extends NodeServer {
|
||||
|
||||
protected final boolean rest; //是否加载REST服务, 为true加载rest节点信息并将所有可REST化的Service生成RestServlet
|
||||
|
||||
protected final HttpServer httpServer;
|
||||
|
||||
public NodeHttpServer(Application application, AnyValue serconf) {
|
||||
super(application, createServer(application, serconf));
|
||||
this.httpServer = (HttpServer) server;
|
||||
this.rest = serconf == null ? false : serconf.getAnyValue("rest") != null;
|
||||
}
|
||||
|
||||
private static Server createServer(Application application, AnyValue serconf) {
|
||||
return new HttpServer(application.getStartTime(), application.getResourceFactory().createChild());
|
||||
}
|
||||
|
||||
public HttpServer getHttpServer() {
|
||||
return httpServer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getSocketAddress() {
|
||||
return httpServer == null ? null : httpServer.getSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected ClassFilter<Service> createServiceClassFilter() {
|
||||
return createClassFilter(this.sncpGroup, null, Service.class, new Class[]{org.redkale.watch.WatchService.class}, Annotation.class, "services", "service");
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected ClassFilter<Filter> createFilterClassFilter() {
|
||||
return createClassFilter(null, null, HttpFilter.class, new Class[]{WatchFilter.class}, null, "filters", "filter");
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected ClassFilter<Servlet> createServletClassFilter() {
|
||||
return createClassFilter(null, WebServlet.class, HttpServlet.class, new Class[]{WatchServlet.class}, null, "servlets", "servlet");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClassFilter createOtherClassFilter() {
|
||||
return createClassFilter(null, RestWebSocket.class, WebSocket.class, null, null, "rest", "websocket");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadService(ClassFilter<? extends Service> serviceFilter, ClassFilter otherFilter) throws Exception {
|
||||
super.loadService(serviceFilter, otherFilter);
|
||||
initWebSocketService();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadFilter(ClassFilter<? extends Filter> filterFilter, ClassFilter otherFilter) throws Exception {
|
||||
if (httpServer != null) loadHttpFilter(this.serverConf.getAnyValue("filters"), filterFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void loadServlet(ClassFilter<? extends Servlet> servletFilter, ClassFilter otherFilter) throws Exception {
|
||||
if (httpServer != null) loadHttpServlet(servletFilter, otherFilter);
|
||||
}
|
||||
|
||||
private void initWebSocketService() {
|
||||
final NodeServer self = this;
|
||||
final ResourceFactory regFactory = application.getResourceFactory();
|
||||
resourceFactory.register((ResourceFactory rf, final Object src, final String resourceName, Field field, Object attachment) -> { //主要用于单点的服务
|
||||
try {
|
||||
if (field.getAnnotation(Resource.class) == null) return;
|
||||
if (!(src instanceof WebSocketServlet)) return;
|
||||
ResourceFactory.ResourceLoader loader = null;
|
||||
ResourceFactory sncpResFactory = null;
|
||||
for (NodeServer ns : application.servers) {
|
||||
if (!ns.isSNCP()) continue;
|
||||
sncpResFactory = ns.resourceFactory;
|
||||
loader = sncpResFactory.findLoader(WebSocketNode.class, field);
|
||||
if (loader != null) break;
|
||||
}
|
||||
if (loader != null) loader.load(sncpResFactory, src, resourceName, field, attachment);
|
||||
synchronized (regFactory) {
|
||||
Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class);
|
||||
if (sncpResFactory != null && resourceFactory.find(RESNAME_SNCP_ADDR, String.class) == null) {
|
||||
resourceFactory.register(RESNAME_SNCP_ADDR, InetSocketAddress.class, sncpResFactory.find(RESNAME_SNCP_ADDR, InetSocketAddress.class));
|
||||
resourceFactory.register(RESNAME_SNCP_ADDR, SocketAddress.class, sncpResFactory.find(RESNAME_SNCP_ADDR, SocketAddress.class));
|
||||
resourceFactory.register(RESNAME_SNCP_ADDR, String.class, sncpResFactory.find(RESNAME_SNCP_ADDR, String.class));
|
||||
}
|
||||
if (nodeService == null) {
|
||||
nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, application.getResourceFactory(), application.getSncpTransportFactory(), (InetSocketAddress) null, (Set<String>) null, (AnyValue) null);
|
||||
regFactory.register(resourceName, WebSocketNode.class, nodeService);
|
||||
}
|
||||
resourceFactory.inject(nodeService, self);
|
||||
field.set(src, nodeService);
|
||||
logger.fine("[" + Thread.currentThread().getName() + "] Load Service " + nodeService);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "WebSocketNode inject error", e);
|
||||
}
|
||||
}, WebSocketNode.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void loadHttpFilter(final AnyValue filtersConf, final ClassFilter<? extends Filter> classFilter) throws Exception {
|
||||
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
|
||||
final String threadName = "[" + Thread.currentThread().getName() + "] ";
|
||||
List<FilterEntry<? extends Filter>> list = new ArrayList(classFilter.getFilterEntrys());
|
||||
for (FilterEntry<? extends Filter> en : list) {
|
||||
Class<HttpFilter> clazz = (Class<HttpFilter>) en.getType();
|
||||
if (Modifier.isAbstract(clazz.getModifiers())) continue;
|
||||
final HttpFilter filter = clazz.getDeclaredConstructor().newInstance();
|
||||
resourceFactory.inject(filter, this);
|
||||
DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty();
|
||||
this.httpServer.addHttpFilter(filter, filterConf);
|
||||
if (sb != null) sb.append(threadName).append(" Load ").append(clazz.getName()).append(LINE_SEPARATOR);
|
||||
}
|
||||
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void loadHttpServlet(final ClassFilter<? extends Servlet> servletFilter, ClassFilter<? extends WebSocket> webSocketFilter) throws Exception {
|
||||
final AnyValue servletsConf = this.serverConf.getAnyValue("servlets");
|
||||
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
|
||||
String prefix0 = servletsConf == null ? "" : servletsConf.getValue("path", "");
|
||||
if (!prefix0.isEmpty() && prefix0.charAt(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1);
|
||||
if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0;
|
||||
final String prefix = prefix0;
|
||||
final String threadName = "[" + Thread.currentThread().getName() + "] ";
|
||||
List<FilterEntry<? extends Servlet>> list = new ArrayList(servletFilter.getFilterEntrys());
|
||||
list.sort((FilterEntry<? extends Servlet> o1, FilterEntry<? extends Servlet> o2) -> { //必须保证WebSocketServlet优先加载, 因为要确保其他的HttpServlet可以注入本地模式的WebSocketNode
|
||||
boolean ws1 = WebSocketServlet.class.isAssignableFrom(o1.getType());
|
||||
boolean ws2 = WebSocketServlet.class.isAssignableFrom(o2.getType());
|
||||
if (ws1 == ws2) {
|
||||
Priority p1 = o1.getType().getAnnotation(Priority.class);
|
||||
Priority p2 = o2.getType().getAnnotation(Priority.class);
|
||||
int v = (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value());
|
||||
return v == 0 ? o1.getType().getName().compareTo(o2.getType().getName()) : 0;
|
||||
}
|
||||
return ws1 ? -1 : 1;
|
||||
});
|
||||
final List<AbstractMap.SimpleEntry<String, String[]>> ss = sb == null ? null : new ArrayList<>();
|
||||
for (FilterEntry<? extends Servlet> en : list) {
|
||||
Class<HttpServlet> clazz = (Class<HttpServlet>) en.getType();
|
||||
if (Modifier.isAbstract(clazz.getModifiers())) continue;
|
||||
WebServlet ws = clazz.getAnnotation(WebServlet.class);
|
||||
if (ws == null || ws.value().length == 0) continue;
|
||||
final HttpServlet servlet = clazz.getDeclaredConstructor().newInstance();
|
||||
resourceFactory.inject(servlet, this);
|
||||
final String[] mappings = ws.value();
|
||||
String pref = ws.repair() ? prefix : "";
|
||||
DefaultAnyValue servletConf = (DefaultAnyValue) en.getProperty();
|
||||
this.httpServer.addHttpServlet(servlet, pref, servletConf, mappings);
|
||||
if (ss != null) {
|
||||
for (int i = 0; i < mappings.length; i++) {
|
||||
mappings[i] = pref + mappings[i];
|
||||
}
|
||||
ss.add(new AbstractMap.SimpleEntry<>(clazz.getName(), mappings));
|
||||
}
|
||||
}
|
||||
int max = 0;
|
||||
if (ss != null && sb != null) {
|
||||
ss.sort((AbstractMap.SimpleEntry<String, String[]> o1, AbstractMap.SimpleEntry<String, String[]> o2) -> o1.getKey().compareTo(o2.getKey()));
|
||||
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
|
||||
if (as.getKey().length() > max) max = as.getKey().length();
|
||||
}
|
||||
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
|
||||
sb.append(threadName).append(" Load ").append(as.getKey());
|
||||
for (int i = 0; i < max - as.getKey().length(); i++) {
|
||||
sb.append(' ');
|
||||
}
|
||||
sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR);
|
||||
}
|
||||
}
|
||||
if (rest && serverConf != null) {
|
||||
final List<Object> restedObjects = new ArrayList<>();
|
||||
for (AnyValue restConf : serverConf.getAnyValues("rest")) {
|
||||
loadRestServlet(webSocketFilter, restConf, restedObjects, sb);
|
||||
}
|
||||
}
|
||||
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString().trim());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void loadRestServlet(final ClassFilter<? extends WebSocket> webSocketFilter, final AnyValue restConf, final List<Object> restedObjects, final StringBuilder sb) throws Exception {
|
||||
if (!rest) return;
|
||||
if (restConf == null) return; //不存在REST服务
|
||||
|
||||
final long starts = System.currentTimeMillis();
|
||||
String prefix0 = restConf.getValue("path", "");
|
||||
if (!prefix0.isEmpty() && prefix0.charAt(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1);
|
||||
if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0;
|
||||
final String prefix = prefix0;
|
||||
|
||||
final String threadName = "[" + Thread.currentThread().getName() + "] ";
|
||||
final List<AbstractMap.SimpleEntry<String, String[]>> ss = sb == null ? null : new ArrayList<>();
|
||||
|
||||
final boolean autoload = restConf.getBoolValue("autoload", true);
|
||||
{ //加载RestService
|
||||
String userTypeStr = restConf.getValue("usertype");
|
||||
final Class userType = userTypeStr == null ? null : this.serverClassLoader.loadClass(userTypeStr);
|
||||
|
||||
final Class baseServletType = this.serverClassLoader.loadClass(restConf.getValue("base", HttpServlet.class.getName()));
|
||||
final Set<String> includeValues = new HashSet<>();
|
||||
final Set<String> excludeValues = new HashSet<>();
|
||||
for (AnyValue item : restConf.getAnyValues("service")) {
|
||||
if (item.getBoolValue("ignore", false)) {
|
||||
excludeValues.add(item.getValue("value", ""));
|
||||
} else {
|
||||
includeValues.add(item.getValue("value", ""));
|
||||
}
|
||||
}
|
||||
|
||||
final ClassFilter restFilter = ClassFilter.create(null, restConf.getValue("includes", ""), restConf.getValue("excludes", ""), includeValues, excludeValues);
|
||||
final boolean finest = logger.isLoggable(Level.FINEST);
|
||||
final CountDownLatch scdl = new CountDownLatch(super.interceptorServices.size());
|
||||
super.interceptorServices.stream().parallel().forEach((service) -> {
|
||||
try {
|
||||
final Class stype = Sncp.getServiceType(service);
|
||||
final String name = Sncp.getResourceName(service);
|
||||
RestService rs = (RestService) stype.getAnnotation(RestService.class);
|
||||
if (rs == null || rs.ignore()) return;
|
||||
|
||||
final String stypename = stype.getName();
|
||||
if (!autoload && !includeValues.contains(stypename)) return;
|
||||
if (!restFilter.accept(stypename)) return;
|
||||
synchronized (restedObjects) {
|
||||
if (restedObjects.contains(service)) {
|
||||
logger.log(Level.WARNING, stype.getName() + " repeat create rest servlet, so ignore");
|
||||
return;
|
||||
}
|
||||
restedObjects.add(service); //避免重复创建Rest对象
|
||||
}
|
||||
HttpServlet servlet = httpServer.addRestServlet(serverClassLoader, service, userType, baseServletType, prefix);
|
||||
if (servlet == null) return; //没有HttpMapping方法的HttpServlet调用Rest.createRestServlet就会返回null
|
||||
String prefix2 = prefix;
|
||||
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
|
||||
if (ws != null && !ws.repair()) prefix2 = "";
|
||||
resourceFactory.inject(servlet, NodeHttpServer.this);
|
||||
//if (finest) logger.finest(threadName + " Create RestServlet(resource.name='" + name + "') = " + servlet);
|
||||
if (ss != null) {
|
||||
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
|
||||
for (int i = 0; i < mappings.length; i++) {
|
||||
mappings[i] = prefix2 + mappings[i];
|
||||
}
|
||||
synchronized (ss) {
|
||||
ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName() + "(rest.name='" + name + "')", mappings));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
scdl.countDown();
|
||||
}
|
||||
});
|
||||
scdl.await();
|
||||
}
|
||||
if (webSocketFilter != null) { //加载RestWebSocket
|
||||
final Set<String> includeValues = new HashSet<>();
|
||||
final Set<String> excludeValues = new HashSet<>();
|
||||
for (AnyValue item : restConf.getAnyValues("websocket")) {
|
||||
if (item.getBoolValue("ignore", false)) {
|
||||
excludeValues.add(item.getValue("value", ""));
|
||||
} else {
|
||||
includeValues.add(item.getValue("value", ""));
|
||||
}
|
||||
}
|
||||
|
||||
final ClassFilter restFilter = ClassFilter.create(null, restConf.getValue("includes", ""), restConf.getValue("excludes", ""), includeValues, excludeValues);
|
||||
final boolean finest = logger.isLoggable(Level.FINEST);
|
||||
|
||||
List<FilterEntry<? extends WebSocket>> list = new ArrayList(webSocketFilter.getFilterEntrys());
|
||||
for (FilterEntry<? extends WebSocket> en : list) {
|
||||
Class<WebSocket> clazz = (Class<WebSocket>) en.getType();
|
||||
if (Modifier.isAbstract(clazz.getModifiers())) {
|
||||
logger.log(Level.FINE, clazz.getName() + " cannot abstract on rest websocket, so ignore");
|
||||
continue;
|
||||
}
|
||||
if (Modifier.isFinal(clazz.getModifiers())) {
|
||||
logger.log(Level.FINE, clazz.getName() + " cannot final on rest websocket, so ignore");
|
||||
continue;
|
||||
}
|
||||
final Class<? extends WebSocket> stype = en.getType();
|
||||
RestWebSocket rs = stype.getAnnotation(RestWebSocket.class);
|
||||
if (rs == null || rs.ignore()) return;
|
||||
|
||||
final String stypename = stype.getName();
|
||||
if (!autoload && !includeValues.contains(stypename)) return;
|
||||
if (!restFilter.accept(stypename)) return;
|
||||
if (restedObjects.contains(stype)) {
|
||||
logger.log(Level.WARNING, stype.getName() + " repeat create rest websocket, so ignore");
|
||||
return;
|
||||
}
|
||||
restedObjects.add(stype); //避免重复创建Rest对象
|
||||
HttpServlet servlet = httpServer.addRestWebSocketServlet(serverClassLoader, stype, prefix, en.getProperty());
|
||||
if (servlet == null) return; //没有RestOnMessage方法的HttpServlet调用Rest.createRestWebSocketServlet就会返回null
|
||||
String prefix2 = prefix;
|
||||
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
|
||||
if (ws != null && !ws.repair()) prefix2 = "";
|
||||
resourceFactory.inject(servlet, NodeHttpServer.this);
|
||||
if (finest) logger.finest(threadName + " " + stype.getName() + " create a RestWebSocketServlet");
|
||||
if (ss != null) {
|
||||
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
|
||||
for (int i = 0; i < mappings.length; i++) {
|
||||
mappings[i] = prefix2 + mappings[i];
|
||||
}
|
||||
ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName(), mappings));
|
||||
}
|
||||
}
|
||||
}
|
||||
//输出信息
|
||||
if (ss != null && !ss.isEmpty() && sb != null) {
|
||||
ss.sort((AbstractMap.SimpleEntry<String, String[]> o1, AbstractMap.SimpleEntry<String, String[]> o2) -> o1.getKey().compareTo(o2.getKey()));
|
||||
int max = 0;
|
||||
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
|
||||
if (as.getKey().length() > max) max = as.getKey().length();
|
||||
}
|
||||
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
|
||||
sb.append(threadName).append(" Load ").append(as.getKey());
|
||||
for (int i = 0; i < max - as.getKey().length(); i++) {
|
||||
sb.append(' ');
|
||||
}
|
||||
sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR);
|
||||
}
|
||||
sb.append(threadName).append(" All HttpServlets load cost " + (System.currentTimeMillis() - starts) + " ms" + LINE_SEPARATOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.boot;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Stream;
|
||||
import javax.annotation.*;
|
||||
import static org.redkale.boot.Application.RESNAME_SNCP_ADDR;
|
||||
import org.redkale.boot.ClassFilter.FilterEntry;
|
||||
import org.redkale.cluster.ClusterAgent;
|
||||
import org.redkale.mq.MessageAgent;
|
||||
import org.redkale.net.*;
|
||||
import org.redkale.net.http.*;
|
||||
import org.redkale.net.sncp.Sncp;
|
||||
import org.redkale.service.*;
|
||||
import org.redkale.util.AnyValue.DefaultAnyValue;
|
||||
import org.redkale.util.*;
|
||||
import org.redkale.watch.*;
|
||||
|
||||
/**
|
||||
* HTTP Server节点的配置Server
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@NodeProtocol("HTTP")
|
||||
public class NodeHttpServer extends NodeServer {
|
||||
|
||||
protected final boolean rest; //是否加载REST服务, 为true加载rest节点信息并将所有可REST化的Service生成RestServlet
|
||||
|
||||
protected final HttpServer httpServer;
|
||||
|
||||
public NodeHttpServer(Application application, AnyValue serconf) {
|
||||
super(application, createServer(application, serconf));
|
||||
this.httpServer = (HttpServer) server;
|
||||
this.rest = serconf == null ? false : serconf.getAnyValue("rest") != null;
|
||||
}
|
||||
|
||||
private static Server createServer(Application application, AnyValue serconf) {
|
||||
return new HttpServer(application, application.getStartTime(), application.getResourceFactory().createChild());
|
||||
}
|
||||
|
||||
public HttpServer getHttpServer() {
|
||||
return httpServer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getSocketAddress() {
|
||||
return httpServer == null ? null : httpServer.getSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected ClassFilter<Service> createServiceClassFilter() {
|
||||
return createClassFilter(this.sncpGroup, null, Service.class, new Class[]{org.redkale.watch.WatchService.class}, Annotation.class, "services", "service");
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected ClassFilter<Filter> createFilterClassFilter() {
|
||||
return createClassFilter(null, null, HttpFilter.class, new Class[]{WatchFilter.class}, null, "filters", "filter");
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected ClassFilter<Servlet> createServletClassFilter() {
|
||||
return createClassFilter(null, WebServlet.class, HttpServlet.class, new Class[]{WatchServlet.class}, null, "servlets", "servlet");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClassFilter createOtherClassFilter() {
|
||||
return createClassFilter(null, RestWebSocket.class, WebSocket.class, null, null, "rest", "websocket");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadService(ClassFilter<? extends Service> serviceFilter, ClassFilter otherFilter) throws Exception {
|
||||
super.loadService(serviceFilter, otherFilter);
|
||||
initWebSocketService();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadFilter(ClassFilter<? extends Filter> filterFilter, ClassFilter otherFilter) throws Exception {
|
||||
if (httpServer != null) loadHttpFilter(filterFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void loadServlet(ClassFilter<? extends Servlet> servletFilter, ClassFilter otherFilter) throws Exception {
|
||||
if (httpServer != null) loadHttpServlet(servletFilter, otherFilter);
|
||||
}
|
||||
|
||||
private void initWebSocketService() {
|
||||
final NodeServer self = this;
|
||||
final ResourceFactory regFactory = application.getResourceFactory();
|
||||
resourceFactory.register((ResourceFactory rf, String srcResourceName, final Object srcObj, final String resourceName, Field field, Object attachment) -> { //主要用于单点的服务
|
||||
try {
|
||||
if (field.getAnnotation(Resource.class) == null) return;
|
||||
if (!(srcObj instanceof WebSocketServlet)) return;
|
||||
ResourceTypeLoader loader = null;
|
||||
ResourceFactory sncpResFactory = null;
|
||||
for (NodeServer ns : application.servers) {
|
||||
if (!ns.isSNCP()) continue;
|
||||
sncpResFactory = ns.resourceFactory;
|
||||
loader = sncpResFactory.findTypeLoader(WebSocketNode.class, field);
|
||||
if (loader != null) break;
|
||||
}
|
||||
if (loader != null) loader.load(sncpResFactory, srcResourceName, srcObj, resourceName, field, attachment);
|
||||
synchronized (regFactory) {
|
||||
Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class);
|
||||
if (sncpResFactory != null && resourceFactory.find(RESNAME_SNCP_ADDR, String.class) == null) {
|
||||
resourceFactory.register(RESNAME_SNCP_ADDR, InetSocketAddress.class, sncpResFactory.find(RESNAME_SNCP_ADDR, InetSocketAddress.class));
|
||||
resourceFactory.register(RESNAME_SNCP_ADDR, SocketAddress.class, sncpResFactory.find(RESNAME_SNCP_ADDR, SocketAddress.class));
|
||||
resourceFactory.register(RESNAME_SNCP_ADDR, String.class, sncpResFactory.find(RESNAME_SNCP_ADDR, String.class));
|
||||
}
|
||||
if (nodeService == null) {
|
||||
MessageAgent messageAgent = null;
|
||||
try {
|
||||
Field c = WebSocketServlet.class.getDeclaredField("messageAgent");
|
||||
c.setAccessible(true);
|
||||
messageAgent = (MessageAgent) c.get(srcObj);
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.WARNING, "WebSocketServlet getMessageAgent error", ex);
|
||||
}
|
||||
nodeService = Sncp.createLocalService(serverClassLoader, resourceName, org.redkale.net.http.WebSocketNodeService.class, messageAgent, application.getResourceFactory(), application.getSncpTransportFactory(), (InetSocketAddress) null, (Set<String>) null, (AnyValue) null);
|
||||
regFactory.register(resourceName, WebSocketNode.class, nodeService);
|
||||
}
|
||||
resourceFactory.inject(resourceName, nodeService, self);
|
||||
field.set(srcObj, nodeService);
|
||||
logger.fine("[" + Thread.currentThread().getName() + "] Load Service " + nodeService);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "WebSocketNode inject error", e);
|
||||
}
|
||||
}, WebSocketNode.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void loadHttpFilter(final ClassFilter<? extends Filter> classFilter) throws Exception {
|
||||
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
|
||||
final String localThreadName = "[" + Thread.currentThread().getName() + "] ";
|
||||
List<FilterEntry<? extends Filter>> list = new ArrayList(classFilter.getFilterEntrys());
|
||||
for (FilterEntry<? extends Filter> en : list) {
|
||||
Class<HttpFilter> clazz = (Class<HttpFilter>) en.getType();
|
||||
if (Modifier.isAbstract(clazz.getModifiers())) continue;
|
||||
RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName());
|
||||
final HttpFilter filter = clazz.getDeclaredConstructor().newInstance();
|
||||
resourceFactory.inject(filter, this);
|
||||
DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty();
|
||||
this.httpServer.addHttpFilter(filter, filterConf);
|
||||
if (sb != null) sb.append(localThreadName).append(" Load ").append(clazz.getName()).append(LINE_SEPARATOR);
|
||||
}
|
||||
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void loadHttpServlet(final ClassFilter<? extends Servlet> servletFilter, ClassFilter<? extends WebSocket> webSocketFilter) throws Exception {
|
||||
RedkaleClassLoader.putReflectionPublicClasses(HttpServlet.class.getName());
|
||||
RedkaleClassLoader.putReflectionPublicClasses(HttpDispatcherServlet.class.getName());
|
||||
RedkaleClassLoader.putReflectionDeclaredConstructors(HttpResourceServlet.class, HttpResourceServlet.class.getName());
|
||||
final AnyValue servletsConf = this.serverConf.getAnyValue("servlets");
|
||||
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
|
||||
String prefix0 = servletsConf == null ? "" : servletsConf.getValue("path", "");
|
||||
if (!prefix0.isEmpty() && prefix0.charAt(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1);
|
||||
if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0;
|
||||
final String prefix = prefix0;
|
||||
final String localThreadName = "[" + Thread.currentThread().getName() + "] ";
|
||||
List<FilterEntry<? extends Servlet>> list = new ArrayList(servletFilter.getFilterEntrys());
|
||||
list.sort((FilterEntry<? extends Servlet> o1, FilterEntry<? extends Servlet> o2) -> { //必须保证WebSocketServlet优先加载, 因为要确保其他的HttpServlet可以注入本地模式的WebSocketNode
|
||||
boolean ws1 = WebSocketServlet.class.isAssignableFrom(o1.getType());
|
||||
boolean ws2 = WebSocketServlet.class.isAssignableFrom(o2.getType());
|
||||
if (ws1 == ws2) {
|
||||
Priority p1 = o1.getType().getAnnotation(Priority.class);
|
||||
Priority p2 = o2.getType().getAnnotation(Priority.class);
|
||||
int v = (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value());
|
||||
return v == 0 ? o1.getType().getName().compareTo(o2.getType().getName()) : 0;
|
||||
}
|
||||
return ws1 ? -1 : 1;
|
||||
});
|
||||
final long starts = System.currentTimeMillis();
|
||||
final List<AbstractMap.SimpleEntry<String, String[]>> ss = sb == null ? null : new ArrayList<>();
|
||||
for (FilterEntry<? extends Servlet> en : list) {
|
||||
Class<HttpServlet> clazz = (Class<HttpServlet>) en.getType();
|
||||
if (Modifier.isAbstract(clazz.getModifiers())) continue;
|
||||
if (clazz.getAnnotation(Rest.RestDyn.class) != null) continue; //动态生成的跳过
|
||||
WebServlet ws = clazz.getAnnotation(WebServlet.class);
|
||||
if (ws == null) continue;
|
||||
if (ws.value().length == 0) {
|
||||
logger.log(Level.INFO, "not found @WebServlet.value in " + clazz.getName());
|
||||
continue;
|
||||
}
|
||||
RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName());
|
||||
final HttpServlet servlet = clazz.getDeclaredConstructor().newInstance();
|
||||
resourceFactory.inject(servlet, this);
|
||||
final String[] mappings = ws.value();
|
||||
String pref = ws.repair() ? prefix : "";
|
||||
DefaultAnyValue servletConf = (DefaultAnyValue) en.getProperty();
|
||||
this.httpServer.addHttpServlet(servlet, pref, servletConf, mappings);
|
||||
if (ss != null) {
|
||||
for (int i = 0; i < mappings.length; i++) {
|
||||
mappings[i] = pref + mappings[i];
|
||||
}
|
||||
ss.add(new AbstractMap.SimpleEntry<>("HttpServlet (type=" + clazz.getName() + ")", mappings));
|
||||
}
|
||||
}
|
||||
final List<AbstractMap.SimpleEntry<String, String[]>> rests = sb == null ? null : new ArrayList<>();
|
||||
final List<AbstractMap.SimpleEntry<String, String[]>> webss = sb == null ? null : new ArrayList<>();
|
||||
if (rest && serverConf != null) {
|
||||
final List<Object> restedObjects = new ArrayList<>();
|
||||
for (AnyValue restConf : serverConf.getAnyValues("rest")) {
|
||||
loadRestServlet(webSocketFilter, restConf, restedObjects, sb, rests, webss);
|
||||
}
|
||||
}
|
||||
int max = 0;
|
||||
if (ss != null && sb != null) {
|
||||
int maxTypeLength = 0;
|
||||
int maxNameLength = 0;
|
||||
if (rests != null) {
|
||||
for (AbstractMap.SimpleEntry<String, String[]> en : rests) {
|
||||
int pos = en.getKey().indexOf('#');
|
||||
if (pos > maxTypeLength) maxTypeLength = pos;
|
||||
int len = en.getKey().length() - pos - 1;
|
||||
if (len > maxNameLength) maxNameLength = len;
|
||||
}
|
||||
}
|
||||
if (webss != null) {
|
||||
for (AbstractMap.SimpleEntry<String, String[]> en : webss) {
|
||||
int pos = en.getKey().indexOf('#');
|
||||
if (pos > maxTypeLength) maxTypeLength = pos;
|
||||
int len = en.getKey().length() - pos - 1;
|
||||
if (len > maxNameLength) maxNameLength = len;
|
||||
}
|
||||
}
|
||||
if (rests != null) {
|
||||
for (AbstractMap.SimpleEntry<String, String[]> en : rests) {
|
||||
StringBuilder sub = new StringBuilder();
|
||||
int pos = en.getKey().indexOf('#');
|
||||
sub.append("RestDynServlet (type=").append(en.getKey().substring(0, pos));
|
||||
for (int i = 0; i < maxTypeLength - pos; i++) {
|
||||
sub.append(' ');
|
||||
}
|
||||
sub.append(", name='").append(en.getKey().substring(pos + 1));
|
||||
for (int i = 0; i < maxNameLength - pos; i++) {
|
||||
sub.append(' ');
|
||||
}
|
||||
sub.append("')");
|
||||
ss.add(new AbstractMap.SimpleEntry<>(sub.toString(), en.getValue()));
|
||||
}
|
||||
}
|
||||
if (webss != null) {
|
||||
for (AbstractMap.SimpleEntry<String, String[]> en : webss) {
|
||||
StringBuilder sub = new StringBuilder();
|
||||
int pos = en.getKey().indexOf('#');
|
||||
sub.append("RestWebSocket (type=").append(en.getKey().substring(0, pos));
|
||||
for (int i = 0; i < maxTypeLength - pos; i++) {
|
||||
sub.append(' ');
|
||||
}
|
||||
sub.append(", name='").append(en.getKey().substring(pos + 1));
|
||||
for (int i = 0; i < maxNameLength - pos; i++) {
|
||||
sub.append(' ');
|
||||
}
|
||||
sub.append("')");
|
||||
ss.add(new AbstractMap.SimpleEntry<>(sub.toString(), en.getValue()));
|
||||
}
|
||||
}
|
||||
ss.sort((AbstractMap.SimpleEntry<String, String[]> o1, AbstractMap.SimpleEntry<String, String[]> o2) -> o1.getKey().compareTo(o2.getKey()));
|
||||
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
|
||||
if (as.getKey().length() > max) max = as.getKey().length();
|
||||
}
|
||||
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
|
||||
sb.append(localThreadName).append("Load ").append(as.getKey());
|
||||
for (int i = 0; i < max - as.getKey().length(); i++) {
|
||||
sb.append(' ');
|
||||
}
|
||||
sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR);
|
||||
}
|
||||
sb.append(localThreadName).append("All HttpServlets load cost ").append(System.currentTimeMillis() - starts).append(" ms").append(LINE_SEPARATOR);
|
||||
}
|
||||
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString().trim());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void loadRestServlet(final ClassFilter<? extends WebSocket> webSocketFilter, final AnyValue restConf, final List<Object> restedObjects, final StringBuilder sb,
|
||||
final List<AbstractMap.SimpleEntry<String, String[]>> rests, final List<AbstractMap.SimpleEntry<String, String[]>> webss) throws Exception {
|
||||
if (!rest) return;
|
||||
if (restConf == null) return; //不存在REST服务
|
||||
|
||||
String prefix0 = restConf.getValue("path", "");
|
||||
if (!prefix0.isEmpty() && prefix0.charAt(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1);
|
||||
if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0;
|
||||
|
||||
final String localThreadName = "[" + Thread.currentThread().getName() + "] ";
|
||||
String mqname = restConf.getValue("mq");
|
||||
MessageAgent agent0 = null;
|
||||
if (mqname != null) {
|
||||
agent0 = application.getMessageAgent(mqname);
|
||||
if (agent0 == null) throw new RuntimeException("not found " + MessageAgent.class.getSimpleName() + " config for (name=" + mqname + ")");
|
||||
}
|
||||
final MessageAgent messageAgent = agent0;
|
||||
if (messageAgent != null) prefix0 = ""; //开启MQ时,prefix字段失效
|
||||
final String prefix = prefix0;
|
||||
final boolean autoload = restConf.getBoolValue("autoload", true);
|
||||
{ //加载RestService
|
||||
String userTypeStr = restConf.getValue("usertype");
|
||||
final Class userType = userTypeStr == null ? null : this.serverClassLoader.loadClass(userTypeStr);
|
||||
|
||||
final Class baseServletType = this.serverClassLoader.loadClass(restConf.getValue("base", HttpServlet.class.getName()));
|
||||
final Set<String> includeValues = new HashSet<>();
|
||||
final Set<String> excludeValues = new HashSet<>();
|
||||
for (AnyValue item : restConf.getAnyValues("service")) {
|
||||
if (item.getBoolValue("ignore", false)) {
|
||||
excludeValues.add(item.getValue("value", ""));
|
||||
} else {
|
||||
includeValues.add(item.getValue("value", ""));
|
||||
}
|
||||
}
|
||||
|
||||
final ClassFilter restFilter = ClassFilter.create(serverClassLoader, null, application.isCompileMode() ? "" : restConf.getValue("includes", ""), application.isCompileMode() ? "" : restConf.getValue("excludes", ""), includeValues, excludeValues);
|
||||
final CountDownLatch scdl = new CountDownLatch(super.interceptorServices.size());
|
||||
Stream<Service> stream = super.interceptorServices.stream();
|
||||
if (!application.isCompileMode()) stream = stream.parallel(); //不能并行,否则在maven plugin运行环境下ClassLoader不对
|
||||
stream.forEach((service) -> {
|
||||
try {
|
||||
final Class stype = Sncp.getServiceType(service);
|
||||
final String name = Sncp.getResourceName(service);
|
||||
RestService rs = (RestService) stype.getAnnotation(RestService.class);
|
||||
if (rs == null || rs.ignore()) return;
|
||||
|
||||
final String stypename = stype.getName();
|
||||
if (!autoload && !includeValues.contains(stypename)) return;
|
||||
if (!restFilter.accept(stypename)) return;
|
||||
synchronized (restedObjects) {
|
||||
if (restedObjects.contains(service)) {
|
||||
logger.log(Level.WARNING, stype.getName() + " repeat create rest servlet, so ignore");
|
||||
return;
|
||||
}
|
||||
restedObjects.add(service); //避免重复创建Rest对象
|
||||
}
|
||||
HttpServlet servlet = httpServer.addRestServlet(serverClassLoader, service, userType, baseServletType, prefix);
|
||||
if (servlet == null) return; //没有HttpMapping方法的HttpServlet调用Rest.createRestServlet就会返回null
|
||||
String prefix2 = prefix;
|
||||
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
|
||||
if (ws != null && !ws.repair()) prefix2 = "";
|
||||
resourceFactory.inject(servlet, NodeHttpServer.this);
|
||||
dynServletMap.put(service, servlet);
|
||||
if (messageAgent != null) messageAgent.putService(this, service, servlet);
|
||||
//if (finest) logger.finest(localThreadName + " Create RestServlet(resource.name='" + name + "') = " + servlet);
|
||||
if (rests != null) {
|
||||
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
|
||||
for (int i = 0; i < mappings.length; i++) {
|
||||
mappings[i] = prefix2 + mappings[i];
|
||||
}
|
||||
synchronized (rests) {
|
||||
rests.add(new AbstractMap.SimpleEntry<>(Sncp.getServiceType(service).getName() + "#" + name, mappings));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
scdl.countDown();
|
||||
}
|
||||
});
|
||||
scdl.await();
|
||||
}
|
||||
if (webSocketFilter != null) { //加载RestWebSocket
|
||||
final Set<String> includeValues = new HashSet<>();
|
||||
final Set<String> excludeValues = new HashSet<>();
|
||||
for (AnyValue item : restConf.getAnyValues("websocket")) {
|
||||
if (item.getBoolValue("ignore", false)) {
|
||||
excludeValues.add(item.getValue("value", ""));
|
||||
} else {
|
||||
includeValues.add(item.getValue("value", ""));
|
||||
}
|
||||
}
|
||||
final ClassFilter restFilter = ClassFilter.create(serverClassLoader, null, application.isCompileMode() ? "" : restConf.getValue("includes", ""), application.isCompileMode() ? "" : restConf.getValue("excludes", ""), includeValues, excludeValues);
|
||||
final boolean finest = logger.isLoggable(Level.FINEST);
|
||||
|
||||
List<FilterEntry<? extends WebSocket>> list = new ArrayList(webSocketFilter.getFilterEntrys());
|
||||
for (FilterEntry<? extends WebSocket> en : list) {
|
||||
Class<WebSocket> clazz = (Class<WebSocket>) en.getType();
|
||||
if (Modifier.isAbstract(clazz.getModifiers())) {
|
||||
logger.log(Level.FINE, clazz.getName() + " cannot abstract on rest websocket, so ignore");
|
||||
continue;
|
||||
}
|
||||
if (Modifier.isFinal(clazz.getModifiers())) {
|
||||
logger.log(Level.FINE, clazz.getName() + " cannot final on rest websocket, so ignore");
|
||||
continue;
|
||||
}
|
||||
final Class<? extends WebSocket> stype = en.getType();
|
||||
if (stype.getAnnotation(Rest.RestDyn.class) != null) continue;
|
||||
RestWebSocket rs = stype.getAnnotation(RestWebSocket.class);
|
||||
if (rs == null || rs.ignore()) continue;
|
||||
|
||||
final String stypename = stype.getName();
|
||||
if (!autoload && !includeValues.contains(stypename)) continue;
|
||||
if (!restFilter.accept(stypename)) continue;
|
||||
if (restedObjects.contains(stype)) {
|
||||
logger.log(Level.WARNING, stype.getName() + " repeat create rest websocket, so ignore");
|
||||
continue;
|
||||
}
|
||||
restedObjects.add(stype); //避免重复创建Rest对象
|
||||
WebSocketServlet servlet = httpServer.addRestWebSocketServlet(serverClassLoader, stype, messageAgent, prefix, en.getProperty());
|
||||
if (servlet == null) continue; //没有RestOnMessage方法的HttpServlet调用Rest.createRestWebSocketServlet就会返回null
|
||||
String prefix2 = prefix;
|
||||
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
|
||||
if (ws != null && !ws.repair()) prefix2 = "";
|
||||
resourceFactory.inject(servlet, NodeHttpServer.this);
|
||||
if (finest) logger.finest(localThreadName + " " + stype.getName() + " create a RestWebSocketServlet");
|
||||
if (webss != null) {
|
||||
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
|
||||
for (int i = 0; i < mappings.length; i++) {
|
||||
mappings[i] = prefix2 + mappings[i];
|
||||
}
|
||||
synchronized (webss) {
|
||||
webss.add(new AbstractMap.SimpleEntry<>(stype.getName() + "#" + rs.name(), mappings));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (messageAgent != null) this.messageAgents.put(messageAgent.getName(), messageAgent);
|
||||
}
|
||||
|
||||
@Override //loadServlet执行之后调用
|
||||
protected void postLoadServlets() {
|
||||
final ClusterAgent cluster = application.clusterAgent;
|
||||
if (!application.isCompileMode() && cluster != null) {
|
||||
NodeProtocol pros = getClass().getAnnotation(NodeProtocol.class);
|
||||
String protocol = pros.value().toUpperCase();
|
||||
if (!cluster.containsProtocol(protocol)) return;
|
||||
if (!cluster.containsPort(server.getSocketAddress().getPort())) return;
|
||||
cluster.register(this, protocol, dynServletMap.keySet(), new HashSet<>());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterClusterDeregisterOnPreDestroyServices(ClusterAgent cluster, String protocol) {
|
||||
cluster.deregister(this, protocol, dynServletMap.keySet(), new HashSet<>());
|
||||
}
|
||||
}
|
||||
@@ -20,5 +20,5 @@ import java.lang.annotation.*;
|
||||
@Documented
|
||||
public @interface NodeProtocol {
|
||||
|
||||
String[] value();
|
||||
String value();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,9 +10,10 @@ import java.net.*;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import org.redkale.boot.ClassFilter.FilterEntry;
|
||||
import org.redkale.mq.MessageAgent;
|
||||
import org.redkale.net.*;
|
||||
import org.redkale.net.sncp.*;
|
||||
import org.redkale.service.Service;
|
||||
import org.redkale.service.*;
|
||||
import org.redkale.util.*;
|
||||
import org.redkale.util.AnyValue.DefaultAnyValue;
|
||||
|
||||
@@ -24,7 +25,7 @@ import org.redkale.util.AnyValue.DefaultAnyValue;
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@NodeProtocol({"SNCP"})
|
||||
@NodeProtocol("SNCP")
|
||||
public class NodeSncpServer extends NodeServer {
|
||||
|
||||
protected final SncpServer sncpServer;
|
||||
@@ -32,19 +33,20 @@ public class NodeSncpServer extends NodeServer {
|
||||
private NodeSncpServer(Application application, AnyValue serconf) {
|
||||
super(application, createServer(application, serconf));
|
||||
this.sncpServer = (SncpServer) this.server;
|
||||
this.consumer = sncpServer == null || application.singletonrun ? null : x -> sncpServer.addSncpServlet(x); //singleton模式下不生成SncpServlet
|
||||
this.consumer = sncpServer == null || application.isSingletonMode() ? null : (agent, x) -> {//singleton模式下不生成SncpServlet
|
||||
if (x.getClass().getAnnotation(Local.class) != null) return; //本地模式的Service不生成SncpServlet
|
||||
SncpDynServlet servlet = sncpServer.addSncpServlet(x);
|
||||
dynServletMap.put(x, servlet);
|
||||
if (agent != null) agent.putService(this, x, servlet);
|
||||
};
|
||||
}
|
||||
|
||||
public static NodeServer createNodeServer(Application application, AnyValue serconf) {
|
||||
if (serconf != null && serconf.getAnyValue("rest") != null) {
|
||||
((AnyValue.DefaultAnyValue) serconf).addValue("_$sncp", "true");
|
||||
return new NodeHttpServer(application, serconf);
|
||||
}
|
||||
return new NodeSncpServer(application, serconf);
|
||||
}
|
||||
|
||||
private static Server createServer(Application application, AnyValue serconf) {
|
||||
return new SncpServer(application.getStartTime(), application.getResourceFactory().createChild());
|
||||
return new SncpServer(application, application.getStartTime(), serconf, application.getResourceFactory().createChild());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,8 +54,8 @@ public class NodeSncpServer extends NodeServer {
|
||||
return sncpServer == null ? null : sncpServer.getSocketAddress();
|
||||
}
|
||||
|
||||
public void consumerAccept(Service service) {
|
||||
if (this.consumer != null) this.consumer.accept(service);
|
||||
public void consumerAccept(MessageAgent messageAgent, Service service) {
|
||||
if (this.consumer != null) this.consumer.accept(messageAgent, service);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,11 +64,11 @@ public class NodeSncpServer extends NodeServer {
|
||||
//-------------------------------------------------------------------
|
||||
if (sncpServer == null) return; //调试时server才可能为null
|
||||
final StringBuilder sb = logger.isLoggable(Level.FINE) ? new StringBuilder() : null;
|
||||
final String threadName = "[" + Thread.currentThread().getName() + "] ";
|
||||
final String localThreadName = "[" + Thread.currentThread().getName() + "] ";
|
||||
List<SncpServlet> servlets = sncpServer.getSncpServlets();
|
||||
Collections.sort(servlets);
|
||||
for (SncpServlet en : servlets) {
|
||||
if (sb != null) sb.append(threadName).append(" Load ").append(en).append(LINE_SEPARATOR);
|
||||
if (sb != null) sb.append(localThreadName).append(" Load ").append(en).append(LINE_SEPARATOR);
|
||||
}
|
||||
if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString());
|
||||
}
|
||||
@@ -88,22 +90,25 @@ public class NodeSncpServer extends NodeServer {
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void loadSncpFilter(final AnyValue servletsConf, final ClassFilter<? extends Filter> classFilter) throws Exception {
|
||||
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
|
||||
final String threadName = "[" + Thread.currentThread().getName() + "] ";
|
||||
final String localThreadName = "[" + Thread.currentThread().getName() + "] ";
|
||||
List<FilterEntry<? extends Filter>> list = new ArrayList(classFilter.getFilterEntrys());
|
||||
for (FilterEntry<? extends Filter> en : list) {
|
||||
Class<SncpFilter> clazz = (Class<SncpFilter>) en.getType();
|
||||
if (Modifier.isAbstract(clazz.getModifiers())) continue;
|
||||
RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName());
|
||||
final SncpFilter filter = clazz.getDeclaredConstructor().newInstance();
|
||||
resourceFactory.inject(filter, this);
|
||||
DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty();
|
||||
this.sncpServer.addSncpFilter(filter, filterConf);
|
||||
if (sb != null) sb.append(threadName).append(" Load ").append(clazz.getName()).append(LINE_SEPARATOR);
|
||||
if (sb != null) sb.append(localThreadName).append(" Load ").append(clazz.getName()).append(LINE_SEPARATOR);
|
||||
}
|
||||
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadServlet(ClassFilter<? extends Servlet> servletFilter, ClassFilter otherFilter) throws Exception {
|
||||
RedkaleClassLoader.putReflectionPublicClasses(SncpServlet.class.getName());
|
||||
RedkaleClassLoader.putReflectionPublicClasses(SncpDynServlet.class.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -16,7 +16,7 @@ import org.redkale.watch.*;
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@NodeProtocol({"WATCH"})
|
||||
@NodeProtocol("WATCH")
|
||||
public class NodeWatchServer extends NodeHttpServer {
|
||||
|
||||
public NodeWatchServer(Application application, AnyValue serconf) {
|
||||
86
src/main/java/org/redkale/boot/PrepareCompiler.java
Normal file
86
src/main/java/org/redkale/boot/PrepareCompiler.java
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.boot;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import javax.persistence.Entity;
|
||||
import org.redkale.boot.ClassFilter.FilterEntry;
|
||||
import org.redkale.convert.Decodeable;
|
||||
import org.redkale.convert.bson.BsonFactory;
|
||||
import org.redkale.convert.json.*;
|
||||
import org.redkale.source.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* 执行一次Application.run提前获取所有动态类
|
||||
*
|
||||
* @author zhangjx
|
||||
* @since 2.5.0
|
||||
*/
|
||||
public class PrepareCompiler {
|
||||
|
||||
// public static void main(String[] args) throws Exception {
|
||||
// new PrepareCompiler().run();
|
||||
// }
|
||||
public Application run() throws Exception {
|
||||
final Application application = new Application(false, true, Application.loadAppConfig());
|
||||
application.init();
|
||||
for (ApplicationListener listener : application.listeners) {
|
||||
listener.preStart(application);
|
||||
}
|
||||
for (ApplicationListener listener : application.listeners) {
|
||||
listener.preCompile(application);
|
||||
}
|
||||
application.start();
|
||||
final boolean hasSncp = application.getNodeServers().stream().filter(v -> v instanceof NodeSncpServer).findFirst().isPresent();
|
||||
final String[] exlibs = (application.excludelibs != null ? (application.excludelibs + ";") : "").split(";");
|
||||
|
||||
final ClassFilter<?> entityFilter = new ClassFilter(application.getClassLoader(), Entity.class, Object.class, (Class[]) null);
|
||||
final ClassFilter<?> beanFilter = new ClassFilter(application.getClassLoader(), Bean.class, Object.class, (Class[]) null);
|
||||
final ClassFilter<?> filterFilter = new ClassFilter(application.getClassLoader(), null, FilterBean.class, (Class[]) null);
|
||||
|
||||
ClassFilter.Loader.load(application.getHome(), application.getClassLoader(), exlibs, entityFilter, beanFilter, filterFilter);
|
||||
|
||||
for (FilterEntry en : entityFilter.getFilterEntrys()) {
|
||||
Class clz = en.getType();
|
||||
if (clz.isInterface() || Modifier.isAbstract(clz.getModifiers())) continue;
|
||||
try {
|
||||
application.dataSources.forEach(source -> source.compile(clz));
|
||||
JsonFactory.root().loadEncoder(clz);
|
||||
if (hasSncp) BsonFactory.root().loadEncoder(clz);
|
||||
Decodeable decoder = JsonFactory.root().loadDecoder(clz);
|
||||
if (hasSncp) BsonFactory.root().loadDecoder(clz);
|
||||
decoder.convertFrom(new JsonReader("{}"));
|
||||
} catch (Exception e) { //JsonFactory.loadDecoder可能会失败,因为class可能包含抽象类字段,如ColumnValue.value字段
|
||||
}
|
||||
}
|
||||
for (FilterEntry en : beanFilter.getFilterEntrys()) {
|
||||
Class clz = en.getType();
|
||||
if (clz.isInterface() || Modifier.isAbstract(clz.getModifiers())) continue;
|
||||
try {
|
||||
JsonFactory.root().loadEncoder(clz);
|
||||
if (hasSncp) BsonFactory.root().loadEncoder(clz);
|
||||
Decodeable decoder = JsonFactory.root().loadDecoder(clz);
|
||||
if (hasSncp) BsonFactory.root().loadDecoder(clz);
|
||||
decoder.convertFrom(new JsonReader("{}"));
|
||||
} catch (Exception e) { //JsonFactory.loadDecoder可能会失败,因为class可能包含抽象类字段,如ColumnValue.value字段
|
||||
}
|
||||
}
|
||||
for (FilterEntry en : filterFilter.getFilterEntrys()) {
|
||||
Class clz = en.getType();
|
||||
if (clz.isInterface() || Modifier.isAbstract(clz.getModifiers())) continue;
|
||||
try {
|
||||
FilterNodeBean.load(clz);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
for (ApplicationListener listener : application.listeners) {
|
||||
listener.postCompile(application);
|
||||
}
|
||||
application.shutdown();
|
||||
return application;
|
||||
}
|
||||
}
|
||||
26
src/main/java/org/redkale/boot/PropertiesAgent.java
Normal file
26
src/main/java/org/redkale/boot/PropertiesAgent.java
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
*/
|
||||
package org.redkale.boot;
|
||||
|
||||
import java.util.Properties;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* 配置源Agent, 在init方法内需要实现读取配置信息,如果支持配置更改通知,也需要在init里实现监听
|
||||
*
|
||||
*
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*
|
||||
* @since 2.7.0
|
||||
*/
|
||||
public abstract class PropertiesAgent {
|
||||
|
||||
public void compile(AnyValue conf) {
|
||||
}
|
||||
|
||||
public abstract void init(ResourceFactory factory, Properties appProperties, AnyValue conf);
|
||||
|
||||
public abstract void destroy(AnyValue conf);
|
||||
}
|
||||
@@ -15,9 +15,15 @@ import org.redkale.watch.WatchService;
|
||||
*/
|
||||
public abstract class AbstractWatchService extends AbstractService implements WatchService {
|
||||
|
||||
/**
|
||||
* 缺少参数
|
||||
*/
|
||||
@Comment("缺少参数")
|
||||
public static final int RET_WATCH_PARAMS_ILLEGAL = 1600_0001;
|
||||
|
||||
/**
|
||||
* 执行异常
|
||||
*/
|
||||
@Comment("执行异常")
|
||||
public static final int RET_WATCH_RUN_EXCEPTION = 1600_0002;
|
||||
}
|
||||
@@ -61,7 +61,7 @@ public class ServerWatchService extends AbstractWatchService {
|
||||
final Server server = node.getServer();
|
||||
InetSocketAddress newAddr = new InetSocketAddress(newhost == null || newhost.isEmpty() ? server.getSocketAddress().getHostString() : newhost, newport);
|
||||
try {
|
||||
server.changeAddress(newAddr);
|
||||
server.changeAddress(application, newAddr);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return new RetResult(RET_SERVER_CHANGEPORT_ERROR, "changeaddress error");
|
||||
@@ -72,7 +72,7 @@ public class ServerWatchService extends AbstractWatchService {
|
||||
private Map<String, Object> formatToMap(NodeServer node) {
|
||||
Server server = node.getServer();
|
||||
Map<String, Object> rs = new LinkedHashMap<>();
|
||||
String protocol = server.getProtocol();
|
||||
String protocol = server.getNetprotocol();
|
||||
if (node instanceof NodeSncpServer) {
|
||||
protocol += "/SNCP";
|
||||
} else if (node instanceof NodeWatchServer) {
|
||||
@@ -81,12 +81,11 @@ public class ServerWatchService extends AbstractWatchService {
|
||||
protocol += "/HTTP";
|
||||
} else {
|
||||
NodeProtocol np = node.getClass().getAnnotation(NodeProtocol.class);
|
||||
if (np != null && np.value().length > 0) protocol += "/" + np.value()[0];
|
||||
protocol += "/" + np.value();
|
||||
}
|
||||
rs.put("name", server.getName());
|
||||
rs.put("protocol", protocol);
|
||||
rs.put("address", server.getSocketAddress());
|
||||
rs.put("threads", server.getThreads());
|
||||
rs.put("backlog", server.getBacklog());
|
||||
rs.put("bufferCapacity", server.getBufferCapacity());
|
||||
rs.put("bufferPoolSize", server.getBufferPoolSize());
|
||||
@@ -72,15 +72,6 @@ public class TransportWatchService extends AbstractWatchService {
|
||||
if (client.getRemoteGroups() != null && client.getRemoteGroups().contains(group)) {
|
||||
client.getRemoteGroupTransport().addRemoteAddresses(address);
|
||||
}
|
||||
} else {
|
||||
if (group.equals(client.getSameGroup())) {
|
||||
client.getSameGroupTransport().addRemoteAddresses(address);
|
||||
}
|
||||
if (client.getDiffGroups() != null && client.getDiffGroups().contains(group)) {
|
||||
for (Transport transport : client.getDiffGroupTransports()) {
|
||||
transport.addRemoteAddresses(address);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DefaultAnyValue node = DefaultAnyValue.create("addr", addr).addValue("port", port);
|
||||
@@ -90,7 +81,7 @@ public class TransportWatchService extends AbstractWatchService {
|
||||
break;
|
||||
}
|
||||
}
|
||||
application.restoreConfig();
|
||||
//application.restoreConfig();
|
||||
}
|
||||
return RetResult.success();
|
||||
}
|
||||
@@ -114,15 +105,6 @@ public class TransportWatchService extends AbstractWatchService {
|
||||
if (client.getRemoteGroups() != null && client.getRemoteGroups().contains(group)) {
|
||||
client.getRemoteGroupTransport().removeRemoteAddresses(address);
|
||||
}
|
||||
} else {
|
||||
if (group.equals(client.getSameGroup())) {
|
||||
client.getSameGroupTransport().removeRemoteAddresses(address);
|
||||
}
|
||||
if (client.getDiffGroups() != null && client.getDiffGroups().contains(group)) {
|
||||
for (Transport transport : client.getDiffGroupTransports()) {
|
||||
transport.removeRemoteAddresses(address);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (AnyValue groupconf : application.getAppConfig().getAnyValue("resources").getAnyValues("group")) {
|
||||
@@ -131,7 +113,7 @@ public class TransportWatchService extends AbstractWatchService {
|
||||
break;
|
||||
}
|
||||
}
|
||||
application.restoreConfig();
|
||||
//application.restoreConfig();
|
||||
}
|
||||
return RetResult.success();
|
||||
}
|
||||
315
src/main/java/org/redkale/cluster/CacheClusterAgent.java
Normal file
315
src/main/java/org/redkale/cluster/CacheClusterAgent.java
Normal file
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.cluster;
|
||||
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.logging.Level;
|
||||
import javax.annotation.Resource;
|
||||
import org.redkale.boot.*;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
import org.redkale.service.Service;
|
||||
import org.redkale.source.CacheSource;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* 使用CacheSource实现的第三方服务发现管理接口cluster
|
||||
*
|
||||
*
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public class CacheClusterAgent extends ClusterAgent implements Resourcable {
|
||||
|
||||
@Resource(name = "$")
|
||||
private CacheSource source;
|
||||
|
||||
private String sourceName;
|
||||
|
||||
protected int ttls = 10; //定时检查的秒数
|
||||
|
||||
protected ScheduledThreadPoolExecutor scheduler;
|
||||
|
||||
//可能被HttpMessageClient用到的服务 key: serviceName
|
||||
protected final ConcurrentHashMap<String, Collection<InetSocketAddress>> httpAddressMap = new ConcurrentHashMap<>();
|
||||
|
||||
//可能被mqtp用到的服务 key: serviceName
|
||||
protected final ConcurrentHashMap<String, Collection<InetSocketAddress>> mqtpAddressMap = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void init(ResourceFactory factory, AnyValue config) {
|
||||
super.init(factory, config);
|
||||
|
||||
this.sourceName = getSourceName();
|
||||
|
||||
this.ttls = config.getIntValue("ttls", 10);
|
||||
if (this.ttls < 5) this.ttls = 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfig(AnyValue config) {
|
||||
super.setConfig(config);
|
||||
this.sourceName = getSourceName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy(AnyValue config) {
|
||||
if (scheduler != null) scheduler.shutdownNow();
|
||||
}
|
||||
|
||||
public String getSourceName() {
|
||||
return config.getValue("source");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String resourceName() {
|
||||
return sourceName;
|
||||
}
|
||||
|
||||
@Override //ServiceLoader时判断配置是否符合当前实现类
|
||||
public boolean acceptsConf(AnyValue config) {
|
||||
if (config == null) return false;
|
||||
return config.getValue("source") != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (this.scheduler == null) {
|
||||
this.scheduler = new ScheduledThreadPoolExecutor(4, (Runnable r) -> {
|
||||
final Thread t = new Thread(r, "Redkale-" + CacheClusterAgent.class.getSimpleName() + "-Task-Thread");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
|
||||
this.scheduler.scheduleAtFixedRate(() -> {
|
||||
try {
|
||||
checkApplicationHealth();
|
||||
checkHttpAddressHealth();
|
||||
loadMqtpAddressHealth();
|
||||
localEntrys.values().stream().filter(e -> !e.canceled).forEach(entry -> {
|
||||
checkLocalHealth(entry);
|
||||
});
|
||||
remoteEntrys.values().stream().filter(entry -> "SNCP".equalsIgnoreCase(entry.protocol)).forEach(entry -> {
|
||||
updateSncpTransport(entry);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "scheduleAtFixedRate check error", e instanceof CompletionException ? ((CompletionException) e).getCause() : e);
|
||||
}
|
||||
}, Math.max(2000, ttls * 1000), Math.max(2000, ttls * 1000), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
protected void loadMqtpAddressHealth() {
|
||||
List<String> keys = source.queryKeysStartsWith("cluster.mqtp:");
|
||||
keys.forEach(serviceName -> {
|
||||
try {
|
||||
this.mqtpAddressMap.put(serviceName, queryAddress(serviceName).get(3, TimeUnit.SECONDS));
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "loadMqtpAddressHealth check " + serviceName + " error", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void checkHttpAddressHealth() {
|
||||
try {
|
||||
this.httpAddressMap.keySet().stream().forEach(serviceName -> {
|
||||
try {
|
||||
this.httpAddressMap.put(serviceName, queryAddress(serviceName).get(3, TimeUnit.SECONDS));
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "checkHttpAddressHealth check " + serviceName + " error", e);
|
||||
}
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "checkHttpAddressHealth check error", ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkLocalHealth(final ClusterEntry entry) {
|
||||
AddressEntry newaddr = new AddressEntry();
|
||||
newaddr.addr = entry.address;
|
||||
newaddr.nodeid = this.nodeid;
|
||||
newaddr.time = System.currentTimeMillis();
|
||||
source.hset(entry.checkName, entry.checkid, AddressEntry.class, newaddr);
|
||||
}
|
||||
|
||||
@Override //获取MQTP的HTTP远程服务的可用ip列表, key = serviceName的后半段
|
||||
public CompletableFuture<Map<String, Collection<InetSocketAddress>>> queryMqtpAddress(String protocol, String module, String resname) {
|
||||
final Map<String, Collection<InetSocketAddress>> rsmap = new ConcurrentHashMap<>();
|
||||
final String servicenamprefix = generateHttpServiceName(protocol, module, null) + ":";
|
||||
mqtpAddressMap.keySet().stream().filter(k -> k.startsWith(servicenamprefix))
|
||||
.forEach(sn -> rsmap.put(sn.substring(servicenamprefix.length()), mqtpAddressMap.get(sn)));
|
||||
return CompletableFuture.completedFuture(rsmap);
|
||||
}
|
||||
|
||||
@Override //获取HTTP远程服务的可用ip列表
|
||||
public CompletableFuture<Collection<InetSocketAddress>> queryHttpAddress(String protocol, String module, String resname) {
|
||||
final String serviceName = generateHttpServiceName(protocol, module, resname);
|
||||
Collection<InetSocketAddress> rs = httpAddressMap.get(serviceName);
|
||||
if (rs != null) return CompletableFuture.completedFuture(rs);
|
||||
return queryAddress(serviceName).thenApply(t -> {
|
||||
httpAddressMap.put(serviceName, t);
|
||||
return t;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Collection<InetSocketAddress>> queryAddress(final ClusterEntry entry) {
|
||||
return queryAddress(entry.serviceName);
|
||||
}
|
||||
|
||||
private CompletableFuture<Collection<InetSocketAddress>> queryAddress(final String serviceName) {
|
||||
final CompletableFuture<Map<String, AddressEntry>> future = source.hmapAsync(serviceName, AddressEntry.class, 0, 10000);
|
||||
return future.thenApply(map -> {
|
||||
final Set<InetSocketAddress> set = new HashSet<>();
|
||||
map.forEach((n, v) -> {
|
||||
if (v != null && (System.currentTimeMillis() - v.time) / 1000 < ttls) set.add(v.addr);
|
||||
});
|
||||
return set;
|
||||
});
|
||||
}
|
||||
|
||||
protected boolean isApplicationHealth() {
|
||||
String serviceName = generateApplicationServiceName();
|
||||
String serviceid = generateApplicationServiceId();
|
||||
AddressEntry entry = (AddressEntry) source.hget(serviceName, serviceid, AddressEntry.class);
|
||||
return entry != null && (System.currentTimeMillis() - entry.time) / 1000 < ttls;
|
||||
}
|
||||
|
||||
protected void checkApplicationHealth() {
|
||||
String checkname = generateApplicationServiceName();
|
||||
String checkid = generateApplicationCheckId();
|
||||
AddressEntry entry = new AddressEntry();
|
||||
entry.addr = this.appAddress;
|
||||
entry.nodeid = this.nodeid;
|
||||
entry.time = System.currentTimeMillis();
|
||||
source.hset(checkname, checkid, AddressEntry.class, entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(Application application) {
|
||||
if (isApplicationHealth()) throw new RuntimeException("application.nodeid=" + nodeid + " exists in cluster");
|
||||
deregister(application);
|
||||
|
||||
String serviceid = generateApplicationServiceId();
|
||||
String serviceName = generateApplicationServiceName();
|
||||
AddressEntry entry = new AddressEntry();
|
||||
entry.addr = this.appAddress;
|
||||
entry.nodeid = this.nodeid;
|
||||
entry.time = System.currentTimeMillis();
|
||||
source.hset(serviceName, serviceid, AddressEntry.class, entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deregister(Application application) {
|
||||
String serviceName = generateApplicationServiceName();
|
||||
source.remove(serviceName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClusterEntry register(NodeServer ns, String protocol, Service service) {
|
||||
deregister(ns, protocol, service, false);
|
||||
//
|
||||
ClusterEntry clusterEntry = new ClusterEntry(ns, protocol, service);
|
||||
AddressEntry entry = new AddressEntry();
|
||||
entry.addr = clusterEntry.address;
|
||||
entry.nodeid = this.nodeid;
|
||||
entry.time = System.currentTimeMillis();
|
||||
source.hset(clusterEntry.serviceName, clusterEntry.serviceid, AddressEntry.class, entry);
|
||||
return clusterEntry;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deregister(NodeServer ns, String protocol, Service service) {
|
||||
deregister(ns, protocol, service, true);
|
||||
}
|
||||
|
||||
protected void deregister(NodeServer ns, String protocol, Service service, boolean realcanceled) {
|
||||
String serviceName = generateServiceName(ns, protocol, service);
|
||||
String serviceid = generateServiceId(ns, protocol, service);
|
||||
ClusterEntry currEntry = null;
|
||||
for (final ClusterEntry entry : localEntrys.values()) {
|
||||
if (entry.serviceName.equals(serviceName) && entry.serviceid.equals(serviceid)) {
|
||||
currEntry = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (currEntry == null) {
|
||||
for (final ClusterEntry entry : remoteEntrys.values()) {
|
||||
if (entry.serviceName.equals(serviceName) && entry.serviceid.equals(serviceid)) {
|
||||
currEntry = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
source.hremove(serviceName, serviceid);
|
||||
if (realcanceled && currEntry != null) currEntry.canceled = true;
|
||||
if (!"mqtp".equals(protocol) && currEntry != null && currEntry.submqtp) {
|
||||
deregister(ns, "mqtp", service, realcanceled);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String generateApplicationServiceName() {
|
||||
return "cluster." + super.generateApplicationServiceName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String generateServiceName(NodeServer ns, String protocol, Service service) {
|
||||
return "cluster." + super.generateServiceName(ns, protocol, service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateHttpServiceName(String protocol, String module, String resname) {
|
||||
return "cluster." + super.generateHttpServiceName(protocol, module, resname);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String generateApplicationCheckName() {
|
||||
return generateApplicationServiceName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String generateApplicationCheckId() {
|
||||
return generateApplicationServiceId();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String generateCheckName(NodeServer ns, String protocol, Service service) {
|
||||
return generateServiceName(ns, protocol, service);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String generateCheckId(NodeServer ns, String protocol, Service service) {
|
||||
return generateServiceId(ns, protocol, service);
|
||||
}
|
||||
|
||||
public static class AddressEntry {
|
||||
|
||||
public InetSocketAddress addr;
|
||||
|
||||
public int nodeid;
|
||||
|
||||
public long time;
|
||||
|
||||
public AddressEntry() {
|
||||
}
|
||||
|
||||
public AddressEntry refresh() {
|
||||
this.time = System.currentTimeMillis();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonConvert.root().convertTo(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
369
src/main/java/org/redkale/cluster/ClusterAgent.java
Normal file
369
src/main/java/org/redkale/cluster/ClusterAgent.java
Normal file
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.cluster;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.net.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.logging.Logger;
|
||||
import javax.annotation.Resource;
|
||||
import org.redkale.boot.*;
|
||||
import static org.redkale.boot.Application.*;
|
||||
import org.redkale.convert.ConvertDisabled;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
import org.redkale.mq.MessageMultiConsumer;
|
||||
import org.redkale.net.*;
|
||||
import org.redkale.net.http.*;
|
||||
import org.redkale.net.sncp.*;
|
||||
import org.redkale.service.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* 第三方服务发现管理接口cluster
|
||||
*
|
||||
*
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*
|
||||
* @since 2.1.0
|
||||
*/
|
||||
public abstract class ClusterAgent {
|
||||
|
||||
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
|
||||
|
||||
@Resource(name = RESNAME_APP_NODEID)
|
||||
protected int nodeid;
|
||||
|
||||
@Resource(name = RESNAME_APP_NAME)
|
||||
protected String appName = "";
|
||||
|
||||
@Resource(name = RESNAME_APP_ADDR)
|
||||
protected InetSocketAddress appAddress;
|
||||
|
||||
protected String name;
|
||||
|
||||
protected boolean waits;
|
||||
|
||||
protected String[] protocols; //必须全大写
|
||||
|
||||
protected int[] ports;
|
||||
|
||||
protected AnyValue config;
|
||||
|
||||
protected TransportFactory transportFactory;
|
||||
|
||||
protected final ConcurrentHashMap<String, ClusterEntry> localEntrys = new ConcurrentHashMap<>();
|
||||
|
||||
protected final ConcurrentHashMap<String, ClusterEntry> remoteEntrys = new ConcurrentHashMap<>();
|
||||
|
||||
public void init(ResourceFactory factory, AnyValue config) {
|
||||
this.config = config;
|
||||
this.name = config.getValue("name", "");
|
||||
this.waits = config.getBoolValue("waits", false);
|
||||
{
|
||||
String ps = config.getValue("protocols", "").toUpperCase();
|
||||
if (ps == null || ps.isEmpty()) ps = "SNCP;HTTP";
|
||||
this.protocols = ps.split(";");
|
||||
}
|
||||
String ts = config.getValue("ports", "");
|
||||
if (ts != null && !ts.isEmpty()) {
|
||||
String[] its = ts.split(";");
|
||||
List<Integer> list = new ArrayList<>();
|
||||
for (String str : its) {
|
||||
if (str.trim().isEmpty()) continue;
|
||||
list.add(Integer.parseInt(str.trim()));
|
||||
}
|
||||
if (!list.isEmpty()) this.ports = list.stream().mapToInt(x -> x).toArray();
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy(AnyValue config) {
|
||||
}
|
||||
|
||||
//ServiceLoader时判断配置是否符合当前实现类
|
||||
public abstract boolean acceptsConf(AnyValue config);
|
||||
|
||||
public boolean containsProtocol(String protocol) {
|
||||
if (protocol == null || protocol.isEmpty()) return false;
|
||||
return protocols == null || Utility.contains(protocols, protocol.toUpperCase());
|
||||
}
|
||||
|
||||
public boolean containsPort(int port) {
|
||||
if (ports == null || ports.length == 0) return true;
|
||||
return Utility.contains(ports, port);
|
||||
}
|
||||
|
||||
public abstract void register(Application application);
|
||||
|
||||
public abstract void deregister(Application application);
|
||||
|
||||
//注册服务, 在NodeService调用Service.init方法之前调用
|
||||
public void register(NodeServer ns, String protocol, Set<Service> localServices, Set<Service> remoteServices) {
|
||||
if (localServices.isEmpty()) return;
|
||||
//注册本地模式
|
||||
for (Service service : localServices) {
|
||||
if (!canRegister(ns, protocol, service)) continue;
|
||||
ClusterEntry htentry = register(ns, protocol, service);
|
||||
localEntrys.put(htentry.serviceid, htentry);
|
||||
if (protocol.toLowerCase().startsWith("http")) {
|
||||
MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class);
|
||||
if (mmc != null) {
|
||||
ClusterEntry mqentry = register(ns, "mqtp", service);
|
||||
localEntrys.put(mqentry.serviceid, mqentry);
|
||||
htentry.submqtp = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
//远程模式加载IP列表, 只支持SNCP协议
|
||||
if (ns.isSNCP()) {
|
||||
for (Service service : remoteServices) {
|
||||
ClusterEntry entry = new ClusterEntry(ns, protocol, service);
|
||||
updateSncpTransport(entry);
|
||||
remoteEntrys.put(entry.serviceid, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//注销服务, 在NodeService调用Service.destroy 方法之前调用
|
||||
public void deregister(NodeServer ns, String protocol, Set<Service> localServices, Set<Service> remoteServices) {
|
||||
//注销本地模式 远程模式不注册
|
||||
for (Service service : localServices) {
|
||||
if (!canRegister(ns, protocol, service)) continue;
|
||||
deregister(ns, protocol, service);
|
||||
}
|
||||
afterDeregister(ns, protocol);
|
||||
}
|
||||
|
||||
protected boolean canRegister(NodeServer ns, String protocol, Service service) {
|
||||
if ("SNCP".equalsIgnoreCase(protocol) && service.getClass().getAnnotation(Local.class) != null) return false;
|
||||
AutoLoad al = service.getClass().getAnnotation(AutoLoad.class);
|
||||
if (al != null && !al.value() && service.getClass().getAnnotation(Local.class) != null) return false;
|
||||
if (service instanceof WebSocketNode) {
|
||||
if (((WebSocketNode) service).getLocalWebSocketEngine() == null) return false;
|
||||
}
|
||||
ClusterEntry entry = new ClusterEntry(ns, protocol, service);
|
||||
if (entry.serviceName.trim().endsWith(serviceSeparator())) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
}
|
||||
|
||||
protected void afterDeregister(NodeServer ns, String protocol) {
|
||||
if (!this.waits) return;
|
||||
int s = intervalCheckSeconds();
|
||||
if (s > 0) { //暂停,弥补其他依赖本进程服务的周期偏差
|
||||
try {
|
||||
Thread.sleep(s * 1000);
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
logger.info(this.getClass().getSimpleName() + " wait for " + s * 1000 + "ms after deregister");
|
||||
}
|
||||
}
|
||||
|
||||
public int intervalCheckSeconds() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
//获取MQTP的HTTP远程服务的可用ip列表, key = serviceName的后半段
|
||||
public abstract CompletableFuture<Map<String, Collection<InetSocketAddress>>> queryMqtpAddress(String protocol, String module, String resname);
|
||||
|
||||
//获取HTTP远程服务的可用ip列表
|
||||
public abstract CompletableFuture<Collection<InetSocketAddress>> queryHttpAddress(String protocol, String module, String resname);
|
||||
|
||||
//获取远程服务的可用ip列表
|
||||
protected abstract CompletableFuture<Collection<InetSocketAddress>> queryAddress(ClusterEntry entry);
|
||||
|
||||
//注册服务
|
||||
protected abstract ClusterEntry register(NodeServer ns, String protocol, Service service);
|
||||
|
||||
//注销服务
|
||||
protected abstract void deregister(NodeServer ns, String protocol, Service service);
|
||||
|
||||
//格式: protocol:classtype-resourcename
|
||||
protected void updateSncpTransport(ClusterEntry entry) {
|
||||
Service service = entry.serviceRef.get();
|
||||
if (service == null) return;
|
||||
Collection<InetSocketAddress> addrs = ClusterAgent.this.queryAddress(entry).join();
|
||||
Sncp.updateTransport(service, transportFactory, Sncp.getResourceType(service).getName() + "-" + Sncp.getResourceName(service), entry.netProtocol, entry.address, null, addrs);
|
||||
}
|
||||
|
||||
protected String urlEncode(String value) {
|
||||
return value == null ? null : URLEncoder.encode(value, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
protected String generateApplicationServiceName() {
|
||||
return "application" + (appName == null || appName.isEmpty() ? "" : ("." + appName)) + ".node." + this.nodeid;
|
||||
}
|
||||
|
||||
protected String generateApplicationServiceType() {
|
||||
return "application.nodes";
|
||||
}
|
||||
|
||||
protected String generateApplicationServiceId() { //与serviceName相同
|
||||
return generateApplicationServiceName();
|
||||
}
|
||||
|
||||
protected String generateApplicationCheckName() {
|
||||
return "check-" + generateApplicationServiceName();
|
||||
}
|
||||
|
||||
protected String generateApplicationCheckId() {
|
||||
return "check-" + generateApplicationServiceId();
|
||||
}
|
||||
|
||||
protected String generateApplicationHost() {
|
||||
return this.appAddress.getHostString();
|
||||
}
|
||||
|
||||
protected int generateApplicationPort() {
|
||||
return this.appAddress.getPort();
|
||||
}
|
||||
|
||||
protected String serviceSeparator() {
|
||||
return "-";
|
||||
}
|
||||
|
||||
//也会提供给HttpMessageClusterAgent适用
|
||||
public String generateHttpServiceName(String protocol, String module, String resname) {
|
||||
return protocol.toLowerCase() + serviceSeparator() + module + (resname == null || resname.isEmpty() ? "" : ("-" + resname));
|
||||
}
|
||||
|
||||
//格式: protocol:classtype-resourcename
|
||||
protected String generateServiceName(NodeServer ns, String protocol, Service service) {
|
||||
if (protocol.toLowerCase().startsWith("http")) { //HTTP使用RestService.name方式是为了与MessageClient中的module保持一致, 因为HTTP依靠的url中的module,无法知道Service类名
|
||||
String resname = Sncp.getResourceName(service);
|
||||
String module = Rest.getRestModule(service).toLowerCase();
|
||||
return protocol.toLowerCase() + serviceSeparator() + module + (resname.isEmpty() ? "" : ("-" + resname));
|
||||
}
|
||||
if ("mqtp".equalsIgnoreCase(protocol)) {
|
||||
MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class);
|
||||
String selfmodule = Rest.getRestModule(service).toLowerCase();
|
||||
return protocol.toLowerCase() + serviceSeparator() + mmc.module() + serviceSeparator() + selfmodule;
|
||||
}
|
||||
if (!Sncp.isSncpDyn(service)) return protocol.toLowerCase() + serviceSeparator() + service.getClass().getName();
|
||||
String resname = Sncp.getResourceName(service);
|
||||
return protocol.toLowerCase() + serviceSeparator() + Sncp.getResourceType(service).getName() + (resname.isEmpty() ? "" : ("-" + resname));
|
||||
}
|
||||
|
||||
//格式: protocol:classtype-resourcename:nodeid
|
||||
protected String generateServiceId(NodeServer ns, String protocol, Service service) {
|
||||
return generateServiceName(ns, protocol, service) + serviceSeparator() + this.nodeid;
|
||||
}
|
||||
|
||||
protected String generateCheckName(NodeServer ns, String protocol, Service service) {
|
||||
return "check-" + generateServiceName(ns, protocol, service);
|
||||
}
|
||||
|
||||
protected String generateCheckId(NodeServer ns, String protocol, Service service) {
|
||||
return "check-" + generateServiceId(ns, protocol, service);
|
||||
}
|
||||
|
||||
protected ConcurrentHashMap<String, ClusterEntry> getLocalEntrys() {
|
||||
return localEntrys;
|
||||
}
|
||||
|
||||
protected ConcurrentHashMap<String, ClusterEntry> getRemoteEntrys() {
|
||||
return remoteEntrys;
|
||||
}
|
||||
|
||||
public TransportFactory getTransportFactory() {
|
||||
return transportFactory;
|
||||
}
|
||||
|
||||
public void setTransportFactory(TransportFactory transportFactory) {
|
||||
this.transportFactory = transportFactory;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String[] getProtocols() {
|
||||
return protocols;
|
||||
}
|
||||
|
||||
public void setProtocols(String[] protocols) {
|
||||
this.protocols = protocols;
|
||||
}
|
||||
|
||||
public int[] getPorts() {
|
||||
return ports;
|
||||
}
|
||||
|
||||
public void setPorts(int[] ports) {
|
||||
this.ports = ports;
|
||||
}
|
||||
|
||||
public AnyValue getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public void setConfig(AnyValue config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public class ClusterEntry {
|
||||
|
||||
//serviceName+nodeid为主 服务的单个实例
|
||||
public String serviceid;
|
||||
|
||||
//以协议+Rest资源名为主 服务类名
|
||||
public String serviceName;
|
||||
|
||||
public String serviceType;
|
||||
|
||||
public String checkid;
|
||||
|
||||
public String checkName;
|
||||
|
||||
//http or sncp or mqtp
|
||||
public String protocol;
|
||||
|
||||
//TCP or UDP
|
||||
public String netProtocol;
|
||||
|
||||
@ConvertDisabled
|
||||
public WeakReference<Service> serviceRef;
|
||||
|
||||
public InetSocketAddress address;
|
||||
|
||||
public boolean canceled;
|
||||
|
||||
public boolean submqtp;
|
||||
|
||||
public ClusterEntry(NodeServer ns, String protocol, Service service) {
|
||||
this.serviceid = generateServiceId(ns, protocol, service);
|
||||
this.serviceName = generateServiceName(ns, protocol, service);
|
||||
this.checkid = generateCheckId(ns, protocol, service);
|
||||
this.checkName = generateCheckName(ns, protocol, service);
|
||||
this.serviceType = Sncp.getServiceType(service).getName();
|
||||
this.protocol = protocol;
|
||||
InetSocketAddress addr = ns.getSocketAddress();
|
||||
String host = addr.getHostString();
|
||||
if ("0.0.0.0".equals(host)) {
|
||||
host = appAddress.getHostString();
|
||||
addr = new InetSocketAddress(host, addr.getPort());
|
||||
}
|
||||
this.address = addr;
|
||||
this.serviceRef = new WeakReference(service);
|
||||
Server server = ns.getServer();
|
||||
this.netProtocol = server instanceof SncpServer ? ((SncpServer) server).getNetprotocol() : Transport.DEFAULT_NETPROTOCOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonConvert.root().convertTo(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/main/java/org/redkale/cluster/ClusterAgentProvider.java
Normal file
25
src/main/java/org/redkale/cluster/ClusterAgentProvider.java
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.cluster;
|
||||
|
||||
import org.redkale.util.AnyValue;
|
||||
|
||||
/**
|
||||
* 自定义的ClusterAgent加载器
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @since 2.5.0
|
||||
*/
|
||||
public interface ClusterAgentProvider {
|
||||
|
||||
public boolean acceptsConf(AnyValue config);
|
||||
|
||||
public Class<? extends ClusterAgent> agentClass();
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import static org.redkale.convert.Reader.ValueType.MAP;
|
||||
/**
|
||||
* 对不明类型的对象进行反序列化。 <br>
|
||||
* <b>注意: 目前只支持文本格式</b> <br>
|
||||
* <p>
|
||||
*
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
@@ -31,12 +31,17 @@ public class AnyDecoder implements Decodeable<Reader, Object> {
|
||||
|
||||
private static final Creator<HashMap> mapCreator = Creator.create(HashMap.class);
|
||||
|
||||
protected final Decodeable<Reader, String> stringDecoder;
|
||||
final Decodeable<Reader, String> stringDecoder;
|
||||
|
||||
protected final CollectionDecoder collectionDecoder;
|
||||
final CollectionDecoder collectionDecoder;
|
||||
|
||||
protected final MapDecoder mapDecoder;
|
||||
final MapDecoder mapDecoder;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param factory ConvertFactory
|
||||
*/
|
||||
public AnyDecoder(final ConvertFactory factory) {
|
||||
this.stringDecoder = factory.loadDecoder(String.class);
|
||||
this.collectionDecoder = new CollectionDecoder(factory, collectionObjectType, Object.class, collectionCreator, this);
|
||||
53
src/main/java/org/redkale/convert/AnyEncoder.java
Normal file
53
src/main/java/org/redkale/convert/AnyEncoder.java
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.convert;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* 对不明类型的对象进行序列化; BSON序列化时将对象的类名写入Writer,JSON则不写入。
|
||||
*
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <T> 序列化的泛型类型
|
||||
*/
|
||||
public final class AnyEncoder<T> implements Encodeable<Writer, T> {
|
||||
|
||||
final ConvertFactory factory;
|
||||
|
||||
AnyEncoder(ConvertFactory factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void convertTo(final Writer out, final T value) {
|
||||
if (value == null) {
|
||||
out.writeClassName(null);
|
||||
out.writeNull();
|
||||
} else {
|
||||
Class clazz = value.getClass();
|
||||
if (clazz == Object.class) {
|
||||
out.writeObjectB(value);
|
||||
out.writeObjectE(value);
|
||||
return;
|
||||
}
|
||||
if (out.needWriteClassName()) out.writeClassName(factory.getEntityAlias(clazz));
|
||||
factory.loadEncoder(clazz).convertTo(out, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean specifyable() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
40
src/main/java/org/redkale/convert/AnyValueDecoder.java
Normal file
40
src/main/java/org/redkale/convert/AnyValueDecoder.java
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.convert;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import org.redkale.util.AnyValue;
|
||||
|
||||
/**
|
||||
* AnyValue的Decoder实现
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <R> Reader输入的子类型
|
||||
*
|
||||
* @since 2.5.0
|
||||
*/
|
||||
public class AnyValueDecoder<R extends Reader> implements Decodeable<R, AnyValue> {
|
||||
|
||||
protected final ConvertFactory factory;
|
||||
|
||||
public AnyValueDecoder(final ConvertFactory factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnyValue convertFrom(R in) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return AnyValue.class;
|
||||
}
|
||||
|
||||
}
|
||||
33
src/main/java/org/redkale/convert/AnyValueEncoder.java
Normal file
33
src/main/java/org/redkale/convert/AnyValueEncoder.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.convert;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import org.redkale.util.AnyValue;
|
||||
|
||||
/**
|
||||
* AnyValue的Encoder实现
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <W> Writer输出的子类
|
||||
*
|
||||
* @since 2.5.0
|
||||
*/
|
||||
public class AnyValueEncoder<W extends Writer> implements Encodeable<W, AnyValue> {
|
||||
|
||||
@Override
|
||||
public void convertTo(W out, AnyValue value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return AnyValue.class;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,6 +7,8 @@ package org.redkale.convert;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
import java.util.function.IntFunction;
|
||||
import org.redkale.util.Creator;
|
||||
|
||||
/**
|
||||
* 数组的反序列化操作类 <br>
|
||||
@@ -30,6 +32,8 @@ public class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
||||
|
||||
protected final Decodeable<Reader, T> componentDecoder;
|
||||
|
||||
protected final IntFunction<T[]> componentArrayFunction;
|
||||
|
||||
protected volatile boolean inited = false;
|
||||
|
||||
protected final Object lock = new Object();
|
||||
@@ -52,6 +56,7 @@ public class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
||||
}
|
||||
factory.register(type, this);
|
||||
this.componentDecoder = factory.loadDecoder(this.componentType);
|
||||
this.componentArrayFunction = Creator.arrayFunction(this.componentClass);
|
||||
} finally {
|
||||
inited = true;
|
||||
synchronized (lock) {
|
||||
@@ -102,7 +107,7 @@ public class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
||||
}
|
||||
}
|
||||
in.readArrayE();
|
||||
T[] rs = (T[]) Array.newInstance((Class) this.componentClass, result.size());
|
||||
T[] rs = this.componentArrayFunction.apply(result.size());
|
||||
return result.toArray(rs);
|
||||
}
|
||||
|
||||
@@ -119,6 +124,7 @@ public class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
||||
}
|
||||
|
||||
protected T readMemberValue(Reader in, DeMember member, Decodeable<Reader, T> decoder, boolean first) {
|
||||
if (in == null) return null;
|
||||
return decoder.convertFrom(in);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
|
||||
protected final Encodeable<Writer, Object> componentEncoder;
|
||||
|
||||
protected final boolean subTypeFinal;
|
||||
|
||||
protected volatile boolean inited = false;
|
||||
|
||||
protected final Object lock = new Object();
|
||||
@@ -47,6 +49,7 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
factory.register(type, this);
|
||||
this.componentEncoder = factory.loadEncoder(this.componentType);
|
||||
this.anyEncoder = factory.getAnyEncoder();
|
||||
this.subTypeFinal = (this.componentType instanceof Class) && Modifier.isFinal(((Class) this.componentType).getModifiers());
|
||||
} finally {
|
||||
inited = true;
|
||||
synchronized (lock) {
|
||||
@@ -65,12 +68,14 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
out.writeNull();
|
||||
return;
|
||||
}
|
||||
if (value.length == 0) {
|
||||
out.writeArrayB(0, componentEncoder, value);
|
||||
int iMax = value.length - 1;
|
||||
if (iMax == -1) {
|
||||
out.writeArrayB(0, this, componentEncoder, value);
|
||||
out.writeArrayE();
|
||||
return;
|
||||
}
|
||||
if (this.componentEncoder == null) {
|
||||
Encodeable<Writer, Object> itemEncoder = this.componentEncoder;
|
||||
if (itemEncoder == null) {
|
||||
if (!this.inited) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
@@ -81,19 +86,29 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (out.writeArrayB(value.length, componentEncoder, value) < 0) {
|
||||
final Type comp = this.componentType;
|
||||
boolean first = true;
|
||||
for (Object v : value) {
|
||||
if (!first) out.writeArrayMark();
|
||||
writeMemberValue(out, member, ((v != null && (v.getClass() == comp || out.specify() == comp)) ? componentEncoder : anyEncoder), v, first);
|
||||
if (first) first = false;
|
||||
if (subTypeFinal) {
|
||||
if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) {
|
||||
for (int i = 0;; i++) {
|
||||
writeMemberValue(out, member, itemEncoder, value[i], i);
|
||||
if (i == iMax) break;
|
||||
out.writeArrayMark();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) {
|
||||
final Type comp = this.componentType;
|
||||
for (int i = 0;; i++) {
|
||||
Object v = value[i];
|
||||
writeMemberValue(out, member, ((v != null && (v.getClass() == comp || out.specify() == comp)) ? itemEncoder : anyEncoder), v, i);
|
||||
if (i == iMax) break;
|
||||
out.writeArrayMark();
|
||||
}
|
||||
}
|
||||
}
|
||||
out.writeArrayE();
|
||||
}
|
||||
|
||||
protected void writeMemberValue(Writer out, EnMember member, Encodeable<Writer, Object> encoder, Object value, boolean first) {
|
||||
protected void writeMemberValue(Writer out, EnMember member, Encodeable<Writer, Object> encoder, Object value, int index) {
|
||||
encoder.convertTo(out, value);
|
||||
}
|
||||
|
||||
@@ -107,6 +122,11 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean specifyable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Type getComponentType() {
|
||||
return componentType;
|
||||
}
|
||||
@@ -114,5 +134,4 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
public Encodeable<Writer, Object> getComponentEncoder() {
|
||||
return componentEncoder;
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user