mirror of
https://github.com/NationalSecurityAgency/timely.git
synced 2026-11-09 17:36:01 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
577688aa54 | ||
|
|
1f0fe4198d | ||
|
|
385b142f71 | ||
|
|
9065dcdce2 | ||
|
|
3877695949 | ||
|
|
d4e504b31c | ||
|
|
32ae6ee399 | ||
|
|
9688514a0f | ||
|
|
9aa089a585 | ||
|
|
8d75089525 | ||
|
|
7857d862b3 | ||
|
|
e8e2e5e144 | ||
|
|
33515061b7 | ||
|
|
7d2ab1be7e | ||
|
|
521969b9ee | ||
|
|
62fb143490 | ||
|
|
08cf3dc5c2 | ||
|
|
6831223f50 | ||
|
|
20ed753723 | ||
|
|
a8e3737200 | ||
|
|
d1c3989a8d | ||
|
|
5712ebe2ae | ||
|
|
e47cbe5e3b | ||
|
|
f5b3a205b2 | ||
|
|
a2b61191c7 | ||
|
|
adfd4080bd | ||
|
|
c5da744883 | ||
|
|
ebc35e5c63 | ||
|
|
3e49716525 | ||
|
|
b47668f277 | ||
|
|
483f5f8f1a | ||
|
|
eb45592f90 | ||
|
|
7ad726a551 | ||
|
|
87a3b43b13 | ||
|
|
b13bd4c65d | ||
|
|
5a5c539688 | ||
|
|
465223cdb6 | ||
|
|
99e4e93603 | ||
|
|
193c9f2c5b | ||
|
|
8490e9cbc6 | ||
|
|
aa08e5df03 | ||
|
|
053017ba39 | ||
|
|
6f188b3473 | ||
|
|
b27b3c3e52 | ||
|
|
b2330262bd | ||
|
|
0702fa3d47 | ||
|
|
813d8e5d28 | ||
|
|
e63f41a20c | ||
|
|
9d7ca484e3 | ||
|
|
bb787fc8ca | ||
|
|
6dd96e1c15 | ||
|
|
a68507a00e | ||
|
|
8d3eb0a433 | ||
|
|
e9384ed058 | ||
|
|
31e021a01f | ||
|
|
ad752cd538 | ||
|
|
ed6578ca73 | ||
|
|
1e54d1dd41 | ||
|
|
cec5d662ad | ||
|
|
f380e4c203 | ||
|
|
19d651a65a | ||
|
|
13b113d1a5 | ||
|
|
979aaa91d5 | ||
|
|
b84d76cd83 | ||
|
|
6a8c406f56 | ||
|
|
30c270c029 | ||
|
|
28066ed281 | ||
|
|
6e37ca292f | ||
|
|
db48d22661 | ||
|
|
365a0b58ea | ||
|
|
d4bd994f03 | ||
|
|
1c5bd80d4b | ||
|
|
d6a7b5ff97 | ||
|
|
ac9cb0aef9 | ||
|
|
d88d714d90 | ||
|
|
ffa2829fe5 | ||
|
|
b5c6495662 | ||
|
|
471c19a7c0 | ||
|
|
1d108d0f40 | ||
|
|
7f1d6da6dc | ||
|
|
a4b9208af0 | ||
|
|
9f83bbd158 | ||
|
|
5c87f10383 | ||
|
|
ccd158df4f | ||
|
|
20d94974fa | ||
|
|
4f5443ce06 | ||
|
|
79bb28c2f8 | ||
|
|
8cba352315 | ||
|
|
884cda666a | ||
|
|
c0dddf7560 | ||
|
|
44cf741f01 | ||
|
|
63e3e30bc5 | ||
|
|
b6084cfd0a | ||
|
|
02428eb687 | ||
|
|
7f88aafcba | ||
|
|
f4dea9008d | ||
|
|
e1026d921c | ||
|
|
35690907be | ||
|
|
3ad0542fc7 | ||
|
|
65c3dbd544 | ||
|
|
bc8ac808b4 | ||
|
|
75f61557a2 | ||
|
|
f90e6b076d | ||
|
|
47cca3d8e7 | ||
|
|
837a7e7873 | ||
|
|
b871b44b72 | ||
|
|
97a4c89d0c | ||
|
|
8e14f805dc | ||
|
|
82717e043d | ||
|
|
018a6939db | ||
|
|
2618c77db0 | ||
|
|
c9d8d34e79 | ||
|
|
40443b32fd | ||
|
|
1151d00926 | ||
|
|
c76359868a | ||
|
|
964c25a36b | ||
|
|
1e86f824af | ||
|
|
01fd02005a | ||
|
|
fb4c2a9ceb | ||
|
|
ecdce169bc | ||
|
|
f0a8dec91c | ||
|
|
e9cfc6bf3c | ||
|
|
150ff8a800 | ||
|
|
727ac193ab | ||
|
|
3c1c9c49d5 | ||
|
|
4a3fdfaea2 | ||
|
|
ce2f2ae8de | ||
|
|
4136f7b0f7 | ||
|
|
dc45e382ce | ||
|
|
57aa27c0eb | ||
|
|
236ca52c93 | ||
|
|
787eb795b6 | ||
|
|
e0cdc7339e | ||
|
|
fd27069a2d | ||
|
|
fcea9014d2 | ||
|
|
9e42fbb04c | ||
|
|
1c508a738f | ||
|
|
7078f7f6e8 | ||
|
|
92d732f8a4 | ||
|
|
c90eab79f9 | ||
|
|
977f2568d7 | ||
|
|
ed9e958df7 | ||
|
|
22ffeba825 | ||
|
|
5d27eaf9cf | ||
|
|
07c5ccefc0 | ||
|
|
47e18611f2 | ||
|
|
83d655b55e | ||
|
|
d4e94080d4 | ||
|
|
befb8d6fac | ||
|
|
b0e0192374 | ||
|
|
87a2950ade | ||
|
|
eb1c0db712 | ||
|
|
5e605079eb | ||
|
|
ebc8810d65 | ||
|
|
5cfcbc7f7b | ||
|
|
adeb496a88 | ||
|
|
b9d45b2185 | ||
|
|
0c37f0dfc4 | ||
|
|
ad3b2769fe | ||
|
|
2e22c87c1b | ||
|
|
7639ce60bc | ||
|
|
ae3d50277e | ||
|
|
dcc563b406 | ||
|
|
f3591651f1 | ||
|
|
7f873ca9e0 | ||
|
|
415dcf2c90 | ||
|
|
fd5cba00f0 | ||
|
|
d14fd0f70e | ||
|
|
bb24528f85 | ||
|
|
872550d0b7 | ||
|
|
a15a6ba1b1 | ||
|
|
be57777a3d | ||
|
|
1fe3ce6396 | ||
|
|
203bc79211 | ||
|
|
685b1554e9 | ||
|
|
1eac62fd59 | ||
|
|
48c514abc7 | ||
|
|
8ab0ed7b24 | ||
|
|
a38011b4a5 | ||
|
|
1f4e6b915d | ||
|
|
402f931fc9 | ||
|
|
6d6c3e6070 | ||
|
|
2e8be87224 | ||
|
|
b7e65975b0 | ||
|
|
d27f5c4b7d | ||
|
|
384ffac2fd | ||
|
|
ab816e432a | ||
|
|
99ee16e359 | ||
|
|
3a53a3c029 | ||
|
|
67263d8db4 | ||
|
|
06c1641ad1 | ||
|
|
234d1e197d | ||
|
|
22e8e480e5 | ||
|
|
22fc8af88c | ||
|
|
bdebff8250 | ||
|
|
d0e46a3894 | ||
|
|
d7769c653d | ||
|
|
094c38223e | ||
|
|
fddf304087 | ||
|
|
452876af0d | ||
|
|
16dca4d000 | ||
|
|
f989143692 | ||
|
|
4fe37604b7 | ||
|
|
5da237e7fe | ||
|
|
23eb3f433e | ||
|
|
fd684122dd | ||
|
|
6ea72a13fa | ||
|
|
a91ac15fab | ||
|
|
e14878ff56 | ||
|
|
8276ba8ff4 | ||
|
|
ec2415e319 | ||
|
|
f5b34ba69a | ||
|
|
c39c82c917 | ||
|
|
e3f36f9057 | ||
|
|
a7596ff0a5 | ||
|
|
a9d8b9b3b1 | ||
|
|
c8e7c646dc | ||
|
|
0dac148f95 | ||
|
|
f2ebab9325 | ||
|
|
1d13e198d0 | ||
|
|
b550f04dae | ||
|
|
b1e0ff5d94 | ||
|
|
3a236aa81e | ||
|
|
a60fd38cda | ||
|
|
6062141cce | ||
|
|
506095a1ed | ||
|
|
7452f6b172 | ||
|
|
321a994cdc | ||
|
|
d82f9707b3 | ||
|
|
923add84f6 | ||
|
|
58bcd3922d | ||
|
|
0bb523732e | ||
|
|
e644e3a5d3 | ||
|
|
8e0396d7d3 | ||
|
|
ebfe330a2b | ||
|
|
f5dcc4efb8 | ||
|
|
2011c772a1 | ||
|
|
fd2f54b3de | ||
|
|
384f8d7b19 | ||
|
|
97f87c9c4a | ||
|
|
13b4487a2b | ||
|
|
e619c48a94 | ||
|
|
b744ba51bb | ||
|
|
44a0dafe03 | ||
|
|
dfa162fed5 | ||
|
|
fd1937a3f7 | ||
|
|
10179a6613 | ||
|
|
8eada057da | ||
|
|
872c00a894 | ||
|
|
bb5c721450 | ||
|
|
7c110ede15 | ||
|
|
eb5235ff8c | ||
|
|
5ad521614d | ||
|
|
73d2997a24 | ||
|
|
5e89f01652 | ||
|
|
757cf8ef79 | ||
|
|
ddf0346f04 | ||
|
|
115d04f19e | ||
|
|
f3eaecc7a9 | ||
|
|
04944c9e8f | ||
|
|
868ea757f1 | ||
|
|
be28b785a1 | ||
|
|
902ddd7fdc | ||
|
|
c665bb4e8f | ||
|
|
14a9aac0fc | ||
|
|
c74bf7b50e | ||
|
|
2828342821 | ||
|
|
482f0b9979 | ||
|
|
058429a9fd | ||
|
|
77cdc3fc66 | ||
|
|
5e649aa91a | ||
|
|
a01287460a | ||
|
|
3530073f2e | ||
|
|
39dc7095f7 | ||
|
|
28b58aed86 | ||
|
|
ae07d2f6d2 | ||
|
|
ef4da2a0c0 | ||
|
|
f00be01caf | ||
|
|
ed5fa1b75c | ||
|
|
b1256f4800 | ||
|
|
b40b3b47ed | ||
|
|
9c7ea33305 | ||
|
|
ff7f3c6708 | ||
|
|
bd6865585c | ||
|
|
c59addd90e | ||
|
|
1f438aaea6 | ||
|
|
8e7b03b64e | ||
|
|
82a99d5537 | ||
|
|
3c36233e6b | ||
|
|
54e4408d8c | ||
|
|
965156d54b | ||
|
|
f1d92f01dd | ||
|
|
052036b0c2 | ||
|
|
c5381c62af | ||
|
|
60d1dbd37d | ||
|
|
512b57d799 | ||
|
|
395502b152 | ||
|
|
18cd104883 | ||
|
|
51e537aca2 | ||
|
|
1b1427c3ee | ||
|
|
324da17ba3 | ||
|
|
719061a638 | ||
|
|
e8a491c9ea | ||
|
|
0f89c96ba0 | ||
|
|
d552a1b5cb | ||
|
|
1bb564f84a | ||
|
|
f868ff0bcf | ||
|
|
d1fdd685a6 | ||
|
|
0f93ffcc2b | ||
|
|
bdbd7a38a8 | ||
|
|
b147e6a2d7 | ||
|
|
200a871f85 | ||
|
|
0a8c73adab | ||
|
|
c1359e81a7 | ||
|
|
3e4f52dfc9 | ||
|
|
f4f2a1abfa | ||
|
|
b5419b1e9c | ||
|
|
3a91faa8e6 | ||
|
|
062e35a3b3 | ||
|
|
00a93ec76e | ||
|
|
65496915b8 | ||
|
|
3125ad8f72 | ||
|
|
1e144aaa66 | ||
|
|
1478579c59 | ||
|
|
d77f1e7b9f | ||
|
|
e559ad1097 | ||
|
|
c2c35cfa25 | ||
|
|
ec9a688846 | ||
|
|
4d4f6efeb8 | ||
|
|
5a69760f54 | ||
|
|
0aff03f8da | ||
|
|
b363be873b | ||
|
|
7dead4c392 | ||
|
|
d71ae97f7a | ||
|
|
cfa9f48dd0 | ||
|
|
1e341563fb | ||
|
|
34c6c69d61 | ||
|
|
a35e1341b6 | ||
|
|
078c42a082 | ||
|
|
e0cee862d9 | ||
|
|
e5cf1f2589 | ||
|
|
6bf197da74 | ||
|
|
0eb4caa425 | ||
|
|
2f617a2160 | ||
|
|
938a056fcc | ||
|
|
f9c4195f23 | ||
|
|
2384d9af3f | ||
|
|
adcf6c7099 | ||
|
|
126ea199cc | ||
|
|
edd22fde15 | ||
|
|
84b3e5e3d6 | ||
|
|
0316c0c5e9 | ||
|
|
259bf0f560 | ||
|
|
6d73ffb052 | ||
|
|
75869a561f | ||
|
|
215d773fcf | ||
|
|
03b28598cc | ||
|
|
df2d2ec5e9 | ||
|
|
0feb513407 | ||
|
|
7386fd2e21 | ||
|
|
fc0c995cb6 | ||
|
|
06680512a8 | ||
|
|
e3e72aec83 | ||
|
|
26eef2f000 | ||
|
|
9bbb62877e | ||
|
|
cabfe33e58 | ||
|
|
a4e1fb3c24 | ||
|
|
0ebbae94fe | ||
|
|
440c799c4c | ||
|
|
3de499063c | ||
|
|
3c4e0d0bae | ||
|
|
0a0e84b8e8 | ||
|
|
477146e5bf | ||
|
|
4e37eeb524 | ||
|
|
4d568401a3 | ||
|
|
535a3b0486 | ||
|
|
5892700b5c | ||
|
|
59ee05c606 | ||
|
|
f2d1387971 | ||
|
|
02e48810ba | ||
|
|
0ddd142279 | ||
|
|
e669a0d4ac | ||
|
|
d1b98be182 | ||
|
|
b171d3c48a | ||
|
|
adf94ffbf6 | ||
|
|
68807cd485 | ||
|
|
ebbd2d89b4 | ||
|
|
53db9d4a64 | ||
|
|
32e0470613 | ||
|
|
ca23e5ccdc | ||
|
|
0c41f3c213 | ||
|
|
c091fae675 | ||
|
|
e307f1dbbb | ||
|
|
c364a2329b | ||
|
|
88fcba6798 | ||
|
|
b90436e48e | ||
|
|
dc7a7dce41 | ||
|
|
6e7c428315 | ||
|
|
fa0115eb57 | ||
|
|
8eb3ce6237 | ||
|
|
bb83688fc7 | ||
|
|
5b3dd91fff | ||
|
|
3c5231fbd0 | ||
|
|
8ea97c7f77 | ||
|
|
5bc89ce207 | ||
|
|
b7a4296978 | ||
|
|
3321fd1616 | ||
|
|
7defe86bf6 | ||
|
|
35598a4e16 | ||
|
|
d84692b7b9 | ||
|
|
1c9d74a75b | ||
|
|
d744b8a22f | ||
|
|
730ee46de9 | ||
|
|
a406646361 | ||
|
|
c1502d0476 | ||
|
|
446f9341b9 | ||
|
|
6d7ba45f2c | ||
|
|
24729b9308 | ||
|
|
bc9b1263be | ||
|
|
9331e3b9db | ||
|
|
27e0db7d14 | ||
|
|
42cb37cc9e | ||
|
|
5b306d8134 | ||
|
|
b39deb560f | ||
|
|
198484ef2b | ||
|
|
d3ab3cc4b7 | ||
|
|
ca40fed573 | ||
|
|
bdd05b3c0c | ||
|
|
7c6594c595 | ||
|
|
c404bc02df | ||
|
|
5acbe01c3d | ||
|
|
e2b70aff42 | ||
|
|
834119ecd8 | ||
|
|
26d47b06a0 | ||
|
|
dc33dfe0a7 | ||
|
|
81b9110cad | ||
|
|
052ac6db37 | ||
|
|
f10e324f5f | ||
|
|
6a22d7c11d | ||
|
|
35a5d35512 | ||
|
|
04c0abbcfa | ||
|
|
e5658f498a | ||
|
|
2e9e2b65f8 | ||
|
|
91ff8a738a | ||
|
|
20f303f3d1 | ||
|
|
a250c6dbc2 | ||
|
|
489fb16b94 | ||
|
|
d72f08d936 | ||
|
|
e7a0552edc | ||
|
|
916412b51b | ||
|
|
6155c542a3 | ||
|
|
8aa611f77a | ||
|
|
84b7705fd1 | ||
|
|
2d75b7a04d | ||
|
|
dac9cff8fc | ||
|
|
b15573155e | ||
|
|
e641bd7d4a | ||
|
|
d562c8f71e | ||
|
|
0a78bb259f | ||
|
|
a4e05ed506 | ||
|
|
82b8ca4616 | ||
|
|
63429f1e9a | ||
|
|
1030376e37 | ||
|
|
f7e6117e85 | ||
|
|
38ad55851a | ||
|
|
7ae21ae972 | ||
|
|
fd80b240bd | ||
|
|
28f442e529 | ||
|
|
7ba798c51d | ||
|
|
5cc9188e3c | ||
|
|
9e84ebf28c | ||
|
|
541998da39 | ||
|
|
bc79a655e5 | ||
|
|
6c077ba873 | ||
|
|
7edf6c803d | ||
|
|
a1e673e599 | ||
|
|
f656a9aa6d | ||
|
|
9a707a0172 |
No files matched your search
@@ -0,0 +1,9 @@
|
||||
If this is a question or feature request, make sure to:
|
||||
|
||||
- [ ] The title starts with `Question:` or `Feature:`.
|
||||
|
||||
If this is an bug report, not a question, make sure to:
|
||||
|
||||
- [ ] I'm not running Windows (which is unsupported), or if I am, I can confirm this issue appears on another platform, or Vagrant.
|
||||
- [ ] This issue appears in the latest `dev` branch.
|
||||
- [ ] I've included my browser and Ruby version in this issue.
|
||||
@@ -0,0 +1,5 @@
|
||||
!!!!! STOP AND READ !!!!!
|
||||
|
||||
If the dropdown above says "base fork: lord/master", you are submitting your change to ALL USERS OF SLATE, not just your company. This is probably not what you want. Click "base fork" to change it to the right place.
|
||||
|
||||
If you're actually trying to submit a change to upstream Slate, please submit to our dev branch, PRs sent to the master branch are generally rejected.
|
||||
+23
-4
@@ -1,6 +1,25 @@
|
||||
.classpath
|
||||
.project
|
||||
.settings/
|
||||
target/
|
||||
*.gem
|
||||
*.rbc
|
||||
.bundle
|
||||
.config
|
||||
coverage
|
||||
InstalledFiles
|
||||
lib/bundler/man
|
||||
pkg
|
||||
rdoc
|
||||
spec/reports
|
||||
test/tmp
|
||||
test/version_tmp
|
||||
tmp
|
||||
*.DS_STORE
|
||||
build/
|
||||
.cache
|
||||
.vagrant
|
||||
.sass-cache
|
||||
|
||||
# YARD artifacts
|
||||
.yardoc
|
||||
_yardoc
|
||||
doc/
|
||||
.idea/
|
||||
*.iml
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
sudo: false
|
||||
|
||||
language: ruby
|
||||
|
||||
rvm:
|
||||
- 2.2.5
|
||||
- 2.3.1
|
||||
|
||||
cache: bundler
|
||||
script: bundle exec middleman build
|
||||
@@ -0,0 +1,88 @@
|
||||
# Changelog
|
||||
|
||||
## Version 1.4.0
|
||||
|
||||
*November 24, 2016*
|
||||
|
||||
- Upgrade Middleman and Rouge gems, should hopefully solve a number of bugs
|
||||
- Update some links in README
|
||||
- Fix broken Vagrant startup script
|
||||
- Fix some problems with deploy.sh help message
|
||||
- Fix bug with language tabs not hiding properly if no error
|
||||
- Add `!default` to SASS variables
|
||||
- Fix bug with logo margin
|
||||
- Bump tested Ruby versions in .travis.yml
|
||||
|
||||
## Version 1.3.3
|
||||
|
||||
*June 11, 2016*
|
||||
|
||||
Documentation and example changes.
|
||||
|
||||
## Version 1.3.2
|
||||
|
||||
*February 3, 2016*
|
||||
|
||||
A small bugfix for slightly incorrect background colors on code samples in some cases.
|
||||
|
||||
## Version 1.3.1
|
||||
|
||||
*January 31, 2016*
|
||||
|
||||
A small bugfix for incorrect whitespace in code blocks.
|
||||
|
||||
## Version 1.3
|
||||
|
||||
*January 27, 2016*
|
||||
|
||||
We've upgraded Middleman and a number of other dependencies, which should fix quite a few bugs.
|
||||
|
||||
Instead of `rake build` and `rake deploy`, you should now run `bundle exec middleman build --clean` to build your server, and `./deploy.sh` to deploy it to Github Pages.
|
||||
|
||||
## Version 1.2
|
||||
|
||||
*June 20, 2015*
|
||||
|
||||
**Fixes:**
|
||||
|
||||
- Remove crash on invalid languages
|
||||
- Update Tocify to scroll to the highlighted header in the Table of Contents
|
||||
- Fix variable leak and update search algorithms
|
||||
- Update Python examples to be valid Python
|
||||
- Update gems
|
||||
- More misc. bugfixes of Javascript errors
|
||||
- Add Dockerfile
|
||||
- Remove unused gems
|
||||
- Optimize images, fonts, and generated asset files
|
||||
- Add chinese font support
|
||||
- Remove RedCarpet header ID patch
|
||||
- Update language tabs to not disturb existing query strings
|
||||
|
||||
## Version 1.1
|
||||
|
||||
*July 27, 2014*
|
||||
|
||||
**Fixes:**
|
||||
|
||||
- Finally, a fix for the redcarpet upgrade bug
|
||||
|
||||
## Version 1.0
|
||||
|
||||
*July 2, 2014*
|
||||
|
||||
[View Issues](https://github.com/tripit/slate/issues?milestone=1&state=closed)
|
||||
|
||||
**Features:**
|
||||
|
||||
- Responsive designs for phones and tablets
|
||||
- Started tagging versions
|
||||
|
||||
**Fixes:**
|
||||
|
||||
- Fixed 'unrecognized expression' error
|
||||
- Fixed #undefined hash bug
|
||||
- Fixed bug where the current language tab would be unselected
|
||||
- Fixed bug where tocify wouldn't highlight the current section while searching
|
||||
- Fixed bug where ids of header tags would have special characters that caused problems
|
||||
- Updated layout so that pages with disabled search wouldn't load search.js
|
||||
- Cleaned up Javascript
|
||||
@@ -1,5 +0,0 @@
|
||||
Dave Marion ([email protected])
|
||||
Eric Newton ([email protected])
|
||||
Jim Klucar ([email protected])
|
||||
Charlie Williams ([email protected])
|
||||
Drew Farris ([email protected])
|
||||
@@ -0,0 +1,9 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
# Middleman
|
||||
gem 'middleman', '~>4.1.0'
|
||||
gem 'middleman-syntax', '~> 3.0.0'
|
||||
gem 'middleman-autoprefixer', '~> 2.7.0'
|
||||
gem "middleman-sprockets", "~> 4.0.0"
|
||||
gem 'rouge', '~> 2.0.5'
|
||||
gem 'redcarpet', '~> 3.3.2'
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
activesupport (4.2.7.1)
|
||||
i18n (~> 0.7)
|
||||
json (~> 1.7, >= 1.7.7)
|
||||
minitest (~> 5.1)
|
||||
thread_safe (~> 0.3, >= 0.3.4)
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.4.0)
|
||||
autoprefixer-rails (6.4.1)
|
||||
execjs
|
||||
backports (3.6.8)
|
||||
coffee-script (2.4.1)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.10.0)
|
||||
compass-import-once (1.0.5)
|
||||
sass (>= 3.2, < 3.5)
|
||||
concurrent-ruby (1.0.2)
|
||||
contracts (0.13.0)
|
||||
dotenv (2.1.1)
|
||||
erubis (2.7.0)
|
||||
execjs (2.7.0)
|
||||
fast_blank (1.0.0)
|
||||
fastimage (2.0.0)
|
||||
addressable (~> 2)
|
||||
ffi (1.9.14)
|
||||
haml (4.0.7)
|
||||
tilt
|
||||
hamster (3.0.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
hashie (3.4.4)
|
||||
i18n (0.7.0)
|
||||
json (1.8.3)
|
||||
kramdown (1.12.0)
|
||||
listen (3.0.8)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
memoist (0.15.0)
|
||||
middleman (4.1.10)
|
||||
coffee-script (~> 2.2)
|
||||
compass-import-once (= 1.0.5)
|
||||
haml (>= 4.0.5)
|
||||
kramdown (~> 1.2)
|
||||
middleman-cli (= 4.1.10)
|
||||
middleman-core (= 4.1.10)
|
||||
sass (>= 3.4.0, < 4.0)
|
||||
middleman-autoprefixer (2.7.0)
|
||||
autoprefixer-rails (>= 6.3.1, < 7.0.0)
|
||||
middleman-core (>= 3.3.3)
|
||||
middleman-cli (4.1.10)
|
||||
thor (>= 0.17.0, < 2.0)
|
||||
middleman-core (4.1.10)
|
||||
activesupport (~> 4.2)
|
||||
addressable (~> 2.3)
|
||||
backports (~> 3.6)
|
||||
bundler (~> 1.1)
|
||||
contracts (~> 0.13.0)
|
||||
dotenv
|
||||
erubis
|
||||
execjs (~> 2.0)
|
||||
fast_blank
|
||||
fastimage (~> 2.0)
|
||||
hamster (~> 3.0)
|
||||
hashie (~> 3.4)
|
||||
i18n (~> 0.7.0)
|
||||
listen (~> 3.0.0)
|
||||
memoist (~> 0.14)
|
||||
padrino-helpers (~> 0.13.0)
|
||||
parallel
|
||||
rack (>= 1.4.5, < 2.0)
|
||||
sass (>= 3.4)
|
||||
servolux
|
||||
tilt (~> 1.4.1)
|
||||
uglifier (~> 3.0)
|
||||
middleman-sprockets (4.0.0)
|
||||
middleman-core (~> 4.0)
|
||||
sprockets (>= 3.0)
|
||||
middleman-syntax (3.0.0)
|
||||
middleman-core (>= 3.2)
|
||||
rouge (~> 2.0)
|
||||
minitest (5.9.0)
|
||||
padrino-helpers (0.13.3.1)
|
||||
i18n (~> 0.6, >= 0.6.7)
|
||||
padrino-support (= 0.13.3.1)
|
||||
tilt (>= 1.4.1, < 3)
|
||||
padrino-support (0.13.3.1)
|
||||
activesupport (>= 3.1)
|
||||
parallel (1.9.0)
|
||||
rack (1.6.4)
|
||||
rb-fsevent (0.9.7)
|
||||
rb-inotify (0.9.7)
|
||||
ffi (>= 0.5.0)
|
||||
redcarpet (3.3.4)
|
||||
rouge (2.0.5)
|
||||
sass (3.4.22)
|
||||
servolux (0.12.0)
|
||||
sprockets (3.7.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
rack (> 1, < 3)
|
||||
thor (0.19.1)
|
||||
thread_safe (0.3.5)
|
||||
tilt (1.4.1)
|
||||
tzinfo (1.2.2)
|
||||
thread_safe (~> 0.1)
|
||||
uglifier (3.0.2)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
middleman (~> 4.1.0)
|
||||
middleman-autoprefixer (~> 2.7.0)
|
||||
middleman-sprockets (~> 4.0.0)
|
||||
middleman-syntax (~> 3.0.0)
|
||||
redcarpet (~> 3.3.2)
|
||||
rouge (~> 2.0.5)
|
||||
|
||||
BUNDLED WITH
|
||||
1.12.5
|
||||
@@ -1,203 +1,13 @@
|
||||
Copyright 2008-2013 Concur Technologies, Inc.
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
not use this file except in compliance with the License. You may obtain
|
||||
a copy of the License at
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
@@ -1,180 +1,110 @@
|
||||
# Timely
|
||||
---
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/lord/img/master/logo-slate.png" alt="Slate: API Documentation Generator" width="226">
|
||||
<br>
|
||||
<a href="https://travis-ci.org/lord/slate"><img src="https://travis-ci.org/lord/slate.svg?branch=master" alt="Build Status"></a>
|
||||
</p>
|
||||
|
||||
Timely is a time series database application that provides secure access to time series data. Timely is written in Java and designed to work with [Apache Accumulo] (http://accumulo.apache.org/) and [Grafana] (http://www.grafana.org).
|
||||
<p align="center">Slate helps you create beautiful, intelligent, responsive API documentation.</p>
|
||||
|
||||
# Design
|
||||
---
|
||||
<p align="center"><img src="https://dl.dropboxusercontent.com/u/95847291/github%20images/slate/slate_screenshot_new.png" width=700 alt="Screenshot of Example Documentation created with Slate"></p>
|
||||
|
||||
Being big users of Apache Accumulo, when we required a time series database application we looked to use software that was compatible with it. Specifically, we started with [OpenTSDB] (http://www.opentsdb.net) and Eric Newton's [Accumulo-OpenTSDB] (https://github.com/ericnewton/accumulo-opentsdb) project to bridge them together and Grafana for visualization. We ultimately ran into some issues that we could not work around. After much discussion we decided to replace OpenTSDB with something more native to Apache Accumulo. The implementation of Timely started with the following constraints and assumptions:
|
||||
<p align="center"><em>The example above was created with Slate. Check it out at <a href="https://lord.github.io/slate">lord.github.io/slate</a>.</em></p>
|
||||
|
||||
1. We need to show progress quickly.
|
||||
2. We started with Grafana, so we didn't need our own user interface.
|
||||
3. We already had experience with OpenTSDB, so we started with its API.
|
||||
4. Space is cheap, ingest and query speed are more important.
|
||||
5. Metrics will be kept short term.
|
||||
6. The number of tags used for each metric will be small \( < 10 \).
|
||||
7. We can create a better API and Grafana plugin at a later time.
|
||||
Features
|
||||
------------
|
||||
|
||||
## Building
|
||||
* **Clean, intuitive design** — With Slate, the description of your API is on the left side of your documentation, and all the code examples are on the right side. Inspired by [Stripe's](https://stripe.com/docs/api) and [Paypal's](https://developer.paypal.com/webapps/developer/docs/api/) API docs. Slate is responsive, so it looks great on tablets, phones, and even in print.
|
||||
|
||||
Timely uses Maven for compiling source, running tests, and packaging distributions. Below is a set of useful Maven lifecyles that we use:
|
||||
|
||||
Command | Description
|
||||
--------|------------
|
||||
mvn compile | Compiles and formats the source
|
||||
mvn test | Runs unit tests
|
||||
mvn package | Runs findbugs and creates a distribution
|
||||
mvn verify | Runs integration tests
|
||||
mvn verify site | Creates the site
|
||||
|
||||
The [CollectD] (http://collectd.org/) plugins require that CollectD is installed locally as the `/usr/share/collectd/java/collectd-api.jar` file is a dependency.
|
||||
* **Everything on a single page** — Gone are the days when your users had to search through a million pages to find what they wanted. Slate puts the entire documentation on a single page. We haven't sacrificed linkability, though. As you scroll, your browser's hash will update to the nearest header, so linking to a particular point in the documentation is still natural and easy.
|
||||
|
||||
## Deployment
|
||||
* **Slate is just Markdown** — When you write docs with Slate, you're just writing Markdown, which makes it simple to edit and understand. Everything is written in Markdown — even the code samples are just Markdown code blocks.
|
||||
|
||||
The Timely server requires a Java 8 runtime. Timely utilizes iterators for Apache Accumulo, so your Accumulo instance will need to be run with Java 8 also.
|
||||
* **Write code samples in multiple languages** — If your API has bindings in multiple programming languages, you can easily put in tabs to switch between them. In your document, you'll distinguish different languages by specifying the language name at the top of each code block, just like with Github Flavored Markdown.
|
||||
|
||||
Create a distribution and untar it somewhere. Modify the `conf/timely.properties` file appropriately. Then, copy the `timely-server` and `commons-lang3` jar files to your Accumulo tservers. Next, launch Timely by running the `bin/timely-server.sh` script.
|
||||
* **Out-of-the-box syntax highlighting** for [over 100 languages](https://github.com/jneen/rouge/wiki/List-of-supported-languages-and-lexers), no configuration required.
|
||||
|
||||
If you just want to kick the tires without having to install and setup Apache Hadoop and Apache Accumulo, then you can start Timely with the `bin/timely-standalone.sh` script. This will start an Accumulo MiniCluster in the background, but be aware that the standalone instance will not save your metric data across restarts.
|
||||
* **Automatic, smoothly scrolling table of contents** on the far left of the page. As you scroll, it displays your current position in the document. It's fast, too. We're using Slate at TripIt to build documentation for our new API, where our table of contents has over 180 entries. We've made sure that the performance remains excellent, even for larger documents.
|
||||
|
||||
## API
|
||||
* **Let your users update your documentation for you** — By default, your Slate-generated documentation is hosted in a public Github repository. Not only does this mean you get free hosting for your docs with Github Pages, but it also makes it simple for other developers to make pull requests to your docs if they find typos or other problems. Of course, if you don't want to use GitHub, you're also welcome to host your docs elsewhere.
|
||||
|
||||
The current Timely API is compatible with a subset of the OpenTSDB API. We currently support the OpenTSDB TCP put API and we have partially implemented the following HTTP operations that the Grafana OpenTSDB datasource uses:
|
||||
Getting started with Slate is super easy! Simply fork this repository and follow the instructions below. Or, if you'd like to check out what Slate is capable of, take a look at the [sample docs](http://lord.github.io/slate).
|
||||
|
||||
* /api/aggregators
|
||||
* /api/query
|
||||
* /api/search/lookup
|
||||
* /api/suggest
|
||||
Getting Started with Slate
|
||||
------------------------------
|
||||
|
||||
We also provide the following additional endpoints:
|
||||
### Prerequisites
|
||||
|
||||
* /login
|
||||
You're going to need:
|
||||
|
||||
This endpoint should be used for non-anonymous access. It supports a POST request with username and password, or a GET request with SSL mutual authentication.
|
||||
- **Linux or OS X** — Windows may work, but is unsupported.
|
||||
- **Ruby, version 2.2.5 or newer**
|
||||
- **Bundler** — If Ruby is already installed, but the `bundle` command doesn't work, just run `gem install bundler` in a terminal.
|
||||
|
||||
* /api/metrics
|
||||
### Getting Set Up
|
||||
|
||||
This endpoint reports the metric names and tags that have been pushed to Timely. Eviction of this information is configurable, so it may reflect metrics that have been reported in the past but are no longer being actively reported. This could be useful in creating graphs in Grafana (so you won't have to go digging in the `meta` table in Accumulo).
|
||||
1. Fork this repository on Github.
|
||||
2. Clone *your forked repository* (not our original one) to your hard drive with `git clone https://github.com/YOURUSERNAME/slate.git`
|
||||
3. `cd slate`
|
||||
4. Initialize and start Slate. You can either do this locally, or with Vagrant:
|
||||
|
||||
## Storage Format
|
||||
```shell
|
||||
# either run this to run locally
|
||||
bundle install
|
||||
bundle exec middleman server
|
||||
|
||||
Metrics sent to Timely are stored in two Accumulo tables, `meta` and `metrics`. The `meta` table stores information about the metric names and tags. It's format is:
|
||||
|
||||
Row | ColumnFamily | ColumnQualifier | Value
|
||||
----|:-------------|:----------------|:-----
|
||||
m:metric | | |
|
||||
t:metric | tagKey | |
|
||||
v:metric | tagKey | tagValue |
|
||||
|
||||
The `metrics` table stores the datapoints using the following format:
|
||||
|
||||
Row | ColumnFamily | ColumnQualifier | Value
|
||||
----|:-------------|:----------------|:-----
|
||||
metric\timestamp | tagKey=tagValue | tagKey=tagValue,tagKey=tagValue,... | metricValue
|
||||
|
||||
As an example, if you sent the following metric to the put api
|
||||
|
||||
```
|
||||
put sys.cpu.user 1447879348291 2.0 rack=r001 host=r001n01 instance=0
|
||||
# OR run this to run with vagrant
|
||||
vagrant up
|
||||
```
|
||||
|
||||
it would get stored in the following manner in the `meta` table:
|
||||
You can now see the docs at http://localhost:4567. Whoa! That was fast!
|
||||
|
||||
Row | ColumnFamily | ColumnQualifier | Value
|
||||
----|:-------------|:----------------|:-----
|
||||
m:sys.cpu.user | | |
|
||||
t:sys.cpu.user | host | |
|
||||
t:sys.cpu.user | instance | |
|
||||
t:sys.cpu.user | rack | |
|
||||
v:sys.cpu.user | host | r001n01 |
|
||||
v:sys.cpu.user | instance | 0 |
|
||||
v:sys.cpu.user | rack | r001 |
|
||||
Now that Slate is all set up on your machine, you'll probably want to learn more about [editing Slate markdown](https://github.com/lord/slate/wiki/Markdown-Syntax), or [how to publish your docs](https://github.com/lord/slate/wiki/Deploying-Slate).
|
||||
|
||||
and in the following manner in the `metrics` table
|
||||
If you'd prefer to use Docker, instructions are available [in the wiki](https://github.com/lord/slate/wiki/Docker).
|
||||
|
||||
Row | ColumnFamily | ColumnQualifier | Value
|
||||
----|:-------------|:----------------|:-----
|
||||
sys.cpu.user\1447879348291 | host=r001n01 | instance=0,rack=r001 | 2.0
|
||||
sys.cpu.user\1447879348291 | instance=0 | host=r001n01,rack=r001 | 2.0
|
||||
sys.cpu.user\1447879348291 | rack=r001 | host=r001n01,instance=0 | 2.0
|
||||
Companies Using Slate
|
||||
---------------------------------
|
||||
|
||||
## Configuration
|
||||
* [NASA](https://api.nasa.gov)
|
||||
* [IBM](https://docs.cloudant.com/api.html)
|
||||
* [Sony](http://developers.cimediacloud.com)
|
||||
* [Mozilla](http://mozilla.github.io/localForage/)
|
||||
* [Best Buy](https://bestbuyapis.github.io/api-documentation/)
|
||||
* [Travis-CI](https://docs.travis-ci.com/api/)
|
||||
* [Greenhouse](https://developers.greenhouse.io/harvest.html)
|
||||
* [Woocommerce](http://woothemes.github.io/woocommerce-rest-api-docs/)
|
||||
* [Appium](http://appium.io/slate/en/master)
|
||||
* [Dwolla](https://docs.dwolla.com/)
|
||||
* [Clearbit](https://clearbit.com/docs)
|
||||
* [Coinbase](https://developers.coinbase.com/api)
|
||||
* [Parrot Drones](http://developer.parrot.com/docs/bebop/)
|
||||
* [Fidor Bank](http://docs.fidor.de/)
|
||||
|
||||
The `NUM_SERVER_THREADS` variable in the `timely-server.sh` script controls how many threads are used in the Netty event group for TCP and HTTP operations. The TCP and HTTP groups use a different event group, so if you set the value to 8, then you will have 8 threads for TCP operations and 8 threads for HTTP operations. The properties file in the `conf` directory supports the following properties:
|
||||
You can view more in [the list on the wiki](https://github.com/lord/slate/wiki/Slate-in-the-Wild).
|
||||
|
||||
Property | Description | Default Value
|
||||
:--------|:------------|:-------------
|
||||
timely.ip | The ip address where the Timely server is running |
|
||||
timely.port.put | The port that will be used for processing put requests |
|
||||
timely.port.query | The port that will be used for processing query requests |
|
||||
timely.instance\_name | The name of your Accumulo instance |
|
||||
timely.zookeepers | The list of Zookeepers for your Accumulo instance |
|
||||
timely.username | The username that Timely will use to connect to Accumulo |
|
||||
timely.password | The password of the Accumulo user |
|
||||
timely.table | The name of the metrics table | timely.metrics
|
||||
timely.meta | The name of the meta table | timely.meta
|
||||
timely.write.latency | The Accumulo BatchWriter latency | 5s
|
||||
timely.write.threads | The Accumulo BatchWriter number of threads | [default](https://github.com/apache/accumulo/blob/master/core/src/main/java/org/apache/accumulo/core/client/BatchWriterConfig.java#L49)
|
||||
timely.write.buffer.size | The Accumulo BatchWriter buffer size | [default](https://github.com/apache/accumulo/blob/master/core/src/main/java/org/apache/accumulo/core/client/BatchWriterConfig.java#L40)
|
||||
timely.metric.age.off.days | The number of days to keep metrics | 7
|
||||
timely.cors.allow.any.origin | Allow any origin in cross origin requests (true/false) | false
|
||||
timely.cors.allow.null.origin | Allow null origins in cross origin requests (true/false) | false
|
||||
timely.cors.allowed.origins | List of allowed origins in cross origin requests (can be null or comma separated list)
|
||||
timely.cors.allowed.methods | List of allowed methods for cross origin requests | <ul><li>DELETE</li><li>GET</li><li>HEAD</li><li>OPTIONS</li><li>PUT</li><li>POST</li></ul>
|
||||
timely.cors.allowed.headers | Comma separated list of allowed HTTP headers for cross origin requests | content-type
|
||||
timely.cors.allow.credentials | Allow credentials to be passed in cross origin requests (true/false) | true
|
||||
timely.scanner.threads | Number of BatchScanner threads to be used in a query | 4
|
||||
timely.metrics.report.tags.ignored | Comma separated list of tags which will not be shown in the /api/metrics response |
|
||||
timely.meta.cache.expiration.minutes | Number of minutes after which unaccessed meta information will be purged from the meta cache | 60
|
||||
timely.meta.cache.initial.capacity | Initial capacity of the meta cache | 2000
|
||||
timely.meta.cache.max.capacity | Maximum capacity of the meta cache | 10000
|
||||
timely.ssl.certificate.file | Public certificate to use for the Timely server (x509 pem format) |
|
||||
timely.ssl.key.file | Private key to use for the Timely server (in pkcs8 format) |
|
||||
timely.ssl.key.pass | Password to the private key |
|
||||
timely.ssl.use.generated.keypair | Use a generated certificate/key pair - useful for testing | false
|
||||
timely.ssl.trust.store.file | Certificate trust store (a concatenated list of trusted CA x509 pem certificates) |
|
||||
timely.ssl.use.openssl | Use OpenSSL (vs JDK SSL) | true
|
||||
timely.ssl.use.ciphers | List of allowed SSL ciphers | see Configuration.java
|
||||
timely.session.max.age | Setting for max age of session cookie (in seconds) | 86400
|
||||
timely.http.address | Address for the Timely server, used for the session cookie domain |
|
||||
grafana.http.address | Address for the Grafana server |
|
||||
timely.allow.anonymous.access | Allow anonymous access | false
|
||||
timely.visibility.cache.expiration.minutes | Column Visibility Cache Expiration (minutes) | 60
|
||||
timely.visibility.cache.initial.capacity | Column Visibility Cache Initial Capacity | 2000
|
||||
timely.visibility.cache.max.capacity | Column Visibility Cache Max Capacity | 10000
|
||||
Need Help? Found a bug?
|
||||
--------------------
|
||||
|
||||
## Security
|
||||
[Submit an issue](https://github.com/lord/slate/issues) to the Slate Github if you need any help. And, of course, feel free to submit pull requests with bug fixes or changes.
|
||||
|
||||
Timely allows users to optionally label their data using Accumulo column visibility expressions. To enable this feature, users should put the expressions in a tag named `viz`. Timely will [flatten] (http://accumulo.apache.org/1.7/apidocs/org/apache/accumulo/core/security/ColumnVisibility.html#flatten%28%29) this expression and store it in the column visibility of the data point in the metrics table. Column visibilities are *not* stored in the meta table, so anyone can see metric names, tag names, and tag values.
|
||||
Contributors
|
||||
--------------------
|
||||
|
||||
Timely provides HTTPS access to the query endpoints. It is possible to allow anonymous access to these endpoints which will allow anyone to see unlabeled data. Timely uses Spring Security to configure user authentication and user role information. Users must call the `/login` endpoint for authentication and Timely will respond by setting a HTTP cookie with a session id. The response to a successful login will be a temporary redirect (HTTP 307) to the configured address for the Grafana server. Grafana itself must be configured to use https for this to work properly. For more information see the [Quick Start] (quick-start/QUICK_START.md) documentation.
|
||||
Slate was built by [Robert Lord](https://lord.io) while interning at [TripIt](https://www.tripit.com/).
|
||||
|
||||
## Tuning
|
||||
Thanks to the following people who have submitted major pull requests:
|
||||
|
||||
1. Each thread in the Timely server that is used for processing TCP put operations has its own BatchWriter. Each BatchWriter honors the `timely.write.latency` and `timely.write.threads` configuration property, but the buffer size for each BatchWriter is `timely.write.buffer.size` divided by the number of threads. For example, if you have 8 threads processing put operations and the following settings, then you will have 8 BatchWriters each using 2 threads with a 30s latency and a maximum buffer size of 128M:
|
||||
- [@chrissrogers](https://github.com/chrissrogers)
|
||||
- [@bootstraponline](https://github.com/bootstraponline)
|
||||
- [@realityking](https://github.com/realityking)
|
||||
- [@cvkef](https://github.com/cvkef)
|
||||
|
||||
```
|
||||
timely.write.latency=30s
|
||||
timely.write.threads=2
|
||||
timely.write.buffer.size=1G
|
||||
```
|
||||
|
||||
2. The `timely.scanner.threads` property is used for BatchScanners on a per query basis. If you set this to 32 and have 8 threads processing HTTP operations, then you might have 256 threads concurrently querying your tablet servers. Be sure to set your ulimits appropriately.
|
||||
|
||||
3. The Timely server contains an object called the meta cache, which is a cache of the keys that would be inserted into the meta table if they did not already exist. This cache is used to reduce the insert load of duplicate keys into the meta table and to serve up data to the `/api/metrics` endpoint. The meta cache is an object that supports eviction based on last access time and can be tuned with the `timely.meta.cache.*` properties.
|
||||
|
||||
|
||||
## NOTES
|
||||
|
||||
1. Not shown in the examples is the encoding of the row and value. You can apply the Timely formatter to your metrics table using the Accumulo shell command:
|
||||
```
|
||||
config -t timely.metrics -s table.formatter=timely.util.TimelyMetricsFormatter
|
||||
```
|
||||
|
||||
2. You can create split points for your metrics table based on the 'm' column family in the meta table.
|
||||
|
||||
3. You can lower the `table.scan.max.memory` property on your metrics table in an attempt to get data back faster from the tablet servers.
|
||||
|
||||
4. If you don't mind losing some metric data in the event of an Accumulo tablet server death, you can set the `table.walog.enabled` property to false and the `table.durability` property to none on your metrics table. This should speed up ingest a little.
|
||||
Also, thanks to [Sauce Labs](http://saucelabs.com) for helping sponsor the project.
|
||||
|
||||
Special Thanks
|
||||
--------------------
|
||||
- [Middleman](https://github.com/middleman/middleman)
|
||||
- [jquery.tocify.js](https://github.com/gfranko/jquery.tocify.js)
|
||||
- [middleman-syntax](https://github.com/middleman/middleman-syntax)
|
||||
- [middleman-gh-pages](https://github.com/edgecase/middleman-gh-pages)
|
||||
- [Font Awesome](http://fortawesome.github.io/Font-Awesome/)
|
||||
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
Vagrant.configure(2) do |config|
|
||||
config.vm.box = "ubuntu/trusty64"
|
||||
config.vm.network :forwarded_port, guest: 4567, host: 4567
|
||||
|
||||
config.vm.provision "bootstrap",
|
||||
type: "shell",
|
||||
inline: <<-SHELL
|
||||
sudo apt-get update
|
||||
sudo apt-get install -yq ruby2.0 ruby2.0-dev pkg-config build-essential nodejs git libxml2-dev libxslt-dev
|
||||
sudo apt-get autoremove -yq
|
||||
gem2.0 install --no-ri --no-rdoc bundler
|
||||
SHELL
|
||||
|
||||
# add the local user git config to the vm
|
||||
config.vm.provision "file", source: "~/.gitconfig", destination: ".gitconfig"
|
||||
|
||||
config.vm.provision "install",
|
||||
type: "shell",
|
||||
privileged: false,
|
||||
inline: <<-SHELL
|
||||
echo "=============================================="
|
||||
echo "Installing app dependencies"
|
||||
cd /vagrant
|
||||
bundle config build.nokogiri --use-system-libraries
|
||||
bundle install
|
||||
SHELL
|
||||
|
||||
config.vm.provision "run",
|
||||
type: "shell",
|
||||
privileged: false,
|
||||
run: "always",
|
||||
inline: <<-SHELL
|
||||
echo "=============================================="
|
||||
echo "Starting up middleman at http://localhost:4567"
|
||||
echo "If it does not come up, check the ~/middleman.log file for any error messages"
|
||||
cd /vagrant
|
||||
bundle exec middleman server --watcher-force-polling --watcher_latency=1 &> ~/middleman.log &
|
||||
SHELL
|
||||
end
|
||||
@@ -1,19 +0,0 @@
|
||||
# Write NSQ plugin for CollectD
|
||||
|
||||
To build, run ```mvn clean package```. Place the resulting jar file somewhere on the local filesystem and add the following to the
|
||||
collectd configuration file
|
||||
|
||||
```
|
||||
LoadPlugin java
|
||||
<Plugin java>
|
||||
JVMArg "-verbose:jni"
|
||||
JVMArg "-Djava.class.path=/usr/share/collectd/java/collectd-api.jar:<path_to_jar>/collectd-nsq-plugin-0.0.1-SNAPSHOT.jar"
|
||||
JVMArg "-Xms1024m"
|
||||
JVMArg "-Xmx1024m"
|
||||
LoadPlugin "timely.collectd.plugin.WriteNSQPlugin"
|
||||
<Plugin "timely.collectd.plugin.WriteNSQPlugin">
|
||||
Host "<NSQHost[,NSQHost]>"
|
||||
Port "<NSQHttpPort>"
|
||||
</Plugin>
|
||||
</Plugin>
|
||||
```
|
||||
@@ -1,129 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.1</version>
|
||||
</parent>
|
||||
<artifactId>collectd-nsq-plugin</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>collectd</groupId>
|
||||
<artifactId>collectd-api</artifactId>
|
||||
<version>5.5.1</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${collectd-api-jar}</systemPath>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>collectd-plugin-parent</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<effort>Max</effort>
|
||||
<threshold>Medium</threshold>
|
||||
<xmlOutput>true</xmlOutput>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
<phase>prepare-package</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.4.2</version>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>shade</id>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<phase>package</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>net.revelc.code</groupId>
|
||||
<artifactId>formatter-maven-plugin</artifactId>
|
||||
<version>0.5.2</version>
|
||||
<configuration>
|
||||
<configFile>${project.parent.basedir}/eclipse/Eclipse-Formatter.xml</configFile>
|
||||
<lineEnding>LF</lineEnding>
|
||||
<overrideConfigCompilerVersion>true</overrideConfigCompilerVersion>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>format</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
|
||||
<plugin>
|
||||
<groupId>org.eclipse.m2e</groupId>
|
||||
<artifactId>lifecycle-mapping</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<configuration>
|
||||
<lifecycleMappingMetadata>
|
||||
<pluginExecutions>
|
||||
<pluginExecution>
|
||||
<pluginExecutionFilter>
|
||||
<groupId>net.revelc.code</groupId>
|
||||
<artifactId>
|
||||
formatter-maven-plugin
|
||||
</artifactId>
|
||||
<versionRange>
|
||||
[0.5.2,)
|
||||
</versionRange>
|
||||
<goals>
|
||||
<goal>format</goal>
|
||||
</goals>
|
||||
</pluginExecutionFilter>
|
||||
<action>
|
||||
<ignore/>
|
||||
</action>
|
||||
</pluginExecution>
|
||||
</pluginExecutions>
|
||||
</lifecycleMappingMetadata>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,149 +0,0 @@
|
||||
package timely.collectd.plugin;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Random;
|
||||
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.entity.EntityBuilder;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.collectd.api.Collectd;
|
||||
import org.collectd.api.CollectdConfigInterface;
|
||||
import org.collectd.api.CollectdShutdownInterface;
|
||||
import org.collectd.api.CollectdWriteInterface;
|
||||
import org.collectd.api.OConfigItem;
|
||||
import org.collectd.api.ValueList;
|
||||
|
||||
/**
|
||||
*
|
||||
* CollectD plugin that writes metrics collected by the StatsD plugin to NSQ.
|
||||
* NSQ can be configured to write statsd metrics to CollectD. If you do this,
|
||||
* set the statsd prefix in NSQ to 'nsq'.
|
||||
*
|
||||
*/
|
||||
public class WriteNSQPlugin extends CollectDPluginParent implements CollectdConfigInterface, CollectdShutdownInterface,
|
||||
CollectdWriteInterface {
|
||||
|
||||
private String host = null;
|
||||
private int port = 0;
|
||||
private String topic = "metrics#ephemeral";
|
||||
private String endpoint = null;
|
||||
private ByteArrayOutputStream baos = null;
|
||||
private DataOutputStream out = null;
|
||||
|
||||
private CloseableHttpClient client = HttpClients.createDefault();
|
||||
|
||||
public WriteNSQPlugin() {
|
||||
Collectd.registerConfig(WriteNSQPlugin.class.getName(), this);
|
||||
Collectd.registerShutdown(WriteNSQPlugin.class.getName(), this);
|
||||
Collectd.registerWrite(WriteNSQPlugin.class.getName(), this);
|
||||
}
|
||||
|
||||
public synchronized int write(ValueList vl) {
|
||||
baos = new ByteArrayOutputStream();
|
||||
out = new DataOutputStream(baos);
|
||||
super.process(vl);
|
||||
try {
|
||||
out.flush();
|
||||
HttpPost post = new HttpPost(endpoint);
|
||||
EntityBuilder request = EntityBuilder.create();
|
||||
request.setBinary(baos.toByteArray());
|
||||
post.setEntity(request.build());
|
||||
CloseableHttpResponse response = client.execute(post);
|
||||
try {
|
||||
int code = response.getStatusLine().getStatusCode();
|
||||
String msg = response.getStatusLine().getReasonPhrase();
|
||||
// Consume the entire response so the connection will be reused
|
||||
EntityUtils.consume(response.getEntity());
|
||||
if (code != HttpStatus.SC_OK) {
|
||||
Collectd.logInfo("Code: " + code + ", msg: " + msg);
|
||||
}
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Collectd.logError("Error sending metrics to NSQ: " + e.getMessage());
|
||||
} finally {
|
||||
if (null != out) {
|
||||
try {
|
||||
out.close();
|
||||
out = null;
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(String metric) {
|
||||
try {
|
||||
out.writeBytes(metric);
|
||||
} catch (IOException e) {
|
||||
Collectd.logError("Error writing metric: " + e.getMessage());
|
||||
try {
|
||||
out.close(); // Should cause IOException in write.
|
||||
} catch (IOException e1) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
}
|
||||
|
||||
public synchronized int shutdown() {
|
||||
Collectd.logInfo("Shutting down connection to NSQ at " + host + ":" + port);
|
||||
try {
|
||||
client.close();
|
||||
} catch (IOException e) {
|
||||
Collectd.logError("Error closing HttpClient: " + e.getMessage());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public synchronized int config(OConfigItem config) {
|
||||
super.config(config);
|
||||
for (OConfigItem child : config.getChildren()) {
|
||||
switch (child.getKey()) {
|
||||
case "host":
|
||||
case "hostname":
|
||||
case "Host":
|
||||
case "HostName":
|
||||
String hostString = child.getValues().get(0).getString();
|
||||
String[] hosts = hostString.split(",");
|
||||
if (hosts.length == 1) {
|
||||
host = hosts[0];
|
||||
} else {
|
||||
host = hosts[new Random().nextInt(hosts.length)];
|
||||
}
|
||||
break;
|
||||
case "Port":
|
||||
case "port":
|
||||
port = Integer.parseInt(child.getValues().get(0).getString());
|
||||
break;
|
||||
case "topic":
|
||||
case "Topic":
|
||||
topic = child.getValues().get(0).getString();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
this.endpoint = "http://" + host + ":" + port + "/mpub?topic=" + topic;
|
||||
if (null != host) {
|
||||
return 0;
|
||||
}
|
||||
if (host == null) {
|
||||
Collectd.logError("NSQ host must be configured");
|
||||
}
|
||||
if (port == 0) {
|
||||
Collectd.logError("NSQ port must be configured");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
# CollectD Plugin
|
||||
|
||||
The CollectD plugin formats metrics acquired by the other plugins and sends them to Timely. The plugin contains code for parsing and extracting information from metrics reported by some of the plugins. One special case is the metrics received by the [StatsD] (https://github.com/etsy/statsd) plugin. It supports processing of [NSQ] (http://nsq.io/) metrics by looking for StatsD metrics with the prefix 'nsq'.
|
||||
|
||||
It also supports parsing of the StatsD metrics coming from the Hadoop Metrics2 framework (a metric with 4 components separated by periods). In this case you will want to set the skip hostname property to true and send the metrics from Hadoop to a local CollectD instance that will add the hostname and other tags.
|
||||
|
||||
The plugin allows for other unspecified formats to be sent as-is, except in the following case: if the StatsD metric contains a period, everything up to the period will be used in an instance tag.
|
||||
|
||||
For example:
|
||||
|
||||
"foo:1|c" will end up as the metric 'statsd.foo'
|
||||
|
||||
"bar.foo:1|c" will end up as the metric 'statsd.foo' with an 'instance=bar' tag.
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.1</version>
|
||||
</parent>
|
||||
<artifactId>collectd-plugin-parent</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>collectd</groupId>
|
||||
<artifactId>collectd-api</artifactId>
|
||||
<version>5.5.1</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${collectd-api-jar}</systemPath>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.jcip</groupId>
|
||||
<artifactId>jcip-annotations</artifactId>
|
||||
<version>1.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-easymock-release-full</artifactId>
|
||||
<version>1.6.4</version>
|
||||
<classifier>full</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<effort>Max</effort>
|
||||
<threshold>Medium</threshold>
|
||||
<xmlOutput>true</xmlOutput>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
<phase>prepare-package</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.19</version>
|
||||
<configuration>
|
||||
<excludedGroups>timely.test.IntegrationTest</excludedGroups>
|
||||
<redirectTestOutputToFile>true</redirectTestOutputToFile>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>net.revelc.code</groupId>
|
||||
<artifactId>formatter-maven-plugin</artifactId>
|
||||
<version>0.5.2</version>
|
||||
<configuration>
|
||||
<configFile>${project.parent.basedir}/eclipse/Eclipse-Formatter.xml</configFile>
|
||||
<lineEnding>LF</lineEnding>
|
||||
<overrideConfigCompilerVersion>true</overrideConfigCompilerVersion>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>format</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
|
||||
<plugin>
|
||||
<groupId>org.eclipse.m2e</groupId>
|
||||
<artifactId>lifecycle-mapping</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<configuration>
|
||||
<lifecycleMappingMetadata>
|
||||
<pluginExecutions>
|
||||
<pluginExecution>
|
||||
<pluginExecutionFilter>
|
||||
<groupId>net.revelc.code</groupId>
|
||||
<artifactId>
|
||||
formatter-maven-plugin
|
||||
</artifactId>
|
||||
<versionRange>
|
||||
[0.5.2,)
|
||||
</versionRange>
|
||||
<goals>
|
||||
<goal>format</goal>
|
||||
</goals>
|
||||
</pluginExecutionFilter>
|
||||
<action>
|
||||
<ignore/>
|
||||
</action>
|
||||
</pluginExecution>
|
||||
</pluginExecutions>
|
||||
</lifecycleMappingMetadata>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</project>
|
||||
-246
@@ -1,246 +0,0 @@
|
||||
package timely.collectd.plugin;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.collectd.api.Collectd;
|
||||
import org.collectd.api.DataSource;
|
||||
import org.collectd.api.OConfigItem;
|
||||
import org.collectd.api.ValueList;
|
||||
|
||||
public abstract class CollectDPluginParent {
|
||||
|
||||
private static final String PUT = "put {0} {1} {2}{3}\n";
|
||||
private static final Pattern HADOOP_STATSD_PATTERN = Pattern
|
||||
.compile("([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)");
|
||||
private static final String INSTANCE = " instance=";
|
||||
private static final String SAMPLE = " sample=";
|
||||
private static final String CODE = " code=";
|
||||
private static final String PERIOD = ".";
|
||||
private static final String SAMPLE_TYPE = " sampleType=";
|
||||
private static final String COUNTER = "COUNTER";
|
||||
private static final String GAUGE = "GAUGE";
|
||||
private static final String DERIVE = "DERIVE";
|
||||
private static final String ABSOLUTE = "ABSOLUTE";
|
||||
private static final Pattern NSQ_PATTERN1 = Pattern.compile("([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)");
|
||||
private static final Pattern NSQ_PATTERN2 = Pattern.compile("([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)");
|
||||
private static final Pattern NSQ_PATTERN3 = Pattern
|
||||
.compile("([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_#]+)\\.([\\w-_]+)");
|
||||
private static final Pattern ETHSTAT_PATTERN = Pattern.compile("([\\w-_]+)_queue_([\\w-_]+)_([\\w-_]+)");
|
||||
private static final String STATSD_PREFIX = "statsd";
|
||||
private static final String NSQ_PREFIX = STATSD_PREFIX + ".nsq.";
|
||||
private static final Pattern HAPROXY_PATTERN = Pattern.compile("([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)");
|
||||
|
||||
private final SMARTCodeMapping smart = new SMARTCodeMapping();
|
||||
|
||||
protected Set<String> addlTags = new HashSet<>();
|
||||
|
||||
public int config(OConfigItem config) {
|
||||
for (OConfigItem child : config.getChildren()) {
|
||||
switch (child.getKey()) {
|
||||
case "Tags":
|
||||
case "tags":
|
||||
String[] additionalTags = child.getValues().get(0).getString().split(",");
|
||||
for (String t : additionalTags) {
|
||||
addlTags.add(" " + t.trim());
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public abstract void write(String metric);
|
||||
|
||||
public void process(ValueList vl) {
|
||||
debugValueList(vl);
|
||||
|
||||
StringBuilder metric = new StringBuilder();
|
||||
Long timestamp = vl.getTime();
|
||||
StringBuilder tags = new StringBuilder();
|
||||
String host = vl.getHost();
|
||||
int idx = host.indexOf(PERIOD);
|
||||
if (-1 != idx) {
|
||||
tags.append(" host=").append(vl.getHost().substring(0, idx));
|
||||
}
|
||||
int nIdx = host.indexOf('n');
|
||||
if (-1 != nIdx) {
|
||||
tags.append(" rack=").append(host.substring(0, nIdx));
|
||||
}
|
||||
for (String tag : addlTags) {
|
||||
tags.append(tag);
|
||||
}
|
||||
if (vl.getPlugin().equals(STATSD_PREFIX)) {
|
||||
Matcher m = HADOOP_STATSD_PATTERN.matcher(vl.getTypeInstance());
|
||||
Matcher n1 = NSQ_PATTERN1.matcher(vl.getTypeInstance());
|
||||
Matcher n2 = NSQ_PATTERN2.matcher(vl.getTypeInstance());
|
||||
Matcher n3 = NSQ_PATTERN3.matcher(vl.getTypeInstance());
|
||||
String instance = null;
|
||||
if (m.matches() && !vl.getTypeInstance().startsWith("nsq")) {
|
||||
// Here we are processing the statsd metrics coming from the
|
||||
// Hadoop Metrics2 StatsDSink without the host name.
|
||||
// The format of metric is:
|
||||
// serviceName.contextName.recordName.metricName. The recordName
|
||||
// is typically duplicative and is dropped here. The serviceName
|
||||
// is used as the instance.
|
||||
metric.append(STATSD_PREFIX).append(PERIOD).append(m.group(2)).append(PERIOD).append(m.group(4));
|
||||
instance = m.group(1);
|
||||
} else if (n1.matches() && vl.getTypeInstance().startsWith("nsq")) {
|
||||
metric.append(NSQ_PREFIX).append(n1.group(2)).append(PERIOD).append(n1.group(3));
|
||||
} else if (n2.matches() && vl.getTypeInstance().startsWith("nsq")) {
|
||||
metric.append(NSQ_PREFIX).append(n2.group(2)).append(PERIOD).append(n2.group(4));
|
||||
instance = n2.group(3);
|
||||
} else if (n3.matches() && vl.getTypeInstance().startsWith("nsq")) {
|
||||
metric.append(NSQ_PREFIX).append(n3.group(4)).append(PERIOD).append(n3.group(6));
|
||||
instance = n3.group(5);
|
||||
} else {
|
||||
// Handle StatsD metrics of unknown formats. If there is a
|
||||
// period in the metric name, use everything up to that as
|
||||
// the instance.
|
||||
int period = vl.getTypeInstance().indexOf('.');
|
||||
if (-1 == period) {
|
||||
metric.append(STATSD_PREFIX).append(PERIOD).append(vl.getTypeInstance());
|
||||
} else {
|
||||
instance = vl.getTypeInstance().substring(0, period);
|
||||
metric.append(STATSD_PREFIX).append(PERIOD).append(vl.getTypeInstance().substring(period + 1));
|
||||
}
|
||||
}
|
||||
timestamp = vl.getTime();
|
||||
if (null != instance) {
|
||||
tags.append(INSTANCE).append(instance);
|
||||
}
|
||||
} else if (vl.getPlugin().equals("ethstat")) {
|
||||
metric.append("sys.ethstat.");
|
||||
if (vl.getTypeInstance().contains("queue")) {
|
||||
Matcher m = ETHSTAT_PATTERN.matcher(vl.getTypeInstance());
|
||||
if (m.matches()) {
|
||||
metric.append(m.group(1)).append("_").append(m.group(3));
|
||||
tags.append(" queue=").append(m.group(2));
|
||||
} else {
|
||||
metric.append(vl.getTypeInstance());
|
||||
}
|
||||
} else {
|
||||
metric.append(vl.getTypeInstance());
|
||||
}
|
||||
tags.append(INSTANCE).append(vl.getPluginInstance());
|
||||
} else if (vl.getPlugin().equals("hddtemp")) {
|
||||
metric.append("sys.hddtemp.").append(vl.getType());
|
||||
tags.append(INSTANCE).append(vl.getTypeInstance());
|
||||
} else if (vl.getPlugin().equals("smart")) {
|
||||
int code = -1;
|
||||
String name = null;
|
||||
if (vl.getTypeInstance().startsWith("attribute-")) {
|
||||
int hyphen = vl.getTypeInstance().indexOf('-');
|
||||
code = Integer.parseInt(vl.getTypeInstance().substring(hyphen + 1));
|
||||
name = smart.get(code);
|
||||
}
|
||||
if (code == -1) {
|
||||
if (notEmpty(vl.getTypeInstance())) {
|
||||
metric.append("sys.smart.").append(vl.getTypeInstance());
|
||||
} else {
|
||||
metric.append("sys.smart.").append(vl.getType());
|
||||
}
|
||||
} else {
|
||||
metric.append("sys.smart.").append(name);
|
||||
tags.append(CODE).append(code);
|
||||
}
|
||||
tags.append(INSTANCE).append(vl.getPluginInstance());
|
||||
} else if (vl.getPlugin().equals("sensors")) {
|
||||
String instance = "";
|
||||
if (vl.getTypeInstance().startsWith("temp")) {
|
||||
instance = vl.getTypeInstance().substring(4);
|
||||
}
|
||||
metric.append("sys.sensors.").append(vl.getType()).append(PERIOD).append(vl.getPluginInstance());
|
||||
tags.append(INSTANCE).append(instance);
|
||||
} else if (vl.getPlugin().equals("haproxy")) {
|
||||
Matcher m = HAPROXY_PATTERN.matcher(vl.getTypeInstance());
|
||||
if (m.matches()) {
|
||||
metric.append("sys.haproxy.").append(m.group(3));
|
||||
tags.append(" proxy=").append(m.group(2));
|
||||
tags.append(" server=").append(m.group(1));
|
||||
} else {
|
||||
metric.append("sys.haproxy.").append(vl.getTypeInstance());
|
||||
}
|
||||
} else if (vl.getPlugin().equals("ipmi")) {
|
||||
metric.append("sys.ipmi.").append(vl.getType());
|
||||
tags.append(INSTANCE).append(vl.getTypeInstance().replaceAll(" ", "_"));
|
||||
} else if (notEmpty(vl.getTypeInstance()) && notEmpty(vl.getType()) && notEmpty(vl.getPluginInstance())
|
||||
&& notEmpty(vl.getPlugin())) {
|
||||
metric.append("sys.").append(vl.getPlugin()).append(PERIOD).append(vl.getType()).append(PERIOD)
|
||||
.append(vl.getTypeInstance());
|
||||
tags.append(INSTANCE).append(vl.getPluginInstance());
|
||||
} else if (notEmpty(vl.getTypeInstance()) && notEmpty(vl.getType()) && notEmpty(vl.getPlugin())) {
|
||||
metric.append("sys.").append(vl.getPlugin()).append(PERIOD).append(vl.getType()).append(PERIOD)
|
||||
.append(vl.getTypeInstance());
|
||||
} else if (notEmpty(vl.getType()) && notEmpty(vl.getPluginInstance()) && notEmpty(vl.getPlugin())) {
|
||||
metric.append("sys.").append(vl.getPlugin()).append(PERIOD).append(vl.getType());
|
||||
tags.append(INSTANCE).append(vl.getPluginInstance());
|
||||
} else if (notEmpty(vl.getType()) && notEmpty(vl.getPlugin())) {
|
||||
metric.append("sys.").append(vl.getPlugin()).append(PERIOD).append(vl.getType());
|
||||
} else {
|
||||
Collectd.logWarning("Unhandled metric: " + vl.toString());
|
||||
return;
|
||||
}
|
||||
final String metricName = metric.toString().replaceAll(" ", "_");
|
||||
for (int i = 0; i < vl.getValues().size(); i++) {
|
||||
StringBuilder tagsWithSample = new StringBuilder(tags.toString());
|
||||
String sampleName = vl.getDataSet().getDataSources().get(i).getName();
|
||||
int type = vl.getDataSet().getDataSources().get(i).getType();
|
||||
String sampleType = convertType(type);
|
||||
if (null != sampleName) {
|
||||
tagsWithSample.append(SAMPLE).append(sampleName);
|
||||
}
|
||||
if (null != sampleType) {
|
||||
tagsWithSample.append(SAMPLE_TYPE).append(sampleType);
|
||||
}
|
||||
Double value = vl.getValues().get(i).doubleValue();
|
||||
String datapoint = MessageFormat.format(PUT, metricName, timestamp.toString(), value.toString(),
|
||||
tagsWithSample.toString());
|
||||
Collectd.logDebug("Writing: " + datapoint);
|
||||
write(datapoint);
|
||||
}
|
||||
}
|
||||
|
||||
private void debugValueList(ValueList vl) {
|
||||
Collectd.logDebug("Input: " + vl.toString());
|
||||
Collectd.logDebug("Plugin: " + vl.getPlugin());
|
||||
Collectd.logDebug("PluginInstance: " + vl.getPluginInstance());
|
||||
Collectd.logDebug("Type: " + vl.getType());
|
||||
Collectd.logDebug("TypeInstance: " + vl.getTypeInstance());
|
||||
for (int i = 0; i < vl.getValues().size(); i++) {
|
||||
Number value = vl.getValues().get(i);
|
||||
DataSource ds = vl.getDataSet().getDataSources().get(i);
|
||||
Collectd.logDebug(convertType(ds.getType()) + " " + ds.getName() + " = " + value);
|
||||
}
|
||||
}
|
||||
|
||||
private String convertType(int type) {
|
||||
String result = null;
|
||||
switch (type) {
|
||||
case 0:
|
||||
result = COUNTER;
|
||||
break;
|
||||
case 1:
|
||||
result = GAUGE;
|
||||
break;
|
||||
case 2:
|
||||
result = DERIVE;
|
||||
break;
|
||||
case 3:
|
||||
result = ABSOLUTE;
|
||||
break;
|
||||
default:
|
||||
result = GAUGE;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean notEmpty(String arg) {
|
||||
return (null != arg) && !(arg.equals(""));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
package timely.collectd.plugin;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class SMARTCodeMapping extends HashMap<Integer, String> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final Map<Integer, String> smart;
|
||||
static {
|
||||
Map<Integer, String> map = new HashMap<>();
|
||||
/**
|
||||
* mapping from https://en.wikipedia.org/wiki/S.M.A.R.T.
|
||||
*/
|
||||
map.put(1, "Read_Error_Rate");
|
||||
map.put(2, "Throughput_Performance");
|
||||
map.put(3, "Spin_Up_Time");
|
||||
map.put(4, "Start_Stop_Count");
|
||||
map.put(5, "Reallocated_Sectors_Count");
|
||||
map.put(6, "Read_Channel_Margin");
|
||||
map.put(7, "Seek_Error_Rate");
|
||||
map.put(8, "Seek_Time_Performance");
|
||||
map.put(9, "Power_On_Hours");
|
||||
map.put(10, "Spin_Retry_Count");
|
||||
map.put(11, "Calibration_Retry_Count");
|
||||
map.put(12, "Power_Cycle_Count");
|
||||
map.put(13, "Soft_Read_Error_Rate");
|
||||
map.put(22, "Current_Helium_Level");
|
||||
map.put(170, "Available_Reserved_Space");
|
||||
map.put(171, "SSD_Program_Fail_Count");
|
||||
map.put(172, "SSD_Erase_Fail_Count");
|
||||
map.put(173, "SSD_Wear_Leveling_Count");
|
||||
map.put(174, "Unexpected_power_loss_count");
|
||||
map.put(175, "Power_Loss_Protection_Failure");
|
||||
map.put(176, "Erase_Fail_Count");
|
||||
map.put(177, "Wear_Range_Delta");
|
||||
map.put(179, "Used_Reserved_Block_Count_Total");
|
||||
map.put(180, "Unused_Reserved_Block_Count_Total");
|
||||
map.put(181, "Program_Fail_Count_Total");
|
||||
map.put(182, "Erase_Fail_Count");
|
||||
map.put(183, "SATA_Downshift_Error_Count");
|
||||
map.put(184, "End_to_End_error");
|
||||
map.put(185, "Head_Stability");
|
||||
map.put(186, "Induced_Op_Vibration_Detection");
|
||||
map.put(187, "Reported_Uncorrectable_Errors");
|
||||
map.put(188, "Command_Timeout");
|
||||
map.put(189, "High_Fly_Writes");
|
||||
map.put(190, "Airflow_Temperature_Celsius");
|
||||
map.put(190, "Temperature_Difference_from_100");
|
||||
map.put(191, "G_sense_Error_Rate");
|
||||
map.put(192, "Unsafe_Shutdown_Count");
|
||||
map.put(193, "Load_Unload_Cycle_Count");
|
||||
map.put(194, "Temperature_Celsius");
|
||||
map.put(195, "Hardware_ECC_Recovered");
|
||||
map.put(196, "Reallocation_Event_Count");
|
||||
map.put(197, "Current_Pending_Sector_Count");
|
||||
map.put(198, "Uncorrectable_Sector_Count");
|
||||
map.put(199, "UltraDMA_CRC_Error_Count");
|
||||
// map.put(200,"Multi_Zone_Error_Rate_[41]");
|
||||
map.put(200, "Write_Error_Rate");
|
||||
map.put(201, "Soft_Read_Error_Rate");
|
||||
map.put(202, "Data_Address_Mark_errors");
|
||||
map.put(203, "Run_Out_Cancel");
|
||||
map.put(204, "Soft_ECC_Correction");
|
||||
map.put(205, "Thermal_Asperity_Rate");
|
||||
map.put(206, "Flying_Height");
|
||||
map.put(207, "Spin_High_Current");
|
||||
map.put(208, "Spin_Buzz");
|
||||
map.put(209, "Offline_Seek_Performance");
|
||||
map.put(210, "Vibration_During_Write");
|
||||
map.put(211, "Vibration_During_Write");
|
||||
map.put(212, "Shock_During_Write");
|
||||
map.put(220, "Disk_Shift");
|
||||
map.put(221, "G_Sense_Error_Rate");
|
||||
map.put(222, "Loaded_Hours");
|
||||
map.put(223, "Load_Unload_Retry_Count");
|
||||
map.put(224, "Load_Friction");
|
||||
map.put(225, "Load_Unload_Cycle_Count");
|
||||
map.put(226, "Load_In_time");
|
||||
map.put(227, "Torque_Amplification_Count");
|
||||
map.put(228, "Power_Off_Retract_Cycle");
|
||||
// map.put(230,"GMR_Head_Amplitude");
|
||||
map.put(230, "Drive_Life_Protection_Status");
|
||||
map.put(231, "Temperature");
|
||||
// map.put(231,"SSD_Life_Left");
|
||||
map.put(232, "Endurance_Remaining");
|
||||
// map.put(232,"Available_Reserved_Space");
|
||||
map.put(233, "Power_On_Hours");
|
||||
// map.put(233,"Media_Wearout_Indicator");
|
||||
map.put(234, "Average_erase_count");
|
||||
map.put(235, "Good_Block_Count");
|
||||
map.put(240, "Head_Flying_Hours");
|
||||
// map.put(240,"Transfer_Error_Rate_(Fujitsu)");
|
||||
map.put(241, "Total_LBAs_Written");
|
||||
map.put(242, "Total_LBAs_Read");
|
||||
map.put(243, "Total_LBAs_Written_Expanded");
|
||||
map.put(244, "Total_LBAs_Read_Expanded");
|
||||
map.put(249, "NAND_Writes_1GiB");
|
||||
map.put(250, "Read_Error_Retry_Rate");
|
||||
map.put(251, "Minimum_Spares_Remaining");
|
||||
map.put(252, "Newly_Added_Bad_Flash_Block");
|
||||
map.put(254, "Free_Fall_Protection");
|
||||
smart = Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
protected SMARTCodeMapping() {
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return smart.size();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return smart.isEmpty();
|
||||
}
|
||||
|
||||
public boolean containsKey(Object key) {
|
||||
return smart.containsKey(key);
|
||||
}
|
||||
|
||||
public boolean containsValue(Object value) {
|
||||
return smart.containsValue(value);
|
||||
}
|
||||
|
||||
public String get(Object key) {
|
||||
return smart.get(key);
|
||||
}
|
||||
|
||||
public String put(Integer key, String value) {
|
||||
return smart.put(key, value);
|
||||
}
|
||||
|
||||
public String remove(Object key) {
|
||||
return smart.remove(key);
|
||||
}
|
||||
|
||||
public void putAll(Map<? extends Integer, ? extends String> m) {
|
||||
smart.putAll(m);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
smart.clear();
|
||||
}
|
||||
|
||||
public Set<Integer> keySet() {
|
||||
return smart.keySet();
|
||||
}
|
||||
|
||||
public Collection<String> values() {
|
||||
return smart.values();
|
||||
}
|
||||
|
||||
public Set<java.util.Map.Entry<Integer, String>> entrySet() {
|
||||
return smart.entrySet();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return smart.equals(o);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return smart.hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,314 +0,0 @@
|
||||
package timely.collectd.plugin;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import net.jcip.annotations.NotThreadSafe;
|
||||
|
||||
import org.collectd.api.Collectd;
|
||||
import org.collectd.api.DataSet;
|
||||
import org.collectd.api.DataSource;
|
||||
import org.collectd.api.ValueList;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.powermock.api.easymock.PowerMock;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({ Collectd.class })
|
||||
@NotThreadSafe
|
||||
public class PluginParentTest {
|
||||
|
||||
public class TestPlugin extends CollectDPluginParent {
|
||||
|
||||
public TestPlugin() {
|
||||
super.addlTags.add(" addl1=foo");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(String metric) {
|
||||
result = metric;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final String HOST = "r01n01.test";
|
||||
private static final Long TIME = 1456156976840L;
|
||||
public String result = null;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
PowerMock.suppress(PowerMock.everythingDeclaredIn(Collectd.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatsDHadoopFormat() throws Exception {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("statsd");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("derive");
|
||||
vl.setTypeInstance("DataNode.dfs.datanode.BlocksRead");
|
||||
vl.setValues(Collections.singletonList((Number) 1.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
assertEquals(
|
||||
"put statsd.dfs.BlocksRead 1456156976840 1.0 host=r01n01 rack=r01 addl1=foo instance=DataNode sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatsDUnknownFormat() throws Exception {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("statsd");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("derive");
|
||||
vl.setTypeInstance("baz");
|
||||
vl.setValues(Collections.singletonList((Number) 1.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
assertEquals("put statsd.baz 1456156976840 1.0 host=r01n01 rack=r01 addl1=foo sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatsDUnknownFormat2() throws Exception {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("statsd");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("derive");
|
||||
vl.setTypeInstance("bar.baz");
|
||||
vl.setValues(Collections.singletonList((Number) 1.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
assertEquals(
|
||||
"put statsd.baz 1456156976840 1.0 host=r01n01 rack=r01 addl1=foo instance=bar sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHddTemp() throws Exception {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("hddtemp");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("temperature");
|
||||
vl.setTypeInstance("sda");
|
||||
vl.setValues(Collections.singletonList((Number) 35.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
assertEquals(
|
||||
"put sys.hddtemp.temperature 1456156976840 35.0 host=r01n01 rack=r01 addl1=foo instance=sda sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmart1() throws Exception {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("smart");
|
||||
vl.setPluginInstance("sda");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("smart_badsectors");
|
||||
vl.setValues(Collections.singletonList((Number) 0.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
assertEquals(
|
||||
"put sys.smart.smart_badsectors 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo instance=sda sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmart2() throws Exception {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("smart");
|
||||
vl.setPluginInstance("sda");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("smart_attribute");
|
||||
vl.setTypeInstance("raw-read-error-rate");
|
||||
vl.setValues(Collections.singletonList((Number) 0.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
assertEquals(
|
||||
"put sys.smart.raw-read-error-rate 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo instance=sda sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmartCode() throws Exception {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("smart");
|
||||
vl.setPluginInstance("sda");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("smart_attribute");
|
||||
vl.setTypeInstance("attribute-242");
|
||||
vl.setValues(Collections.singletonList((Number) 0.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
assertEquals(
|
||||
"put sys.smart.Total_LBAs_Read 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo code=242 instance=sda sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSensors() {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("sensors");
|
||||
vl.setPluginInstance("coretemp-isa-0000");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("temperature");
|
||||
vl.setTypeInstance("temp1");
|
||||
vl.setValues(Collections.singletonList((Number) 35.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
assertEquals(
|
||||
"put sys.sensors.temperature.coretemp-isa-0000 1456156976840 35.0 host=r01n01 rack=r01 addl1=foo instance=1 sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHAProxy1() {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("haproxy");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("gauge");
|
||||
vl.setTypeInstance("run_queue");
|
||||
vl.setValues(Collections.singletonList((Number) 0.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
assertEquals(
|
||||
"put sys.haproxy.run_queue 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHAProxy2() {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("haproxy");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("gauge");
|
||||
vl.setTypeInstance("server1.proxy1.queue_current");
|
||||
vl.setValues(Collections.singletonList((Number) 0.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
assertEquals(
|
||||
"put sys.haproxy.queue_current 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo proxy=proxy1 server=server1 sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEthStatNoQueue() {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("ethstat");
|
||||
vl.setPluginInstance("eth0");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("derive");
|
||||
vl.setTypeInstance("tx_comp_queue_full");
|
||||
vl.setValues(Collections.singletonList((Number) 6.0D));
|
||||
DataSet ds = new DataSet("DERIVE");
|
||||
ds.addDataSource(new DataSource("value", 1, 6.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
assertEquals(
|
||||
"put sys.ethstat.tx_comp_queue_full 1456156976840 6.0 host=r01n01 rack=r01 addl1=foo instance=eth0 sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEthStatWithQueue() {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("ethstat");
|
||||
vl.setPluginInstance("eth0");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("derive");
|
||||
vl.setTypeInstance("rx_queue_1_bytes");
|
||||
vl.setValues(Collections.singletonList((Number) 6.0D));
|
||||
DataSet ds = new DataSet("DERIVE");
|
||||
ds.addDataSource(new DataSource("value", 1, 6.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
assertEquals(
|
||||
"put sys.ethstat.rx_bytes 1456156976840 6.0 host=r01n01 rack=r01 addl1=foo queue=1 instance=eth0 sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIpmi() {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("ipmi");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("temperature");
|
||||
vl.setTypeInstance("LAN NIC Temp system_board (3.2)");
|
||||
vl.setValues(Collections.singletonList((Number) 6.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 6.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
assertEquals(
|
||||
"put sys.ipmi.temperature 1456156976840 6.0 host=r01n01 rack=r01 addl1=foo instance=LAN_NIC_Temp_system_board_(3.2) sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
# Write Timely plugin for CollectD
|
||||
|
||||
To build, run ```mvn clean package```. Place the resulting jar file somewhere on the local filesystem and add the following to the
|
||||
collectd configuration file
|
||||
|
||||
```
|
||||
LoadPlugin java
|
||||
<Plugin java>
|
||||
JVMArg "-verbose:jni"
|
||||
JVMArg "-Djava.class.path=/usr/share/collectd/java/collectd-api.jar:<path_to_jar>/collectd-timely-plugin-0.0.1-SNAPSHOT.jar"
|
||||
JVMArg "-Xms1024m"
|
||||
JVMArg "-Xmx1024m"
|
||||
LoadPlugin "timely.collectd.plugin.WriteTimelyPlugin"
|
||||
<Plugin "timely.collectd.plugin.WriteTimelyPlugin">
|
||||
Host "<TimelyHost>"
|
||||
Port "<TimelyPutPort>"
|
||||
</Plugin>
|
||||
</Plugin>
|
||||
```
|
||||
@@ -1,157 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.1</version>
|
||||
</parent>
|
||||
<artifactId>collectd-timely-plugin</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>collectd</groupId>
|
||||
<artifactId>collectd-api</artifactId>
|
||||
<version>5.5.1</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${collectd-api-jar}</systemPath>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.jcip</groupId>
|
||||
<artifactId>jcip-annotations</artifactId>
|
||||
<version>1.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.javassist</groupId>
|
||||
<artifactId>javassist</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-easymock-release-full</artifactId>
|
||||
<version>1.6.4</version>
|
||||
<classifier>full</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>collectd-plugin-parent</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<effort>Max</effort>
|
||||
<threshold>Medium</threshold>
|
||||
<xmlOutput>true</xmlOutput>
|
||||
<excludeFilterFile>src/main/resources/findbugs-excludes.xml</excludeFilterFile>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
<phase>prepare-package</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.4.2</version>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>shade</id>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<phase>package</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.19</version>
|
||||
<configuration>
|
||||
<excludedGroups>timely.test.IntegrationTest</excludedGroups>
|
||||
<redirectTestOutputToFile>true</redirectTestOutputToFile>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>net.revelc.code</groupId>
|
||||
<artifactId>formatter-maven-plugin</artifactId>
|
||||
<version>0.5.2</version>
|
||||
<configuration>
|
||||
<configFile>${project.parent.basedir}/eclipse/Eclipse-Formatter.xml</configFile>
|
||||
<lineEnding>LF</lineEnding>
|
||||
<overrideConfigCompilerVersion>true</overrideConfigCompilerVersion>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>format</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
|
||||
<plugin>
|
||||
<groupId>org.eclipse.m2e</groupId>
|
||||
<artifactId>lifecycle-mapping</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<configuration>
|
||||
<lifecycleMappingMetadata>
|
||||
<pluginExecutions>
|
||||
<pluginExecution>
|
||||
<pluginExecutionFilter>
|
||||
<groupId>net.revelc.code</groupId>
|
||||
<artifactId>
|
||||
formatter-maven-plugin
|
||||
</artifactId>
|
||||
<versionRange>
|
||||
[0.5.2,)
|
||||
</versionRange>
|
||||
<goals>
|
||||
<goal>format</goal>
|
||||
</goals>
|
||||
</pluginExecutionFilter>
|
||||
<action>
|
||||
<ignore/>
|
||||
</action>
|
||||
</pluginExecution>
|
||||
</pluginExecutions>
|
||||
</lifecycleMappingMetadata>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,129 +0,0 @@
|
||||
package timely.collectd.plugin;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.Socket;
|
||||
|
||||
import org.collectd.api.Collectd;
|
||||
import org.collectd.api.CollectdConfigInterface;
|
||||
import org.collectd.api.CollectdShutdownInterface;
|
||||
import org.collectd.api.CollectdWriteInterface;
|
||||
import org.collectd.api.OConfigItem;
|
||||
import org.collectd.api.ValueList;
|
||||
|
||||
/**
|
||||
*
|
||||
* CollectD plugin that writes metrics collected by the StatsD plugin to Timely.
|
||||
*
|
||||
*/
|
||||
public class WriteTimelyPlugin extends CollectDPluginParent implements CollectdConfigInterface,
|
||||
CollectdShutdownInterface, CollectdWriteInterface {
|
||||
|
||||
private String host = null;
|
||||
private int port = 0;
|
||||
private Socket sock = null;
|
||||
private PrintWriter out = null;
|
||||
private long connectTime = 0L;
|
||||
private long backoff = 2000;
|
||||
|
||||
public WriteTimelyPlugin() {
|
||||
Collectd.registerConfig(WriteTimelyPlugin.class.getName(), this);
|
||||
Collectd.registerShutdown(WriteTimelyPlugin.class.getName(), this);
|
||||
Collectd.registerWrite(WriteTimelyPlugin.class.getName(), this);
|
||||
}
|
||||
|
||||
private synchronized int connect() {
|
||||
if (null == sock || !sock.isConnected() || out.checkError()) {
|
||||
if (System.currentTimeMillis() > (connectTime + backoff)) {
|
||||
try {
|
||||
connectTime = System.currentTimeMillis();
|
||||
sock = new Socket(host, port);
|
||||
out = new PrintWriter(sock.getOutputStream(), false);
|
||||
backoff = 2000;
|
||||
Collectd.logInfo("Connected to Timely at " + host + ":" + port);
|
||||
} catch (IOException e) {
|
||||
Collectd.logError("Error connecting to Timely at " + host + ":" + port + ". Error: "
|
||||
+ e.getMessage());
|
||||
backoff = backoff * 2;
|
||||
sock = null;
|
||||
out = null;
|
||||
Collectd.logWarning("Will retry connection in " + backoff + " ms.");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
Collectd.logWarning("Not writing to Timely, waiting to reconnect");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public synchronized int write(ValueList vl) {
|
||||
|
||||
int c = connect();
|
||||
if (c != 0) {
|
||||
return c;
|
||||
}
|
||||
super.process(vl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(String metric) {
|
||||
out.write(metric);
|
||||
}
|
||||
|
||||
public synchronized void flush() {
|
||||
if (null != out) {
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized int shutdown() {
|
||||
Collectd.logInfo("Shutting down connection to Timely at " + host + ":" + port);
|
||||
if (null != sock) {
|
||||
try {
|
||||
if (null != out) {
|
||||
out.close();
|
||||
}
|
||||
sock.close();
|
||||
} catch (IOException e) {
|
||||
Collectd.logError("Error closing connection to Timely at " + host + ":" + port + ". Error: "
|
||||
+ e.getMessage());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public synchronized int config(OConfigItem config) {
|
||||
super.config(config);
|
||||
for (OConfigItem child : config.getChildren()) {
|
||||
switch (child.getKey()) {
|
||||
case "host":
|
||||
case "hostname":
|
||||
case "Host":
|
||||
case "HostName":
|
||||
host = child.getValues().get(0).getString();
|
||||
break;
|
||||
case "Port":
|
||||
case "port":
|
||||
port = Integer.parseInt(child.getValues().get(0).getString());
|
||||
break;
|
||||
default:
|
||||
|
||||
}
|
||||
}
|
||||
if (null != host) {
|
||||
return 0;
|
||||
}
|
||||
if (host == null) {
|
||||
Collectd.logError("Timely host must be configured");
|
||||
}
|
||||
if (port == 0) {
|
||||
Collectd.logError("Timely port must be configured");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<FindBugsFilter>
|
||||
<Or>
|
||||
<Bug pattern="DM_DEFAULT_ENCODING"/>
|
||||
<Method name="connect" />
|
||||
<Class name="timely.collectd.plugin.WriteTimelyPlugin"/>
|
||||
</Or>
|
||||
</FindBugsFilter>
|
||||
-202
@@ -1,202 +0,0 @@
|
||||
package timely.collectd.plugin;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.Collections;
|
||||
|
||||
import net.jcip.annotations.NotThreadSafe;
|
||||
|
||||
import org.collectd.api.Collectd;
|
||||
import org.collectd.api.DataSet;
|
||||
import org.collectd.api.DataSource;
|
||||
import org.collectd.api.OConfigItem;
|
||||
import org.collectd.api.ValueList;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.powermock.api.easymock.PowerMock;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({ Collectd.class })
|
||||
@NotThreadSafe
|
||||
public class WriteTimelyPluginTest {
|
||||
|
||||
private WriteTimelyPlugin plugin = null;
|
||||
private FakeServer server = null;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
PowerMock.suppress(PowerMock.everythingDeclaredIn(Collectd.class));
|
||||
server = new FakeServer();
|
||||
}
|
||||
|
||||
private ValueList createMetric() {
|
||||
ValueList val = new ValueList();
|
||||
val.setHost("r001n01.localdomain");
|
||||
val.setTime(System.currentTimeMillis());
|
||||
val.setPlugin("hddtemp");
|
||||
val.setPluginInstance(null);
|
||||
val.setType("temperature");
|
||||
val.setTypeInstance("sda");
|
||||
val.setValues(Collections.singletonList((Number) new Double(2.0)));
|
||||
DataSet ds = new DataSet("test");
|
||||
ds.addDataSource(new DataSource("test", DataSource.TYPE_COUNTER, 0, 100));
|
||||
val.setDataSet(ds);
|
||||
return val;
|
||||
}
|
||||
|
||||
private void setupPlugin() throws Exception {
|
||||
OConfigItem host = new OConfigItem("Host");
|
||||
host.addValue(server.getHost());
|
||||
OConfigItem port = new OConfigItem("Port");
|
||||
port.addValue(Integer.toString(server.getPort()));
|
||||
OConfigItem config = new OConfigItem("");
|
||||
config.addChild(host);
|
||||
config.addChild(port);
|
||||
plugin = new WriteTimelyPlugin();
|
||||
Assert.assertEquals(0, plugin.config(config));
|
||||
// Seed with one metric to create the connection
|
||||
Assert.assertEquals(0, plugin.write(createMetric()));
|
||||
plugin.flush();
|
||||
Thread.sleep(100);
|
||||
Assert.assertTrue(server.messageReceived());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrite() throws Exception {
|
||||
Thread t = new Thread(server);
|
||||
t.start();
|
||||
setupPlugin();
|
||||
while (!server.ready()) {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
Assert.assertEquals(0, plugin.write(createMetric()));
|
||||
plugin.flush();
|
||||
Thread.sleep(100);
|
||||
Assert.assertTrue(server.messageReceived());
|
||||
plugin.shutdown();
|
||||
server.shutdown();
|
||||
t.join();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteAfterServerRestart() throws Exception {
|
||||
Thread t = new Thread(server);
|
||||
t.start();
|
||||
setupPlugin();
|
||||
while (!server.ready()) {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
Assert.assertEquals(0, plugin.write(createMetric()));
|
||||
plugin.flush();
|
||||
Thread.sleep(100);
|
||||
Assert.assertTrue(server.messageReceived());
|
||||
server.shutdown();
|
||||
t.join();
|
||||
Thread.sleep(2000);
|
||||
|
||||
server.create();
|
||||
Thread t2 = new Thread(server);
|
||||
t2.start();
|
||||
while (!server.ready()) {
|
||||
Thread.sleep(1000);
|
||||
// Keep sending metrics to plugin to force reconnect
|
||||
int result = plugin.write(createMetric());
|
||||
System.out.println("Wrote to client, result: " + result);
|
||||
Assert.assertEquals(0, result);
|
||||
plugin.flush();
|
||||
}
|
||||
Assert.assertEquals(0, plugin.write(createMetric()));
|
||||
plugin.flush();
|
||||
Thread.sleep(1000);
|
||||
Assert.assertTrue(server.messageReceived());
|
||||
plugin.shutdown();
|
||||
server.shutdown();
|
||||
t2.join();
|
||||
}
|
||||
|
||||
private static class FakeServer implements Runnable {
|
||||
|
||||
private ServerSocket server = null;
|
||||
private BufferedReader reader = null;
|
||||
private String host = null;
|
||||
private int port = 0;
|
||||
private Socket s = null;
|
||||
private boolean received = false;
|
||||
|
||||
public FakeServer() throws Exception {
|
||||
create();
|
||||
}
|
||||
|
||||
public void create() throws Exception {
|
||||
server = new ServerSocket(port);
|
||||
server.setReceiveBufferSize(8192);
|
||||
host = server.getInetAddress().getHostAddress();
|
||||
port = server.getLocalPort();
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void shutdown() throws Exception {
|
||||
System.out.println("Closing server.");
|
||||
s.close();
|
||||
s = null;
|
||||
reader.close();
|
||||
reader = null;
|
||||
server.close();
|
||||
server = null;
|
||||
}
|
||||
|
||||
public boolean ready() throws Exception {
|
||||
return (s != null && s.isConnected());
|
||||
}
|
||||
|
||||
public boolean messageReceived() {
|
||||
try {
|
||||
return Boolean.valueOf(this.received);
|
||||
} finally {
|
||||
this.received = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
s = server.accept();
|
||||
System.out.println("Client connected.");
|
||||
reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
|
||||
while (server != null && !server.isClosed() && s != null && s.isConnected()) {
|
||||
String line;
|
||||
try {
|
||||
line = reader.readLine();
|
||||
} catch (Exception e) {
|
||||
// Might have been closed
|
||||
System.out.println("Closed by Exception: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
if (null != line) {
|
||||
this.received = true;
|
||||
}
|
||||
System.out.println(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
System.out.println("Exiting run method.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="WARN">
|
||||
<Appenders>
|
||||
<Console name="CONSOLE" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="TRACE">
|
||||
<AppenderRef ref="CONSOLE"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
@@ -0,0 +1,46 @@
|
||||
# Markdown
|
||||
set :markdown_engine, :redcarpet
|
||||
set :markdown,
|
||||
fenced_code_blocks: true,
|
||||
smartypants: true,
|
||||
disable_indented_code_blocks: true,
|
||||
prettify: true,
|
||||
tables: true,
|
||||
with_toc_data: true,
|
||||
no_intra_emphasis: true
|
||||
|
||||
# Assets
|
||||
set :css_dir, 'stylesheets'
|
||||
set :js_dir, 'javascripts'
|
||||
set :images_dir, 'images'
|
||||
set :fonts_dir, 'fonts'
|
||||
|
||||
# Activate the syntax highlighter
|
||||
activate :syntax
|
||||
|
||||
activate :sprockets
|
||||
|
||||
activate :autoprefixer do |config|
|
||||
config.browsers = ['last 2 version', 'Firefox ESR']
|
||||
config.cascade = false
|
||||
config.inline = true
|
||||
end
|
||||
|
||||
# Github pages require relative links
|
||||
activate :relative_assets
|
||||
set :relative_links, true
|
||||
|
||||
# Build Configuration
|
||||
configure :build do
|
||||
# If you're having trouble with Middleman hanging, commenting
|
||||
# out the following two lines has been known to help
|
||||
activate :minify_css
|
||||
activate :minify_javascript
|
||||
# activate :relative_assets
|
||||
# activate :asset_hash
|
||||
# activate :gzip
|
||||
end
|
||||
|
||||
# Deploy Configuration
|
||||
# If you want Middleman to listen on a different port, you can set that below
|
||||
set :port, 4567
|
||||
@@ -0,0 +1,203 @@
|
||||
#!/usr/bin/env bash
|
||||
set -o errexit #abort if any command fails
|
||||
me=$(basename "$0")
|
||||
|
||||
help_message="\
|
||||
Usage: $me [-c FILE] [<options>]
|
||||
Deploy generated files to a git branch.
|
||||
|
||||
Options:
|
||||
|
||||
-h, --help Show this help information.
|
||||
-v, --verbose Increase verbosity. Useful for debugging.
|
||||
-e, --allow-empty Allow deployment of an empty directory.
|
||||
-m, --message MESSAGE Specify the message used when committing on the
|
||||
deploy branch.
|
||||
-n, --no-hash Don't append the source commit's hash to the deploy
|
||||
commit's message.
|
||||
"
|
||||
|
||||
bundle exec middleman build --clean
|
||||
|
||||
parse_args() {
|
||||
# Set args from a local environment file.
|
||||
if [ -e ".env" ]; then
|
||||
source .env
|
||||
fi
|
||||
|
||||
# Parse arg flags
|
||||
# If something is exposed as an environment variable, set/overwrite it
|
||||
# here. Otherwise, set/overwrite the internal variable instead.
|
||||
while : ; do
|
||||
if [[ $1 = "-h" || $1 = "--help" ]]; then
|
||||
echo "$help_message"
|
||||
return 0
|
||||
elif [[ $1 = "-v" || $1 = "--verbose" ]]; then
|
||||
verbose=true
|
||||
shift
|
||||
elif [[ $1 = "-e" || $1 = "--allow-empty" ]]; then
|
||||
allow_empty=true
|
||||
shift
|
||||
elif [[ ( $1 = "-m" || $1 = "--message" ) && -n $2 ]]; then
|
||||
commit_message=$2
|
||||
shift 2
|
||||
elif [[ $1 = "-n" || $1 = "--no-hash" ]]; then
|
||||
GIT_DEPLOY_APPEND_HASH=false
|
||||
shift
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Set internal option vars from the environment and arg flags. All internal
|
||||
# vars should be declared here, with sane defaults if applicable.
|
||||
|
||||
# Source directory & target branch.
|
||||
deploy_directory=build
|
||||
deploy_branch=gh-pages
|
||||
|
||||
#if no user identity is already set in the current git environment, use this:
|
||||
default_username=${GIT_DEPLOY_USERNAME:-deploy.sh}
|
||||
default_email=${GIT_DEPLOY_EMAIL:-}
|
||||
|
||||
#repository to deploy to. must be readable and writable.
|
||||
repo=origin
|
||||
|
||||
#append commit hash to the end of message by default
|
||||
append_hash=${GIT_DEPLOY_APPEND_HASH:-true}
|
||||
}
|
||||
|
||||
main() {
|
||||
parse_args "$@"
|
||||
|
||||
enable_expanded_output
|
||||
|
||||
if ! git diff --exit-code --quiet --cached; then
|
||||
echo Aborting due to uncommitted changes in the index >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
commit_title=`git log -n 1 --format="%s" HEAD`
|
||||
commit_hash=` git log -n 1 --format="%H" HEAD`
|
||||
|
||||
#default commit message uses last title if a custom one is not supplied
|
||||
if [[ -z $commit_message ]]; then
|
||||
commit_message="publish: $commit_title"
|
||||
fi
|
||||
|
||||
#append hash to commit message unless no hash flag was found
|
||||
if [ $append_hash = true ]; then
|
||||
commit_message="$commit_message"$'\n\n'"generated from commit $commit_hash"
|
||||
fi
|
||||
|
||||
previous_branch=`git rev-parse --abbrev-ref HEAD`
|
||||
|
||||
if [ ! -d "$deploy_directory" ]; then
|
||||
echo "Deploy directory '$deploy_directory' does not exist. Aborting." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# must use short form of flag in ls for compatibility with OS X and BSD
|
||||
if [[ -z `ls -A "$deploy_directory" 2> /dev/null` && -z $allow_empty ]]; then
|
||||
echo "Deploy directory '$deploy_directory' is empty. Aborting. If you're sure you want to deploy an empty tree, use the --allow-empty / -e flag." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if git ls-remote --exit-code $repo "refs/heads/$deploy_branch" ; then
|
||||
# deploy_branch exists in $repo; make sure we have the latest version
|
||||
|
||||
disable_expanded_output
|
||||
git fetch --force $repo $deploy_branch:$deploy_branch
|
||||
enable_expanded_output
|
||||
fi
|
||||
|
||||
# check if deploy_branch exists locally
|
||||
if git show-ref --verify --quiet "refs/heads/$deploy_branch"
|
||||
then incremental_deploy
|
||||
else initial_deploy
|
||||
fi
|
||||
|
||||
restore_head
|
||||
}
|
||||
|
||||
initial_deploy() {
|
||||
git --work-tree "$deploy_directory" checkout --orphan $deploy_branch
|
||||
git --work-tree "$deploy_directory" add --all
|
||||
commit+push
|
||||
}
|
||||
|
||||
incremental_deploy() {
|
||||
#make deploy_branch the current branch
|
||||
git symbolic-ref HEAD refs/heads/$deploy_branch
|
||||
#put the previously committed contents of deploy_branch into the index
|
||||
git --work-tree "$deploy_directory" reset --mixed --quiet
|
||||
git --work-tree "$deploy_directory" add --all
|
||||
|
||||
set +o errexit
|
||||
diff=$(git --work-tree "$deploy_directory" diff --exit-code --quiet HEAD --)$?
|
||||
set -o errexit
|
||||
case $diff in
|
||||
0) echo No changes to files in $deploy_directory. Skipping commit.;;
|
||||
1) commit+push;;
|
||||
*)
|
||||
echo git diff exited with code $diff. Aborting. Staying on branch $deploy_branch so you can debug. To switch back to master, use: git symbolic-ref HEAD refs/heads/master && git reset --mixed >&2
|
||||
return $diff
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
commit+push() {
|
||||
set_user_id
|
||||
git --work-tree "$deploy_directory" commit -m "$commit_message"
|
||||
|
||||
disable_expanded_output
|
||||
#--quiet is important here to avoid outputting the repo URL, which may contain a secret token
|
||||
git push --quiet $repo $deploy_branch
|
||||
enable_expanded_output
|
||||
}
|
||||
|
||||
#echo expanded commands as they are executed (for debugging)
|
||||
enable_expanded_output() {
|
||||
if [ $verbose ]; then
|
||||
set -o xtrace
|
||||
set +o verbose
|
||||
fi
|
||||
}
|
||||
|
||||
#this is used to avoid outputting the repo URL, which may contain a secret token
|
||||
disable_expanded_output() {
|
||||
if [ $verbose ]; then
|
||||
set +o xtrace
|
||||
set -o verbose
|
||||
fi
|
||||
}
|
||||
|
||||
set_user_id() {
|
||||
if [[ -z `git config user.name` ]]; then
|
||||
git config user.name "$default_username"
|
||||
fi
|
||||
if [[ -z `git config user.email` ]]; then
|
||||
git config user.email "$default_email"
|
||||
fi
|
||||
}
|
||||
|
||||
restore_head() {
|
||||
if [[ $previous_branch = "HEAD" ]]; then
|
||||
#we weren't on any branch before, so just set HEAD back to the commit it was on
|
||||
git update-ref --no-deref HEAD $commit_hash $deploy_branch
|
||||
else
|
||||
git symbolic-ref HEAD refs/heads/$previous_branch
|
||||
fi
|
||||
|
||||
git reset --mixed
|
||||
}
|
||||
|
||||
filter() {
|
||||
sed -e "s|$repo|\$repo|g"
|
||||
}
|
||||
|
||||
sanitize() {
|
||||
"$@" 2> >(filter 1>&2) | filter
|
||||
}
|
||||
|
||||
[[ $1 = --source-only ]] || main "$@"
|
||||
@@ -1,295 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<profiles version="12">
|
||||
<profile kind="CodeFormatterProfile" name="Timely" version="12">
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.source" value="1.8"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.8"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="80"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.8"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
|
||||
</profile>
|
||||
</profiles>
|
||||
Executable
+148
@@ -0,0 +1,148 @@
|
||||
{
|
||||
"IcoMoonType": "selection",
|
||||
"icons": [
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
"M438.857 73.143q119.429 0 220.286 58.857t159.714 159.714 58.857 220.286-58.857 220.286-159.714 159.714-220.286 58.857-220.286-58.857-159.714-159.714-58.857-220.286 58.857-220.286 159.714-159.714 220.286-58.857zM512 785.714v-108.571q0-8-5.143-13.429t-12.571-5.429h-109.714q-7.429 0-13.143 5.714t-5.714 13.143v108.571q0 7.429 5.714 13.143t13.143 5.714h109.714q7.429 0 12.571-5.429t5.143-13.429zM510.857 589.143l10.286-354.857q0-6.857-5.714-10.286-5.714-4.571-13.714-4.571h-125.714q-8 0-13.714 4.571-5.714 3.429-5.714 10.286l9.714 354.857q0 5.714 5.714 10t13.714 4.286h105.714q8 0 13.429-4.286t6-10z"
|
||||
],
|
||||
"attrs": [],
|
||||
"isMulticolor": false,
|
||||
"tags": [
|
||||
"exclamation-circle"
|
||||
],
|
||||
"defaultCode": 61546,
|
||||
"grid": 14
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 100,
|
||||
"order": 4,
|
||||
"prevSize": 28,
|
||||
"code": 58880,
|
||||
"name": "exclamation-sign",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"iconIdx": 0
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
"M585.143 786.286v-91.429q0-8-5.143-13.143t-13.143-5.143h-54.857v-292.571q0-8-5.143-13.143t-13.143-5.143h-182.857q-8 0-13.143 5.143t-5.143 13.143v91.429q0 8 5.143 13.143t13.143 5.143h54.857v182.857h-54.857q-8 0-13.143 5.143t-5.143 13.143v91.429q0 8 5.143 13.143t13.143 5.143h256q8 0 13.143-5.143t5.143-13.143zM512 274.286v-91.429q0-8-5.143-13.143t-13.143-5.143h-109.714q-8 0-13.143 5.143t-5.143 13.143v91.429q0 8 5.143 13.143t13.143 5.143h109.714q8 0 13.143-5.143t5.143-13.143zM877.714 512q0 119.429-58.857 220.286t-159.714 159.714-220.286 58.857-220.286-58.857-159.714-159.714-58.857-220.286 58.857-220.286 159.714-159.714 220.286-58.857 220.286 58.857 159.714 159.714 58.857 220.286z"
|
||||
],
|
||||
"attrs": [],
|
||||
"isMulticolor": false,
|
||||
"tags": [
|
||||
"info-circle"
|
||||
],
|
||||
"defaultCode": 61530,
|
||||
"grid": 14
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 85,
|
||||
"order": 3,
|
||||
"name": "info-sign",
|
||||
"prevSize": 28,
|
||||
"code": 58882
|
||||
},
|
||||
"setIdx": 0,
|
||||
"iconIdx": 2
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
"M733.714 419.429q0-16-10.286-26.286l-52-51.429q-10.857-10.857-25.714-10.857t-25.714 10.857l-233.143 232.571-129.143-129.143q-10.857-10.857-25.714-10.857t-25.714 10.857l-52 51.429q-10.286 10.286-10.286 26.286 0 15.429 10.286 25.714l206.857 206.857q10.857 10.857 25.714 10.857 15.429 0 26.286-10.857l310.286-310.286q10.286-10.286 10.286-25.714zM877.714 512q0 119.429-58.857 220.286t-159.714 159.714-220.286 58.857-220.286-58.857-159.714-159.714-58.857-220.286 58.857-220.286 159.714-159.714 220.286-58.857 220.286 58.857 159.714 159.714 58.857 220.286z"
|
||||
],
|
||||
"attrs": [],
|
||||
"isMulticolor": false,
|
||||
"tags": [
|
||||
"check-circle"
|
||||
],
|
||||
"defaultCode": 61528,
|
||||
"grid": 14
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 83,
|
||||
"order": 9,
|
||||
"prevSize": 28,
|
||||
"code": 58886,
|
||||
"name": "ok-sign"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"iconIdx": 6
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
"M658.286 475.429q0-105.714-75.143-180.857t-180.857-75.143-180.857 75.143-75.143 180.857 75.143 180.857 180.857 75.143 180.857-75.143 75.143-180.857zM950.857 950.857q0 29.714-21.714 51.429t-51.429 21.714q-30.857 0-51.429-21.714l-196-195.429q-102.286 70.857-228 70.857-81.714 0-156.286-31.714t-128.571-85.714-85.714-128.571-31.714-156.286 31.714-156.286 85.714-128.571 128.571-85.714 156.286-31.714 156.286 31.714 128.571 85.714 85.714 128.571 31.714 156.286q0 125.714-70.857 228l196 196q21.143 21.143 21.143 51.429z"
|
||||
],
|
||||
"width": 951,
|
||||
"attrs": [],
|
||||
"isMulticolor": false,
|
||||
"tags": [
|
||||
"search"
|
||||
],
|
||||
"defaultCode": 61442,
|
||||
"grid": 14
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 2,
|
||||
"order": 1,
|
||||
"prevSize": 28,
|
||||
"code": 58887,
|
||||
"name": "icon-search"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"iconIdx": 7
|
||||
}
|
||||
],
|
||||
"height": 1024,
|
||||
"metadata": {
|
||||
"name": "slate",
|
||||
"license": "SIL OFL 1.1"
|
||||
},
|
||||
"preferences": {
|
||||
"showGlyphs": true,
|
||||
"showQuickUse": true,
|
||||
"showQuickUse2": true,
|
||||
"showSVGs": true,
|
||||
"fontPref": {
|
||||
"prefix": "icon-",
|
||||
"metadata": {
|
||||
"fontFamily": "slate",
|
||||
"majorVersion": 1,
|
||||
"minorVersion": 0,
|
||||
"description": "Based on FontAwesome",
|
||||
"license": "SIL OFL 1.1"
|
||||
},
|
||||
"metrics": {
|
||||
"emSize": 1024,
|
||||
"baseline": 6.25,
|
||||
"whitespace": 50
|
||||
},
|
||||
"resetPoint": 58880,
|
||||
"showSelector": false,
|
||||
"selector": "class",
|
||||
"classSelector": ".icon",
|
||||
"showMetrics": false,
|
||||
"showMetadata": true,
|
||||
"showVersion": true,
|
||||
"ie7": false
|
||||
},
|
||||
"imagePref": {
|
||||
"prefix": "icon-",
|
||||
"png": true,
|
||||
"useClassSelector": true,
|
||||
"color": 4473924,
|
||||
"bgColor": 16777215
|
||||
},
|
||||
"historySize": 100,
|
||||
"showCodes": true,
|
||||
"gridSize": 16,
|
||||
"showLiga": false
|
||||
}
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<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>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.1</version>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>server</module>
|
||||
<module>collectd-timely-plugin</module>
|
||||
<module>collectd-nsq-plugin</module>
|
||||
<module>collectd-plugin-parent</module>
|
||||
</modules>
|
||||
<properties>
|
||||
<!-- Configure the os-maven-plugin extension to expand the classifier on -->
|
||||
<!-- Fedora-"like" systems. -->
|
||||
<os.detection.classifierWithLikes>fedora</os.detection.classifierWithLikes>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<collectd-api-jar>/usr/share/collectd/java/collectd-api.jar</collectd-api-jar>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<version.accumulo>1.7.0</version.accumulo>
|
||||
<version.caffeine>2.2.0</version.caffeine>
|
||||
<version.commons-configuration>1.10</version.commons-configuration>
|
||||
<version.commons-lang3>3.1</version.commons-lang3>
|
||||
<version.hadoop>2.6.2</version.hadoop>
|
||||
<version.jackson>2.6.1</version.jackson>
|
||||
<version.javassist>3.20.0-GA</version.javassist>
|
||||
<version.junit>4.11</version.junit>
|
||||
<version.log4j>2.5</version.log4j>
|
||||
<version.netty>4.0.36.Final</version.netty>
|
||||
<version.security>4.0.4.RELEASE</version.security>
|
||||
<version.slf4j>1.7.12</version.slf4j>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<version>${version.jackson}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${version.jackson}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${version.jackson}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jdk8</artifactId>
|
||||
<version>${version.jackson}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<version>${version.caffeine}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-configuration</groupId>
|
||||
<artifactId>commons-configuration</artifactId>
|
||||
<version>${version.commons-configuration}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>${version.netty}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${version.junit}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>${version.log4j}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>${version.log4j}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
<version>${version.log4j}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.accumulo</groupId>
|
||||
<artifactId>accumulo-core</artifactId>
|
||||
<version>${version.accumulo}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.accumulo</groupId>
|
||||
<artifactId>accumulo-minicluster</artifactId>
|
||||
<version>${version.accumulo}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${version.commons-lang3}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.hadoop</groupId>
|
||||
<artifactId>hadoop-common</artifactId>
|
||||
<version>${version.hadoop}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.mortbay.jetty</groupId>
|
||||
<artifactId>jetty</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.mortbay.jetty</groupId>
|
||||
<artifactId>jetty-util</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.hadoop</groupId>
|
||||
<artifactId>hadoop-client</artifactId>
|
||||
<version>${version.hadoop}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.mortbay.jetty</groupId>
|
||||
<artifactId>jetty-util</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.hadoop</groupId>
|
||||
<artifactId>hadoop-minicluster</artifactId>
|
||||
<version>${version.hadoop}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.mortbay.jetty</groupId>
|
||||
<artifactId>jetty</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.mortbay.jetty</groupId>
|
||||
<artifactId>jetty-util</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.javassist</groupId>
|
||||
<artifactId>javassist</artifactId>
|
||||
<version>${version.javassist}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
<version>${version.security}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
<version>${version.security}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${version.slf4j}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.3</version>
|
||||
<configuration>
|
||||
<source>${maven.compiler.source}</source>
|
||||
<target>${maven.compiler.target}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<version>3.0.3</version>
|
||||
<configuration>
|
||||
<effort>Max</effort>
|
||||
<threshold>Medium</threshold>
|
||||
<xmlOutput>true</xmlOutput>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
<phase>prepare-package</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,152 +0,0 @@
|
||||
{
|
||||
"id": 4,
|
||||
"title": "Standalone Test",
|
||||
"originalTitle": "Standalone Test",
|
||||
"tags": [],
|
||||
"style": "dark",
|
||||
"timezone": "browser",
|
||||
"editable": true,
|
||||
"hideControls": false,
|
||||
"sharedCrosshair": false,
|
||||
"rows": [
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "Timely Standalone",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"id": 1,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 2,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 12,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"downsampleAggregator": "avg",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "",
|
||||
"errors": {},
|
||||
"metric": "timely.metrics.received",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"downsampleAggregator": "avg",
|
||||
"downsampleFillPolicy": "none",
|
||||
"errors": {},
|
||||
"metric": "timely.keys.meta.inserted",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"downsampleAggregator": "avg",
|
||||
"downsampleFillPolicy": "none",
|
||||
"errors": {},
|
||||
"metric": "timely.keys.metric.inserted",
|
||||
"refId": "C"
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Timely Status",
|
||||
"tooltip": {
|
||||
"msResolution": true,
|
||||
"shared": true,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "Row"
|
||||
}
|
||||
],
|
||||
"time": {
|
||||
"from": "now-1h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"now": true,
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
],
|
||||
"time_options": [
|
||||
"5m",
|
||||
"15m",
|
||||
"1h",
|
||||
"6h",
|
||||
"12h",
|
||||
"24h",
|
||||
"2d",
|
||||
"7d",
|
||||
"30d"
|
||||
]
|
||||
},
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"annotations": {
|
||||
"list": []
|
||||
},
|
||||
"refresh": "1m",
|
||||
"schemaVersion": 12,
|
||||
"version": 3,
|
||||
"links": []
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ `uname` == "Darwin" ]]; then
|
||||
THIS_SCRIPT=`python -c 'import os,sys; print os.path.realpath(sys.argv[1])' $0`
|
||||
else
|
||||
THIS_SCRIPT=`readlink -f $0`
|
||||
fi
|
||||
|
||||
THIS_DIR="${THIS_SCRIPT%/*}"
|
||||
BASE_DIR=${THIS_DIR}/..
|
||||
CONF_DIR="${BASE_DIR}/conf"
|
||||
CONF_FILE="${CONF_DIR}/timely.properties"
|
||||
LIB_DIR="${BASE_DIR}/lib"
|
||||
NUM_SERVER_THREADS=4
|
||||
HOST="127.0.0.1"
|
||||
PORT=54321
|
||||
|
||||
CP=""
|
||||
SEP=""
|
||||
for j in ${LIB_DIR}/*.jar; do
|
||||
CP="${CP}${SEP}${j}"
|
||||
SEP=":"
|
||||
done
|
||||
|
||||
JVM_ARGS="-Xmx128m -Xms128m -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS} -Dlog4j.configurationFile=${THIS_DIR}/log4j2.xml"
|
||||
|
||||
echo "$JAVA_HOME/bin/java -classpath ${CP} ${JVM_ARGS} timely.util.InsertTestData ${HOST} ${PORT} --fast"
|
||||
exec $JAVA_HOME/bin/java -classpath ${CP} ${JVM_ARGS} timely.util.InsertTestData ${HOST} ${PORT} --fast
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ `uname` == "Darwin" ]]; then
|
||||
THIS_SCRIPT=`python -c 'import os,sys; print os.path.realpath(sys.argv[1])' $0`
|
||||
else
|
||||
THIS_SCRIPT=`readlink -f $0`
|
||||
fi
|
||||
|
||||
THIS_DIR="${THIS_SCRIPT%/*}"
|
||||
BASE_DIR=${THIS_DIR}/..
|
||||
CONF_DIR="${BASE_DIR}/conf"
|
||||
CONF_FILE="${CONF_DIR}/timely.properties"
|
||||
LIB_DIR="${BASE_DIR}/lib"
|
||||
NUM_SERVER_THREADS=4
|
||||
HOST="127.0.0.1"
|
||||
PORT=54321
|
||||
|
||||
CP=""
|
||||
SEP=""
|
||||
for j in ${LIB_DIR}/*.jar; do
|
||||
CP="${CP}${SEP}${j}"
|
||||
SEP=":"
|
||||
done
|
||||
|
||||
JVM_ARGS="-Xmx128m -Xms128m -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS} -Dlog4j.configurationFile=${THIS_DIR}/log4j2.xml"
|
||||
|
||||
echo "$JAVA_HOME/bin/java -classpath ${CP} ${JVM_ARGS} timely.util.InsertTestData ${HOST} ${PORT}"
|
||||
exec $JAVA_HOME/bin/java -classpath ${CP} ${JVM_ARGS} timely.util.InsertTestData ${HOST} ${PORT}
|
||||
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="warn" monitorInterval="60">
|
||||
<Appenders>
|
||||
<RollingRandomAccessFile name="Rolling" fileName="logs/timely-server.log"
|
||||
filePattern="logs/$${date:yyyy-MM}/timely-server-%d{MM-dd-yyyy}-%i.log.gz">
|
||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
||||
<Policies>
|
||||
<SizeBasedTriggeringPolicy size="1024 MB" />
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy max="10" />
|
||||
</RollingRandomAccessFile>
|
||||
<Console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="trace" includeLocation="false">
|
||||
<AppenderRef ref="Console" level="warn" />
|
||||
<AppenderRef ref="Rolling" level="trace" />
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
@@ -1,37 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ `uname` == "Darwin" ]]; then
|
||||
THIS_SCRIPT=`python -c 'import os,sys; print os.path.realpath(sys.argv[1])' $0`
|
||||
else
|
||||
THIS_SCRIPT=`readlink -f $0`
|
||||
fi
|
||||
|
||||
THIS_DIR="${THIS_SCRIPT%/*}"
|
||||
NATIVE_DIR="${THIS_DIR}/META-INF/native"
|
||||
BASE_DIR=${THIS_DIR}/..
|
||||
TMP_DIR="${BASE_DIR}/tmp"
|
||||
CONF_DIR="${BASE_DIR}/conf"
|
||||
CONF_FILE="${CONF_DIR}/timely.properties"
|
||||
LIB_DIR="${BASE_DIR}/lib"
|
||||
NUM_SERVER_THREADS=4
|
||||
|
||||
if [[ -e ${TMP_DIR} ]]; then
|
||||
rm -rf ${TMP_DIR}
|
||||
fi
|
||||
mkdir ${TMP_DIR}
|
||||
|
||||
if [[ -e ${NATIVE_DIR} ]]; then
|
||||
rm -rf ${NATIVE_DIR}
|
||||
fi
|
||||
mkdir -p ${NATIVE_DIR}
|
||||
|
||||
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-tcnative*.jar META-INF/native/libnetty-tcnative.so
|
||||
|
||||
export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*"
|
||||
JVM_ARGS="-Xmx256m -Xms256m -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS} -Dlog4j.configurationFile=${THIS_DIR}/log4j2.xml"
|
||||
JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
|
||||
JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}/libnetty-tcnative.so"
|
||||
|
||||
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.Server "${CONF_FILE}""
|
||||
$JAVA_HOME/bin/java ${JVM_ARGS} timely.Server "${CONF_FILE}"
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ `uname` == "Darwin" ]]; then
|
||||
THIS_SCRIPT=`python -c 'import os,sys; print os.path.realpath(sys.argv[1])' $0`
|
||||
else
|
||||
THIS_SCRIPT=`readlink -f $0`
|
||||
fi
|
||||
|
||||
THIS_DIR="${THIS_SCRIPT%/*}"
|
||||
NATIVE_DIR="${THIS_DIR}/META-INF/native"
|
||||
BASE_DIR=${THIS_DIR}/..
|
||||
TMP_DIR="${BASE_DIR}/tmp"
|
||||
CONF_DIR="${BASE_DIR}/conf"
|
||||
CONF_FILE="${CONF_DIR}/timely-standalone.properties"
|
||||
LIB_DIR="${BASE_DIR}/lib"
|
||||
NUM_SERVER_THREADS=4
|
||||
|
||||
if [[ -e ${TMP_DIR} ]]; then
|
||||
rm -rf ${TMP_DIR}
|
||||
fi
|
||||
mkdir ${TMP_DIR}
|
||||
|
||||
if [[ -e ${NATIVE_DIR} ]]; then
|
||||
rm -rf ${NATIVE_DIR}
|
||||
fi
|
||||
mkdir -p ${NATIVE_DIR}
|
||||
|
||||
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-tcnative*.jar META-INF/native/libnetty-tcnative.so
|
||||
|
||||
export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*"
|
||||
JVM_ARGS="-Xmx256m -Xms256m -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS} -Dlog4j.configurationFile=${THIS_DIR}/log4j2.xml"
|
||||
JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
|
||||
JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}/libnetty-tcnative.so"
|
||||
#JVM_ARGS="${JVM_ARGS} -agentlib:jdwp=transport=dt_socket,address=54323,server=y,suspend=y"
|
||||
|
||||
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.StandaloneServer "${CONF_FILE}" "${TMP_DIR}""
|
||||
$JAVA_HOME/bin/java ${JVM_ARGS} timely.StandaloneServer "${CONF_FILE}" "${TMP_DIR}"
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
<beans:beans xmlns="http://www.springframework.org/schema/security"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:util="http://www.springframework.org/schema/util"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://www.springframework.org/schema/security
|
||||
http://www.springframework.org/schema/security/spring-security.xsd
|
||||
http://www.springframework.org/schema/util
|
||||
http://www.springframework.org/schema/util/spring-util.xsd">
|
||||
|
||||
<beans:bean id="x509PrincipalExtractor" class="org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor">
|
||||
<beans:property name="subjectDnRegex" value="CN=(.*?)(?:,|$)"/>
|
||||
</beans:bean>
|
||||
|
||||
<beans:bean id="timelyUserDetailsService" class="timely.auth.UserDetailsService">
|
||||
<beans:property name="users">
|
||||
<util:map key-type="java.lang.String">
|
||||
<beans:entry key="example.com">
|
||||
<util:list>
|
||||
<beans:value>D</beans:value>
|
||||
<beans:value>E</beans:value>
|
||||
<beans:value>F</beans:value>
|
||||
</util:list>
|
||||
</beans:entry>
|
||||
</util:map>
|
||||
</beans:property>
|
||||
</beans:bean>
|
||||
|
||||
<beans:bean id="x509AuthorizationProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
|
||||
<beans:property name="preAuthenticatedUserDetailsService" ref="timelyUserDetailsService" />
|
||||
</beans:bean>
|
||||
|
||||
<user-service id="usernamePasswordService">
|
||||
<user name="test" password="test1" authorities="A,B,C" />
|
||||
</user-service>
|
||||
|
||||
<beans:bean id="UserNamePasswordAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
|
||||
<beans:property name="userDetailsService" ref="usernamePasswordService"/>
|
||||
</beans:bean>
|
||||
|
||||
<authentication-manager alias="authenticationManager">
|
||||
<!-- X509 PreAuthenticated authorization provider -->
|
||||
<authentication-provider ref="x509AuthorizationProvider"/>
|
||||
<!-- Basic authorization provider, validates provided username and password -->
|
||||
<authentication-provider ref="UserNamePasswordAuthenticationProvider" />
|
||||
</authentication-manager>
|
||||
|
||||
</beans:beans>
|
||||
@@ -1,35 +0,0 @@
|
||||
timely.ip=127.0.0.1
|
||||
timely.port.put=54321
|
||||
timely.port.query=54322
|
||||
timely.instance_name=TimelyStandalone
|
||||
timely.zookeepers=127.0.0.1:9804
|
||||
timely.username=root
|
||||
timely.password=secret
|
||||
timely.table=timely.metrics
|
||||
timely.meta=timely.meta
|
||||
timely.write.latency=5s
|
||||
timely.write.threads=3
|
||||
timely.write.buffer.size=52428800
|
||||
timely.metric.age.off.days=7
|
||||
timely.cors.allow.any.origin=true
|
||||
timely.cors.allow.null.origin=false
|
||||
timely.cors.allowed.origins=
|
||||
timely.cors.allowed.methods=DELETE,GET,HEAD,OPTIONS,PUT,POST
|
||||
timely.cors.allowed.headers=content-type
|
||||
timely.cors.allow.credentials=true
|
||||
timely.scanner.threads=4
|
||||
timely.metrics.report.tags.ignored=
|
||||
timely.meta.cache.expiration.minutes=60
|
||||
timely.meta.cache.initial.capacity=2000
|
||||
timely.meta.cache.max.capacity=10000
|
||||
#timely.ssl.certificate.file=
|
||||
#timely.ssl.key.file=
|
||||
#timely.ssl.key.pass=
|
||||
timely.ssl.use.generated.keypair=true
|
||||
#timely.ssl.trust.store.file=
|
||||
timely.ssl.use.openssl=true
|
||||
#timely.ssl.use.ciphers=
|
||||
timely.session.max.age=86400
|
||||
timely.http.address=localhost
|
||||
grafana.http.address=http://localhost:3000/login
|
||||
timely.allow.anonymous.access=true
|
||||
@@ -1,35 +0,0 @@
|
||||
timely.ip=
|
||||
timely.port.put=
|
||||
timely.port.query=
|
||||
timely.instance_name=
|
||||
timely.zookeepers=
|
||||
timely.username=
|
||||
timely.password=
|
||||
timely.table=timely.metrics
|
||||
timely.meta=timely.meta
|
||||
timely.write.latency=5s
|
||||
timely.write.threads=3
|
||||
timely.write.buffer.size=52428800
|
||||
timely.metric.age.off.days=7
|
||||
timely.cors.allow.any.origin=false
|
||||
timely.cors.allow.null.origin=false
|
||||
timely.cors.allowed.origins=
|
||||
timely.cors.allowed.methods=DELETE,GET,HEAD,OPTIONS,PUT,POST
|
||||
timely.cors.allowed.headers=content-type
|
||||
timely.cors.allow.credentials=true
|
||||
timely.scanner.threads=4
|
||||
timely.metrics.report.tags.ignored=
|
||||
timely.meta.cache.expiration.minutes=60
|
||||
timely.meta.cache.initial.capacity=2000
|
||||
timely.meta.cache.max.capacity=10000
|
||||
timely.ssl.certificate.file=
|
||||
timely.ssl.key.file=
|
||||
timely.ssl.key.pass=
|
||||
timely.ssl.use.generated.keypair=false
|
||||
timely.ssl.trust.store.file=
|
||||
timely.ssl.use.openssl=true
|
||||
timely.ssl.use.ciphers=
|
||||
timely.session.max.age=86400
|
||||
timely.http.address=
|
||||
grafana.http.address=
|
||||
timely.allow.anonymous.access=false
|
||||
-468
@@ -1,468 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<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>
|
||||
<artifactId>timely-server</artifactId>
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.1</version>
|
||||
</parent>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jdk8</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-configuration</groupId>
|
||||
<artifactId>commons-configuration</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lmax</groupId>
|
||||
<artifactId>disruptor</artifactId>
|
||||
<version>3.3.2</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-tcnative</artifactId>
|
||||
<version>1.1.33.Fork15</version>
|
||||
<classifier>${os.detected.classifier}</classifier>
|
||||
<!-- Eclipse doesn't like this, but it works on the cmd line -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.accumulo</groupId>
|
||||
<artifactId>accumulo-core</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.accumulo</groupId>
|
||||
<artifactId>accumulo-minicluster</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.hadoop</groupId>
|
||||
<artifactId>hadoop-common</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.hadoop</groupId>
|
||||
<artifactId>hadoop-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.hadoop</groupId>
|
||||
<artifactId>hadoop-minicluster</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.javassist</groupId>
|
||||
<artifactId>javassist</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<extensions>
|
||||
<!-- Use os-maven-plugin to initialize the "os.detected" properties -->
|
||||
<extension>
|
||||
<groupId>kr.motd.maven</groupId>
|
||||
<artifactId>os-maven-plugin</artifactId>
|
||||
<version>1.5.0.Final</version>
|
||||
</extension>
|
||||
</extensions>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>assemble</id>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<phase>package</phase>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>src/main/dist/assembly.xml</descriptor>
|
||||
</descriptors>
|
||||
<tarLongFileMode>posix</tarLongFileMode>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>2.10</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-dependencies</id>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<phase>prepare-package</phase>
|
||||
<configuration>
|
||||
<excludeArtifactIds>junit</excludeArtifactIds>
|
||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>unpack-native</id>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<phase>process-test-classes</phase>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-tcnative</artifactId>
|
||||
<version>1.1.33.Fork15</version>
|
||||
<classifier>${os.detected.classifier}</classifier>
|
||||
<type>jar</type>
|
||||
<overWrite>false</overWrite>
|
||||
<outputDirectory>${project.build.directory}/native</outputDirectory>
|
||||
<includes>**/*.so</includes>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>2.19</version>
|
||||
<configuration>
|
||||
<argLine>${failsafeArgLine} -Djava.library.path=${project.build.dir}/native/META-INF/native/libnetty-tcnative.so -Dzookeeper.sasl.client=false</argLine>
|
||||
<includes>
|
||||
<include>**/*.java</include>
|
||||
</includes>
|
||||
<groups>timely.test.IntegrationTest</groups>
|
||||
<redirectTestOutputToFile>true</redirectTestOutputToFile>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>integration-test</goal>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<effort>Max</effort>
|
||||
<threshold>Medium</threshold>
|
||||
<xmlOutput>true</xmlOutput>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
<phase>prepare-package</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.19</version>
|
||||
<configuration>
|
||||
<argLine>${sureFireArgLine} -Djava.library.path=${project.build.dir}/native/META-INF/native/libnetty-tcnative.so -Dzookeeper.sasl.client=false</argLine>
|
||||
<excludedGroups>timely.test.IntegrationTest</excludedGroups>
|
||||
<redirectTestOutputToFile>true</redirectTestOutputToFile>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.7.6.201602180812</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>jacoco-prepare-ut</id>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
<phase>process-test-classes</phase>
|
||||
<configuration>
|
||||
<destFile>${project.build.directory}/coverage/jacoco-ut.exec</destFile>
|
||||
<propertyName>sureFireArgLine</propertyName>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>jacoco-prepare-it</id>
|
||||
<goals>
|
||||
<goal>prepare-agent-integration</goal>
|
||||
</goals>
|
||||
<phase>pre-integration-test</phase>
|
||||
<configuration>
|
||||
<destFile>${project.build.directory}/coverage/jacoco-it.exec</destFile>
|
||||
<propertyName>failsafeArgLine</propertyName>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>net.revelc.code</groupId>
|
||||
<artifactId>formatter-maven-plugin</artifactId>
|
||||
<version>0.5.2</version>
|
||||
<configuration>
|
||||
<configFile>${project.parent.basedir}/eclipse/Eclipse-Formatter.xml</configFile>
|
||||
<lineEnding>LF</lineEnding>
|
||||
<overrideConfigCompilerVersion>true</overrideConfigCompilerVersion>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>format</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<!--This plugin's configuration is used to store Eclipse m2e settings
|
||||
only. It has no influence on the Maven build itself. -->
|
||||
<plugin>
|
||||
<groupId>org.eclipse.m2e</groupId>
|
||||
<artifactId>lifecycle-mapping</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<configuration>
|
||||
<lifecycleMappingMetadata>
|
||||
<pluginExecutions>
|
||||
<pluginExecution>
|
||||
<pluginExecutionFilter>
|
||||
<groupId>net.revelc.code</groupId>
|
||||
<artifactId>
|
||||
formatter-maven-plugin
|
||||
</artifactId>
|
||||
<versionRange>
|
||||
[0.5.2,)
|
||||
</versionRange>
|
||||
<goals>
|
||||
<goal>format</goal>
|
||||
</goals>
|
||||
</pluginExecutionFilter>
|
||||
<action>
|
||||
<ignore/>
|
||||
</action>
|
||||
</pluginExecution>
|
||||
<pluginExecution>
|
||||
<pluginExecutionFilter>
|
||||
<groupId>
|
||||
org.apache.maven.plugins
|
||||
</groupId>
|
||||
<artifactId>
|
||||
maven-dependency-plugin
|
||||
</artifactId>
|
||||
<versionRange>
|
||||
[2.10,)
|
||||
</versionRange>
|
||||
<goals>
|
||||
<goal>
|
||||
copy-dependencies
|
||||
</goal>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
</pluginExecutionFilter>
|
||||
<action>
|
||||
<ignore/>
|
||||
</action>
|
||||
</pluginExecution>
|
||||
</pluginExecutions>
|
||||
</lifecycleMappingMetadata>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>rpm</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>rpm-maven-plugin</artifactId>
|
||||
<version>2.1.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>generate-rpm</id>
|
||||
<goals>
|
||||
<goal>rpm</goal>
|
||||
</goals>
|
||||
<phase>package</phase>
|
||||
<configuration>
|
||||
<name>timely-server</name>
|
||||
<group>Applications/System</group>
|
||||
<prefix>/opt/timely</prefix>
|
||||
<prefixes>
|
||||
<prefix>INSTALL_DIR</prefix>
|
||||
</prefixes>
|
||||
<mappings>
|
||||
<mapping>
|
||||
<directory>/opt/timely/bin</directory>
|
||||
<filemode>600</filemode>
|
||||
<sources>
|
||||
<source>
|
||||
<location>${project.basedir}/bin</location>
|
||||
<excludes>
|
||||
<excludes>*.sh</excludes>
|
||||
</excludes>
|
||||
</source>
|
||||
</sources>
|
||||
</mapping>
|
||||
<mapping>
|
||||
<directory>/opt/timely/bin</directory>
|
||||
<filemode>700</filemode>
|
||||
<sources>
|
||||
<source>
|
||||
<location>${project.basedir}/bin</location>
|
||||
<includes>
|
||||
<include>*.sh</include>
|
||||
</includes>
|
||||
</source>
|
||||
</sources>
|
||||
</mapping>
|
||||
<mapping>
|
||||
<directory>/opt/timely/conf</directory>
|
||||
<filemode>600</filemode>
|
||||
<sources>
|
||||
<source>
|
||||
<location>${project.basedir}/conf</location>
|
||||
<includes>
|
||||
<include>*</include>
|
||||
</includes>
|
||||
</source>
|
||||
</sources>
|
||||
</mapping>
|
||||
<mapping>
|
||||
<directory>/opt/timely/lib</directory>
|
||||
<filemode>600</filemode>
|
||||
<sources>
|
||||
<source>
|
||||
<location>${project.basedir}/target/lib</location>
|
||||
</source>
|
||||
<source>
|
||||
<location>${project.basedir}/target/${project.artifactId}-${project.version}.jar</location>
|
||||
</source>
|
||||
</sources>
|
||||
</mapping>
|
||||
</mappings>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<version>3.0.3</version>
|
||||
<configuration>
|
||||
<effort>Max</effort>
|
||||
<threshold>Medium</threshold>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-pmd-plugin</artifactId>
|
||||
<version>3.6</version>
|
||||
<configuration>
|
||||
<targetJdk>1.8</targetJdk>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.7.6.201602180812</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<id>JaCoCo Unit Test Coverage</id>
|
||||
<configuration>
|
||||
<dataFile>${project.build.directory}/coverage/jacoco-ut.exec</dataFile>
|
||||
</configuration>
|
||||
<reports>
|
||||
<report>report</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
<reportSet>
|
||||
<id>JaCoCo Integration Test Coverage</id>
|
||||
<configuration>
|
||||
<dataFile>${project.build.directory}/coverage/jacoco-it.exec</dataFile>
|
||||
</configuration>
|
||||
<reports>
|
||||
<report>report-integration</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
</reportSets>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
</project>
|
||||
Vendored
-47
@@ -1,47 +0,0 @@
|
||||
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
|
||||
<id>dist</id>
|
||||
<formats>
|
||||
<format>tar.gz</format>
|
||||
</formats>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}</directory>
|
||||
<outputDirectory></outputDirectory>
|
||||
<includes>
|
||||
<include>README*</include>
|
||||
<include>LICENSE*</include>
|
||||
<include>NOTICE*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.build.directory}</directory>
|
||||
<outputDirectory>lib</outputDirectory>
|
||||
<includes>
|
||||
<include>timely*.jar</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.build.directory}/lib</directory>
|
||||
<outputDirectory>lib</outputDirectory>
|
||||
<includes>
|
||||
<include>*.jar</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}/bin</directory>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
<includes>
|
||||
<include>*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}/conf</directory>
|
||||
<outputDirectory>conf</outputDirectory>
|
||||
<includes>
|
||||
<include>*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
||||
@@ -1,180 +0,0 @@
|
||||
package timely;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.accumulo.core.client.BatchWriterConfig;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hadoop.fs.FileSystem;
|
||||
import org.apache.hadoop.fs.Path;
|
||||
|
||||
public class Configuration {
|
||||
|
||||
public static final String IP = "timely.ip";
|
||||
public static final String PUT_PORT = "timely.port.put";
|
||||
public static final String QUERY_PORT = "timely.port.query";
|
||||
public static final String ZOOKEEPERS = "timely.zookeepers";
|
||||
public static final String INSTANCE_NAME = "timely.instance_name";
|
||||
public static final String USERNAME = "timely.username";
|
||||
public static final String PASSWORD = "timely.password";
|
||||
public static final String METRICS_TABLE = "timely.table";
|
||||
private static final String METRICS_TABLE_DEFAULT = "timely.metrics";
|
||||
public static final String META_TABLE = "timely.meta";
|
||||
private static final String META_TABLE_DEFAULT = "timely.meta";
|
||||
public static final String METRICS_AGEOFF_DAYS = "timely.metric.age.off.days";
|
||||
private static final String METRICS_AGEOFF_DAYS_DEFAULT = "7";
|
||||
|
||||
public static final String MAX_LATENCY = "timely.write.latency";
|
||||
private static final String MAX_LATENCY_DEFAULT = "5s";
|
||||
public static final String WRITE_THREADS = "timely.write.threads";
|
||||
public static final String WRITE_BUFFER_SIZE = "timely.write.buffer.size";
|
||||
public static final String SCANNER_THREADS = "timely.scanner.threads";
|
||||
|
||||
public static final String CORS_ALLOW_ANY_ORIGIN = "timely.cors.allow.any.origin";
|
||||
private static final String CORS_ALLOW_ANY_ORIGIN_DEFAULT = "false";
|
||||
public static final String CORS_ALLOW_NULL_ORIGIN = "timely.cors.allow.null.origin";
|
||||
private static final String CORS_ALLOW_NULL_ORIGIN_DEFAULT = "false";
|
||||
public static final String CORS_ALLOWED_ORIGINS = "timely.cors.allowed.origins";
|
||||
private static final String CORS_ALLOWED_ORIGINS_DEFAULT = "";
|
||||
public static final String CORS_ALLOWED_METHODS = "timely.cors.allowed.methods";
|
||||
private static final String CORS_ALLOWED_METHODS_DEFAULT = "DELETE,GET,HEAD,OPTIONS,PUT,POST";
|
||||
public static final String CORS_ALLOWED_HEADERS = "timely.cors.allowed.headers";
|
||||
private static final String CORS_ALLOWED_HEADERS_DEFAULT = "content-type";
|
||||
public static final String CORS_ALLOW_CREDENTIALS = "timely.cors.allow.credentials";
|
||||
private static final String CORS_ALLOW_CREDENTIALS_DEFAULT = "true";
|
||||
|
||||
public static final String METRICS_IGNORED_TAGS = "timely.metrics.report.tags.ignored";
|
||||
|
||||
public static final String META_CACHE_EXPIRATION = "timely.meta.cache.expiration.minutes";
|
||||
public static final Long META_CACHE_EXPIRATION_DEFAULT = 60L;
|
||||
public static final String META_CACHE_INITIAL_CAPACITY = "timely.meta.cache.initial.capacity";
|
||||
public static final Integer META_CACHE_INITIAL_CAPACITY_DEFAULT = 2000;
|
||||
public static final String META_CACHE_MAX_CAPACITY = "timely.meta.cache.max.capacity";
|
||||
public static final Integer META_CACHE_MAX_CAPACITY_DEFAULT = 10000;
|
||||
|
||||
public static final String VISIBILITY_CACHE_EXPIRATION = "timely.visibility.cache.expiration.minutes";
|
||||
public static final Long VISIBILITY_EXPIRATION_DEFAULT = 60L;
|
||||
public static final String VISIBILITY_CACHE_INITIAL_CAPACITY = "timely.visibility.cache.initial.capacity";
|
||||
public static final Integer VISIBILITY_CACHE_INITIAL_CAPACITY_DEFAULT = 2000;
|
||||
public static final String VISIBILITY_CACHE_MAX_CAPACITY = "timely.visibility.cache.max.capacity";
|
||||
public static final Integer VISIBILITY_CACHE_MAX_CAPACITY_DEFAULT = 10000;
|
||||
|
||||
/** Security properties */
|
||||
public static final String SSL_CERTIFICATE_FILE = "timely.ssl.certificate.file";
|
||||
public static final String SSL_PRIVATE_KEY_FILE = "timely.ssl.key.file";
|
||||
public static final String SSL_PRIVATE_KEY_PASS = "timely.ssl.key.pass";
|
||||
public static final String SSL_USE_GENERATED_KEYPAIR = "timely.ssl.use.generated.keypair";
|
||||
private static final String SSL_USE_GENERATED_KEYPAIR_DEFAULT = "false";
|
||||
public static final String SSL_TRUST_STORE_FILE = "timely.ssl.trust.store.file";
|
||||
public static final String SSL_USE_OPENSSL = "timely.ssl.use.openssl";
|
||||
private static final String SSL_USE_OPENSSL_DEFAULT = "true";
|
||||
public static final String SSL_USE_CIPHERS = "timely.ssl.use.ciphers";
|
||||
private static final String SSL_USE_CIPHERS_DEFAULT = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:"
|
||||
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:TLS_RSA_WITH_AES_128_GCM_SHA256:"
|
||||
+ "TLS_RSA_WITH_AES_128_CBC_SHA:SSL_RSA_WITH_3DES_EDE_CBC_SHA";
|
||||
|
||||
public static final String SESSION_MAX_AGE = "timely.session.max.age";
|
||||
public static final long SESSION_MAX_AGE_DEFAULT = 86400;
|
||||
public static final String TIMELY_HTTP_ADDRESS = "timely.http.address";
|
||||
public static final String GRAFANA_HTTP_ADDRESS = "grafana.http.address";
|
||||
public static final String ALLOW_ANONYMOUS_ACCESS = "timely.allow.anonymous.access";
|
||||
private static final String ALLOW_ANONYMOUS_ACCESS_DEFAULT = "false";
|
||||
|
||||
private static final List<String> REQUIRED_PROPERTIES = new ArrayList<>();
|
||||
static {
|
||||
REQUIRED_PROPERTIES.add(IP);
|
||||
REQUIRED_PROPERTIES.add(PUT_PORT);
|
||||
REQUIRED_PROPERTIES.add(QUERY_PORT);
|
||||
REQUIRED_PROPERTIES.add(ZOOKEEPERS);
|
||||
REQUIRED_PROPERTIES.add(INSTANCE_NAME);
|
||||
REQUIRED_PROPERTIES.add(USERNAME);
|
||||
REQUIRED_PROPERTIES.add(PASSWORD);
|
||||
REQUIRED_PROPERTIES.add(TIMELY_HTTP_ADDRESS);
|
||||
REQUIRED_PROPERTIES.add(GRAFANA_HTTP_ADDRESS);
|
||||
};
|
||||
|
||||
private final Properties props = new Properties();
|
||||
|
||||
public Configuration(Path path) throws IOException {
|
||||
org.apache.hadoop.conf.Configuration hadoopConf = new org.apache.hadoop.conf.Configuration();
|
||||
try (FileSystem fs = FileSystem.get(hadoopConf)) {
|
||||
try (InputStream inputStream = fs.open(path)) {
|
||||
init(inputStream);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Configuration(File conf) throws IOException {
|
||||
try (InputStream inputStream = new FileInputStream(conf)) {
|
||||
init(inputStream);
|
||||
}
|
||||
}
|
||||
|
||||
private void init(InputStream configStream) throws IOException {
|
||||
// Add defaults
|
||||
props.setProperty(METRICS_TABLE, METRICS_TABLE_DEFAULT);
|
||||
props.setProperty(META_TABLE, META_TABLE_DEFAULT);
|
||||
props.setProperty(MAX_LATENCY, MAX_LATENCY_DEFAULT);
|
||||
props.setProperty(METRICS_AGEOFF_DAYS, METRICS_AGEOFF_DAYS_DEFAULT);
|
||||
final BatchWriterConfig defaults = new BatchWriterConfig();
|
||||
props.setProperty(WRITE_BUFFER_SIZE, "" + defaults.getMaxMemory());
|
||||
props.setProperty(WRITE_THREADS, "" + defaults.getMaxWriteThreads());
|
||||
props.setProperty(SCANNER_THREADS, "" + 4);
|
||||
props.setProperty(CORS_ALLOW_ANY_ORIGIN, CORS_ALLOW_ANY_ORIGIN_DEFAULT);
|
||||
props.setProperty(CORS_ALLOW_NULL_ORIGIN, CORS_ALLOW_NULL_ORIGIN_DEFAULT);
|
||||
props.setProperty(CORS_ALLOWED_ORIGINS, CORS_ALLOWED_ORIGINS_DEFAULT);
|
||||
props.setProperty(CORS_ALLOWED_METHODS, CORS_ALLOWED_METHODS_DEFAULT);
|
||||
props.setProperty(CORS_ALLOWED_HEADERS, CORS_ALLOWED_HEADERS_DEFAULT);
|
||||
props.setProperty(CORS_ALLOW_CREDENTIALS, CORS_ALLOW_CREDENTIALS_DEFAULT);
|
||||
props.setProperty(META_CACHE_EXPIRATION, "" + META_CACHE_EXPIRATION_DEFAULT);
|
||||
props.setProperty(META_CACHE_INITIAL_CAPACITY, "" + META_CACHE_INITIAL_CAPACITY_DEFAULT);
|
||||
props.setProperty(META_CACHE_MAX_CAPACITY, "" + META_CACHE_MAX_CAPACITY_DEFAULT);
|
||||
props.setProperty(SSL_USE_GENERATED_KEYPAIR, SSL_USE_GENERATED_KEYPAIR_DEFAULT);
|
||||
props.setProperty(SSL_USE_OPENSSL, SSL_USE_OPENSSL_DEFAULT);
|
||||
props.setProperty(SSL_USE_CIPHERS, SSL_USE_CIPHERS_DEFAULT);
|
||||
props.setProperty(ALLOW_ANONYMOUS_ACCESS, ALLOW_ANONYMOUS_ACCESS_DEFAULT);
|
||||
props.setProperty(SESSION_MAX_AGE, SESSION_MAX_AGE_DEFAULT + "");
|
||||
|
||||
props.setProperty(VISIBILITY_CACHE_EXPIRATION, VISIBILITY_EXPIRATION_DEFAULT + "");
|
||||
props.setProperty(VISIBILITY_CACHE_INITIAL_CAPACITY, VISIBILITY_CACHE_INITIAL_CAPACITY_DEFAULT + "");
|
||||
props.setProperty(VISIBILITY_CACHE_MAX_CAPACITY, VISIBILITY_CACHE_MAX_CAPACITY_DEFAULT + "");
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(configStream, StandardCharsets.UTF_8))) {
|
||||
props.load(reader);
|
||||
validate();
|
||||
}
|
||||
}
|
||||
|
||||
protected void validate() {
|
||||
REQUIRED_PROPERTIES.forEach(p -> {
|
||||
if (!props.containsKey(p) || StringUtils.isEmpty(props.getProperty(p))) {
|
||||
throw new IllegalArgumentException("Required property " + p + " must be specified.");
|
||||
}
|
||||
});
|
||||
if (!getBoolean(Configuration.SSL_USE_GENERATED_KEYPAIR)) {
|
||||
if (StringUtils.isEmpty(get(Configuration.SSL_CERTIFICATE_FILE))) {
|
||||
throw new IllegalArgumentException(Configuration.SSL_CERTIFICATE_FILE + "must be specified.");
|
||||
}
|
||||
if (StringUtils.isEmpty(get(Configuration.SSL_PRIVATE_KEY_FILE))) {
|
||||
throw new IllegalArgumentException(Configuration.SSL_PRIVATE_KEY_FILE + "must be specified.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String get(String property) {
|
||||
return props.getProperty(property);
|
||||
}
|
||||
|
||||
public boolean getBoolean(String property) {
|
||||
return Boolean.parseBoolean(get(property));
|
||||
}
|
||||
}
|
||||
@@ -1,376 +0,0 @@
|
||||
package timely;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.epoll.EpollEventLoopGroup;
|
||||
import io.netty.channel.epoll.EpollServerSocketChannel;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.ServerSocketChannel;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
|
||||
import io.netty.handler.codec.Delimiters;
|
||||
import io.netty.handler.codec.http.HttpContentCompressor;
|
||||
import io.netty.handler.codec.http.HttpContentDecompressor;
|
||||
import io.netty.handler.codec.http.HttpMethod;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||
import io.netty.handler.codec.http.cors.CorsConfig;
|
||||
import io.netty.handler.codec.http.cors.CorsHandler;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import io.netty.handler.ssl.ClientAuth;
|
||||
import io.netty.handler.ssl.OpenSslServerContext;
|
||||
import io.netty.handler.ssl.OpenSslServerSessionContext;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.ssl.SslProvider;
|
||||
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||
import io.netty.util.internal.SystemPropertyUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import timely.api.query.response.TimelyException;
|
||||
import timely.auth.AuthCache;
|
||||
import timely.auth.VisibilityCache;
|
||||
import timely.netty.http.HttpAggregatorsRequestHandler;
|
||||
import timely.netty.http.HttpMetricsRequestHandler;
|
||||
import timely.netty.http.HttpQueryDecoder;
|
||||
import timely.netty.http.HttpQueryRequestHandler;
|
||||
import timely.netty.http.HttpSearchLookupRequestHandler;
|
||||
import timely.netty.http.HttpSuggestRequestHandler;
|
||||
import timely.netty.http.TimelyExceptionHandler;
|
||||
import timely.netty.http.login.BasicAuthLoginRequestHandler;
|
||||
import timely.netty.http.login.X509LoginRequestHandler;
|
||||
import timely.netty.tcp.TcpPutDecoder;
|
||||
import timely.netty.tcp.TcpPutHandler;
|
||||
import timely.store.DataStore;
|
||||
import timely.store.DataStoreFactory;
|
||||
import timely.store.MetaCacheFactory;
|
||||
|
||||
public class Server {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Server.class);
|
||||
private static final int EPOLL_MIN_MAJOR_VERSION = 2;
|
||||
private static final int EPOLL_MIN_MINOR_VERSION = 6;
|
||||
private static final int EPOLL_MIN_PATCH_VERSION = 32;
|
||||
private static final String OS_NAME = "os.name";
|
||||
private static final String OS_VERSION = "os.version";
|
||||
|
||||
protected static final CountDownLatch LATCH = new CountDownLatch(1);
|
||||
|
||||
private Configuration config = null;
|
||||
private EventLoopGroup tcpWorkerGroup = null;
|
||||
private EventLoopGroup tcpBossGroup = null;
|
||||
private EventLoopGroup httpWorkerGroup = null;
|
||||
private EventLoopGroup httpBossGroup = null;
|
||||
protected Channel putChannelHandle = null;
|
||||
protected Channel queryChannelHandle = null;
|
||||
protected DataStore dataStore = null;
|
||||
protected volatile boolean shutdown = false;
|
||||
|
||||
private static boolean useEpoll() {
|
||||
|
||||
// Should we just return true if this is Linux and if we get an error
|
||||
// during Epoll
|
||||
// setup handle it there?
|
||||
final String os = SystemPropertyUtil.get(OS_NAME).toLowerCase().trim();
|
||||
final String[] version = SystemPropertyUtil.get(OS_VERSION).toLowerCase().trim().split("\\.");
|
||||
if (os.startsWith("linux") && version.length >= 3) {
|
||||
final int major = Integer.parseInt(version[0]);
|
||||
if (major > EPOLL_MIN_MAJOR_VERSION) {
|
||||
return true;
|
||||
} else if (major == EPOLL_MIN_MAJOR_VERSION) {
|
||||
final int minor = Integer.parseInt(version[1]);
|
||||
if (minor > EPOLL_MIN_MINOR_VERSION) {
|
||||
return true;
|
||||
} else if (minor == EPOLL_MIN_MINOR_VERSION) {
|
||||
final int patch = Integer.parseInt(version[2].substring(0, 2));
|
||||
if (patch >= EPOLL_MIN_PATCH_VERSION) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static String usage() {
|
||||
|
||||
return "Server <configFile>";
|
||||
}
|
||||
|
||||
public static void fatal(String msg, Throwable t) {
|
||||
LOG.error(msg, t);
|
||||
LATCH.countDown();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
if (args.length != 1) {
|
||||
System.err.println(usage());
|
||||
}
|
||||
final File conf = new File(args[0]);
|
||||
if (!conf.canRead()) {
|
||||
throw new RuntimeException("Configuration file does not exist or cannot be read");
|
||||
}
|
||||
Server s = new Server(conf);
|
||||
try {
|
||||
LATCH.await();
|
||||
} catch (final InterruptedException e) {
|
||||
LOG.info("Server shutting down.");
|
||||
} finally {
|
||||
s.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private void shutdownHook() {
|
||||
|
||||
final Runnable shutdownRunner = new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!shutdown) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
};
|
||||
final Thread hook = new Thread(shutdownRunner, "shutdown-hook-thread");
|
||||
Runtime.getRuntime().addShutdownHook(hook);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
try {
|
||||
putChannelHandle.close().sync();
|
||||
} catch (final Exception e) {
|
||||
LOG.error("Error shutting down tcp channel", e);
|
||||
}
|
||||
try {
|
||||
queryChannelHandle.close().sync();
|
||||
} catch (final Exception e) {
|
||||
LOG.error("Error shutting down http channel", e);
|
||||
}
|
||||
try {
|
||||
tcpBossGroup.shutdownGracefully().sync();
|
||||
} catch (final Exception e) {
|
||||
LOG.error("Error closing Netty TCP boss thread group", e);
|
||||
}
|
||||
try {
|
||||
tcpWorkerGroup.shutdownGracefully().sync();
|
||||
} catch (final Exception e) {
|
||||
LOG.error("Error closing Netty TCP worker thread group", e);
|
||||
}
|
||||
try {
|
||||
httpBossGroup.shutdownGracefully().sync();
|
||||
} catch (final Exception e) {
|
||||
LOG.error("Error closing Netty HTTP boss thread group", e);
|
||||
}
|
||||
try {
|
||||
httpWorkerGroup.shutdownGracefully().sync();
|
||||
} catch (final Exception e) {
|
||||
LOG.error("Error closing Netty HTTP worker thread group", e);
|
||||
}
|
||||
try {
|
||||
dataStore.flush();
|
||||
} catch (TimelyException e) {
|
||||
LOG.error("Error flushing to server during shutdown", e);
|
||||
}
|
||||
MetaCacheFactory.close();
|
||||
this.shutdown = true;
|
||||
LOG.info("Server shut down.");
|
||||
}
|
||||
|
||||
public Server(File conf) throws Exception {
|
||||
|
||||
config = new Configuration(conf);
|
||||
int nettyThreads = Math.max(1,
|
||||
SystemPropertyUtil.getInt("io.netty.eventLoopThreads", Runtime.getRuntime().availableProcessors() * 2));
|
||||
dataStore = DataStoreFactory.create(config, nettyThreads);
|
||||
// Initialize the MetaCache
|
||||
MetaCacheFactory.getCache(config);
|
||||
// initialize the auth cache
|
||||
AuthCache.setSessionMaxAge(config);
|
||||
// Initialize the VisibilityCache
|
||||
VisibilityCache.init(config);
|
||||
final boolean useEpoll = useEpoll();
|
||||
Class<? extends ServerSocketChannel> channelClass = null;
|
||||
if (useEpoll) {
|
||||
tcpWorkerGroup = new EpollEventLoopGroup();
|
||||
tcpBossGroup = new EpollEventLoopGroup();
|
||||
httpWorkerGroup = new EpollEventLoopGroup();
|
||||
httpBossGroup = new EpollEventLoopGroup();
|
||||
channelClass = EpollServerSocketChannel.class;
|
||||
} else {
|
||||
tcpWorkerGroup = new NioEventLoopGroup();
|
||||
tcpBossGroup = new NioEventLoopGroup();
|
||||
httpWorkerGroup = new NioEventLoopGroup();
|
||||
httpBossGroup = new NioEventLoopGroup();
|
||||
channelClass = NioServerSocketChannel.class;
|
||||
}
|
||||
LOG.info("Using channel class {}", channelClass.getSimpleName());
|
||||
|
||||
final ServerBootstrap putServer = new ServerBootstrap();
|
||||
putServer.group(tcpBossGroup, tcpWorkerGroup);
|
||||
putServer.channel(channelClass);
|
||||
putServer.handler(new LoggingHandler());
|
||||
putServer.childHandler(setupTcpChannel());
|
||||
putServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
|
||||
putServer.option(ChannelOption.SO_BACKLOG, 128);
|
||||
putServer.option(ChannelOption.SO_KEEPALIVE, true);
|
||||
final int putPort = Integer.parseInt(config.get(Configuration.PUT_PORT));
|
||||
putChannelHandle = putServer.bind(putPort).sync().channel();
|
||||
final String putAddress = ((InetSocketAddress) putChannelHandle.localAddress()).getAddress().getHostAddress();
|
||||
|
||||
final int queryPort = Integer.parseInt(config.get(Configuration.QUERY_PORT));
|
||||
SslContext sslCtx = createSSLContext(config);
|
||||
if (sslCtx instanceof OpenSslServerContext) {
|
||||
OpenSslServerContext openssl = (OpenSslServerContext) sslCtx;
|
||||
String application = "Timely_" + queryPort;
|
||||
OpenSslServerSessionContext opensslCtx = openssl.sessionContext();
|
||||
opensslCtx.setSessionCacheEnabled(true);
|
||||
opensslCtx.setSessionCacheSize(128);
|
||||
opensslCtx.setSessionIdContext(application.getBytes(StandardCharsets.UTF_8));
|
||||
opensslCtx.setSessionTimeout(Integer.parseInt(config.get(Configuration.SESSION_MAX_AGE)));
|
||||
}
|
||||
final ServerBootstrap queryServer = new ServerBootstrap();
|
||||
queryServer.group(httpBossGroup, httpWorkerGroup);
|
||||
queryServer.channel(channelClass);
|
||||
queryServer.handler(new LoggingHandler());
|
||||
queryServer.childHandler(setupHttpChannel(config, sslCtx));
|
||||
queryServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
|
||||
queryServer.option(ChannelOption.SO_BACKLOG, 128);
|
||||
queryServer.option(ChannelOption.SO_KEEPALIVE, true);
|
||||
queryChannelHandle = queryServer.bind(queryPort).sync().channel();
|
||||
final String queryAddress = ((InetSocketAddress) queryChannelHandle.localAddress()).getAddress()
|
||||
.getHostAddress();
|
||||
|
||||
shutdownHook();
|
||||
LOG.info("Server started. Listening on {}:{} for TCP traffic and {}:{} for HTTP traffic", putAddress, putPort,
|
||||
queryAddress, queryPort);
|
||||
}
|
||||
|
||||
protected SslContext createSSLContext(Configuration config) throws Exception {
|
||||
|
||||
Boolean generate = config.getBoolean(Configuration.SSL_USE_GENERATED_KEYPAIR);
|
||||
SslContextBuilder ssl = null;
|
||||
if (generate) {
|
||||
LOG.warn("Using generated self signed server certificate");
|
||||
Date begin = new Date();
|
||||
Date end = new Date(begin.getTime() + 86400000);
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate("localhost", begin, end);
|
||||
ssl = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey());
|
||||
} else {
|
||||
String cert = config.get(Configuration.SSL_CERTIFICATE_FILE);
|
||||
String key = config.get(Configuration.SSL_PRIVATE_KEY_FILE);
|
||||
String keyPass = config.get(Configuration.SSL_PRIVATE_KEY_PASS);
|
||||
if (null == cert || null == key) {
|
||||
throw new IllegalArgumentException("Check your SSL properties, something is wrong.");
|
||||
}
|
||||
ssl = SslContextBuilder.forServer(new File(cert), new File(key), keyPass);
|
||||
}
|
||||
|
||||
String ciphers = config.get(Configuration.SSL_USE_CIPHERS);
|
||||
ssl.ciphers(Arrays.asList(ciphers.split(":")));
|
||||
|
||||
// Can't set to REQUIRE because the CORS pre-flight requests will fail.
|
||||
ssl.clientAuth(ClientAuth.OPTIONAL);
|
||||
|
||||
Boolean useOpenSSL = config.getBoolean(Configuration.SSL_USE_OPENSSL);
|
||||
if (useOpenSSL) {
|
||||
ssl.sslProvider(SslProvider.OPENSSL);
|
||||
} else {
|
||||
ssl.sslProvider(SslProvider.JDK);
|
||||
}
|
||||
String trustStore = config.get(Configuration.SSL_TRUST_STORE_FILE);
|
||||
if (null != trustStore) {
|
||||
ssl.trustManager(new File(trustStore));
|
||||
}
|
||||
return ssl.build();
|
||||
}
|
||||
|
||||
protected ChannelHandler setupHttpChannel(Configuration config, SslContext sslCtx) {
|
||||
return new ChannelInitializer<SocketChannel>() {
|
||||
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) throws Exception {
|
||||
|
||||
ch.pipeline().addLast("ssl", sslCtx.newHandler(ch.alloc()));
|
||||
ch.pipeline().addLast("decompressor", new HttpContentDecompressor());
|
||||
ch.pipeline().addLast("decoder", new HttpRequestDecoder());
|
||||
ch.pipeline().addLast("encoder", new HttpResponseEncoder());
|
||||
ch.pipeline().addLast("deflater", new HttpContentCompressor());
|
||||
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(8192));
|
||||
final CorsConfig.Builder ccb;
|
||||
if (config.getBoolean(Configuration.CORS_ALLOW_ANY_ORIGIN)) {
|
||||
ccb = new CorsConfig.Builder();
|
||||
} else {
|
||||
String origins = config.get(Configuration.CORS_ALLOWED_ORIGINS);
|
||||
ccb = new CorsConfig.Builder(origins.split(","));
|
||||
}
|
||||
if (config.getBoolean(Configuration.CORS_ALLOW_NULL_ORIGIN)) {
|
||||
ccb.allowNullOrigin();
|
||||
}
|
||||
if (config.getBoolean(Configuration.CORS_ALLOW_CREDENTIALS)) {
|
||||
ccb.allowCredentials();
|
||||
}
|
||||
if (!StringUtils.isEmpty(config.get(Configuration.CORS_ALLOWED_METHODS))) {
|
||||
String[] methods = config.get(Configuration.CORS_ALLOWED_METHODS).split(",");
|
||||
HttpMethod[] m = new HttpMethod[methods.length];
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
m[i] = HttpMethod.valueOf(methods[i]);
|
||||
}
|
||||
ccb.allowedRequestMethods(m);
|
||||
}
|
||||
if (!StringUtils.isEmpty(config.get(Configuration.CORS_ALLOWED_HEADERS))) {
|
||||
ccb.allowedRequestHeaders(config.get(Configuration.CORS_ALLOWED_HEADERS).split(","));
|
||||
}
|
||||
CorsConfig cors = ccb.build();
|
||||
LOG.trace("Cors configuration: {}", cors);
|
||||
ch.pipeline().addLast("cors", new CorsHandler(cors));
|
||||
ch.pipeline().addLast("queryDecoder", new HttpQueryDecoder(config));
|
||||
ch.pipeline().addLast("login", new X509LoginRequestHandler(config));
|
||||
ch.pipeline().addLast("doLogin", new BasicAuthLoginRequestHandler(config));
|
||||
ch.pipeline().addLast("aggregators", new HttpAggregatorsRequestHandler());
|
||||
ch.pipeline().addLast("metrics", new HttpMetricsRequestHandler(config));
|
||||
ch.pipeline().addLast("query", new HttpQueryRequestHandler(dataStore));
|
||||
ch.pipeline().addLast("search", new HttpSearchLookupRequestHandler(dataStore));
|
||||
ch.pipeline().addLast("suggest", new HttpSuggestRequestHandler(dataStore));
|
||||
ch.pipeline().addLast("error", new TimelyExceptionHandler());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected ChannelHandler setupTcpChannel() {
|
||||
return new ChannelInitializer<SocketChannel>() {
|
||||
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) throws Exception {
|
||||
ch.pipeline().addLast("frame", new DelimiterBasedFrameDecoder(8192, true, Delimiters.lineDelimiter()));
|
||||
ch.pipeline().addLast("putDecoder", new TcpPutDecoder());
|
||||
ch.pipeline().addLast("putHandler", new TcpPutHandler(dataStore));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
package timely;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.accumulo.core.client.AccumuloException;
|
||||
import org.apache.accumulo.core.client.AccumuloSecurityException;
|
||||
import org.apache.accumulo.core.client.Connector;
|
||||
import org.apache.accumulo.core.client.admin.SecurityOperations;
|
||||
import org.apache.accumulo.core.security.Authorizations;
|
||||
import org.apache.accumulo.minicluster.MemoryUnit;
|
||||
import org.apache.accumulo.minicluster.MiniAccumuloCluster;
|
||||
import org.apache.accumulo.minicluster.MiniAccumuloConfig;
|
||||
import org.apache.accumulo.minicluster.ServerType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class StandaloneServer extends Server {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(StandaloneServer.class);
|
||||
|
||||
private static MiniAccumuloCluster mac = null;
|
||||
|
||||
public StandaloneServer(File conf) throws Exception {
|
||||
super(conf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
try {
|
||||
mac.stop();
|
||||
LOG.info("MiniAccumuloCluster shutdown.");
|
||||
} catch (IOException | InterruptedException e) {
|
||||
System.err.println("Error stopping MiniAccumuloCluster");
|
||||
e.printStackTrace();
|
||||
}
|
||||
super.shutdown();
|
||||
}
|
||||
|
||||
private static String usage() {
|
||||
return "StandaloneServer <configFile> <directory>";
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args.length != 2) {
|
||||
System.err.println(usage());
|
||||
}
|
||||
final File conf = new File(args[0]);
|
||||
if (!conf.canRead()) {
|
||||
throw new RuntimeException("Configuration file does not exist or cannot be read");
|
||||
}
|
||||
File tmp = new File(args[1]);
|
||||
if (!tmp.canWrite()) {
|
||||
System.err.println("Unable to write to directory: " + tmp);
|
||||
System.exit(1);
|
||||
}
|
||||
File accumuloDir = new File(tmp, "accumulo");
|
||||
MiniAccumuloConfig macConfig = new MiniAccumuloConfig(accumuloDir, "secret");
|
||||
macConfig.setInstanceName("TimelyStandalone");
|
||||
macConfig.setZooKeeperPort(9804);
|
||||
macConfig.setNumTservers(1);
|
||||
macConfig.setMemory(ServerType.TABLET_SERVER, 1, MemoryUnit.GIGABYTE);
|
||||
try {
|
||||
mac = new MiniAccumuloCluster(macConfig);
|
||||
mac.start();
|
||||
mac.getInstanceName();
|
||||
|
||||
} catch (IOException | InterruptedException e) {
|
||||
System.err.println("Error starting MiniAccumuloCluster: " + e.getMessage());
|
||||
System.exit(1);
|
||||
}
|
||||
try {
|
||||
Connector conn = mac.getConnector("root", "secret");
|
||||
SecurityOperations sops = conn.securityOperations();
|
||||
Authorizations rootAuths = new Authorizations("A", "B", "C", "D", "E", "F", "G", "H", "I");
|
||||
sops.changeUserAuthorizations("root", rootAuths);
|
||||
} catch (AccumuloException | AccumuloSecurityException e) {
|
||||
System.err.println("Error configuring root user");
|
||||
System.exit(1);
|
||||
}
|
||||
try {
|
||||
new StandaloneServer(conf);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error starting server");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
try {
|
||||
LATCH.await();
|
||||
} catch (InterruptedException e) {
|
||||
LOG.info("Server shutting down.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package timely.api;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class AuthenticatedRequest implements Request {
|
||||
|
||||
private String sessionId = null;
|
||||
private Map<String, String> requestHeaders = new HashMap<>();
|
||||
|
||||
public String getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(String sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
public void addHeaders(List<Entry<String, String>> headers) {
|
||||
headers.forEach(l -> requestHeaders.put(l.getKey(), l.getValue()));
|
||||
}
|
||||
|
||||
public Map<String, String> getRequestHeaders() {
|
||||
return Collections.unmodifiableMap(requestHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("{");
|
||||
buf.append("Session ID: ").append(sessionId);
|
||||
buf.append(", Request Headers: ").append(requestHeaders.toString());
|
||||
buf.append("}");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate() {
|
||||
Request.super.validate();
|
||||
if (null == sessionId) {
|
||||
throw new IllegalArgumentException("SessionID is null, must log in first");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package timely.api;
|
||||
|
||||
public interface Request {
|
||||
|
||||
/**
|
||||
* Validates the request, throws IllegalArgumentException if not valid.
|
||||
*/
|
||||
default void validate() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
package timely.api.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.accumulo.core.data.Key;
|
||||
import org.apache.accumulo.core.data.Value;
|
||||
|
||||
public class Meta implements Comparable<Meta> {
|
||||
|
||||
public static final String METRIC_PREFIX = "m:";
|
||||
public static final String TAG_PREFIX = "t:";
|
||||
public static final String VALUE_PREFIX = "v:";
|
||||
|
||||
private String metric;
|
||||
private String tagKey;
|
||||
private String tagValue;
|
||||
|
||||
public Meta(String metric, String tagKey, String tagValue) {
|
||||
super();
|
||||
this.metric = metric;
|
||||
this.tagKey = tagKey;
|
||||
this.tagValue = tagValue;
|
||||
}
|
||||
|
||||
public String getMetric() {
|
||||
return metric;
|
||||
}
|
||||
|
||||
public void setMetric(String metric) {
|
||||
this.metric = metric;
|
||||
}
|
||||
|
||||
public String getTagKey() {
|
||||
return tagKey;
|
||||
}
|
||||
|
||||
public void setTagKey(String tagKey) {
|
||||
this.tagKey = tagKey;
|
||||
}
|
||||
|
||||
public String getTagValue() {
|
||||
return tagValue;
|
||||
}
|
||||
|
||||
public void setTagValue(String tagValue) {
|
||||
this.tagValue = tagValue;
|
||||
}
|
||||
|
||||
public List<Key> toKeys() {
|
||||
List<Key> keys = new ArrayList<>();
|
||||
keys.add(new Key(METRIC_PREFIX + metric));
|
||||
keys.add(new Key(TAG_PREFIX + metric, tagKey));
|
||||
keys.add(new Key(VALUE_PREFIX + metric, tagKey, tagValue));
|
||||
return keys;
|
||||
}
|
||||
|
||||
public static Meta parse(Key k, Value v) {
|
||||
if (k.getColumnQualifier().getLength() > 0) {
|
||||
return new Meta(k.getRow().toString().substring(METRIC_PREFIX.length()), k.getColumnFamily().toString(), k
|
||||
.getColumnQualifier().toString());
|
||||
} else if (k.getColumnFamily().getLength() > 0) {
|
||||
return new Meta(k.getRow().toString().substring(METRIC_PREFIX.length()), k.getColumnFamily().toString(),
|
||||
null);
|
||||
|
||||
} else {
|
||||
return new Meta(k.getRow().toString().substring(METRIC_PREFIX.length()), null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((metric == null) ? 0 : metric.hashCode());
|
||||
result = prime * result + ((tagKey == null) ? 0 : tagKey.hashCode());
|
||||
result = prime * result + ((tagValue == null) ? 0 : tagValue.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Meta other = (Meta) obj;
|
||||
if (metric == null) {
|
||||
if (other.metric != null)
|
||||
return false;
|
||||
} else if (!metric.equals(other.metric))
|
||||
return false;
|
||||
if (tagKey == null) {
|
||||
if (other.tagKey != null)
|
||||
return false;
|
||||
} else if (!tagKey.equals(other.tagKey))
|
||||
return false;
|
||||
if (tagValue == null) {
|
||||
if (other.tagValue != null)
|
||||
return false;
|
||||
} else if (!tagValue.equals(other.tagValue))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MetaKey [metric=" + metric + ", tagKey=" + tagKey + ", tagValue=" + tagValue + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Meta o) {
|
||||
int result = metric.compareTo(o.metric);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
result = tagKey.compareTo(o.tagKey);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
return tagValue.compareTo(o.tagValue);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
package timely.api.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.accumulo.core.client.lexicoder.DoubleLexicoder;
|
||||
import org.apache.accumulo.core.client.lexicoder.LongLexicoder;
|
||||
import org.apache.accumulo.core.client.lexicoder.PairLexicoder;
|
||||
import org.apache.accumulo.core.client.lexicoder.StringLexicoder;
|
||||
import org.apache.accumulo.core.data.Key;
|
||||
import org.apache.accumulo.core.data.Mutation;
|
||||
import org.apache.accumulo.core.data.Value;
|
||||
import org.apache.accumulo.core.security.ColumnVisibility;
|
||||
import org.apache.accumulo.core.util.ComparablePair;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
|
||||
import timely.api.Request;
|
||||
|
||||
public class Metric implements Request {
|
||||
|
||||
private static final PairLexicoder<String, Long> rowCoder = new PairLexicoder<>(new StringLexicoder(),
|
||||
new LongLexicoder());
|
||||
private static final DoubleLexicoder valueCoder = new DoubleLexicoder();
|
||||
|
||||
public static final ColumnVisibility EMPTY_VISIBILITY = new ColumnVisibility();
|
||||
|
||||
private String metric;
|
||||
private long timestamp;
|
||||
private double value;
|
||||
private List<Tag> tags;
|
||||
|
||||
private ColumnVisibility visibility = EMPTY_VISIBILITY;
|
||||
|
||||
public Metric() {
|
||||
}
|
||||
|
||||
public Metric(String metric, long timestamp, double value, List<Tag> tags) {
|
||||
super();
|
||||
this.metric = metric;
|
||||
this.timestamp = timestamp;
|
||||
this.value = value;
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
public String getMetric() {
|
||||
return metric;
|
||||
}
|
||||
|
||||
public void setMetric(String metric) {
|
||||
this.metric = metric;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public double getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(double value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public List<Tag> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public void setTags(List<Tag> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
public void addTag(Tag tag) {
|
||||
if (null == this.tags) {
|
||||
tags = new ArrayList<Tag>();
|
||||
}
|
||||
this.tags.add(tag);
|
||||
}
|
||||
|
||||
public ColumnVisibility getVisibility() {
|
||||
return visibility;
|
||||
}
|
||||
|
||||
public void setVisibility(ColumnVisibility visibility) {
|
||||
this.visibility = visibility;
|
||||
}
|
||||
|
||||
public Mutation toMutation() {
|
||||
final byte[] row = rowCoder.encode(new ComparablePair<String, Long>(this.metric, this.timestamp));
|
||||
final Value value = new Value(valueCoder.encode(this.value));
|
||||
final Mutation m = new Mutation(row);
|
||||
Collections.sort(tags);
|
||||
for (final Tag entry : tags) {
|
||||
final String cf = entry.getKey() + "=" + entry.getValue();
|
||||
final StringBuffer otherTags = new StringBuffer();
|
||||
String sep = "";
|
||||
for (final Tag inner : this.tags) {
|
||||
if (inner != entry) {
|
||||
otherTags.append(sep).append(inner.getKey()).append("=").append(inner.getValue());
|
||||
sep = ",";
|
||||
}
|
||||
}
|
||||
m.put(cf, otherTags, visibility, this.timestamp, value);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
public static Metric parse(Key k, Value v) {
|
||||
ComparablePair<String, Long> row = rowCoder.decode(k.getRow().getBytes());
|
||||
Double value = valueCoder.decode(v.get());
|
||||
Metric m = new Metric();
|
||||
m.setMetric(row.getFirst());
|
||||
m.setTimestamp(row.getSecond());
|
||||
Tag tag = new Tag(k.getColumnFamily().toString());
|
||||
m.addTag(tag);
|
||||
String qualifier = k.getColumnQualifier().toString();
|
||||
if (!qualifier.isEmpty()) {
|
||||
for (String otherTag : qualifier.split(",")) {
|
||||
m.addTag(new Tag(otherTag));
|
||||
}
|
||||
}
|
||||
m.setValue(value);
|
||||
return m;
|
||||
}
|
||||
|
||||
public static byte[] encodeRowKey(String metric, Long timestamp) {
|
||||
return rowCoder.encode(new ComparablePair<String, Long>(metric, timestamp));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("[metric = ").append(metric);
|
||||
buf.append(", timestamp = ").append(timestamp);
|
||||
tags.forEach(t -> buf.append(", ").append(t.getKey()).append(" = ").append(t.getValue()));
|
||||
buf.append(", value = ").append(value).append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
HashCodeBuilder hc = new HashCodeBuilder(17, 37);
|
||||
hc.append(metric);
|
||||
hc.append(timestamp);
|
||||
hc.append(value);
|
||||
hc.append(tags);
|
||||
return hc.toHashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (null == obj) {
|
||||
return false;
|
||||
}
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof Metric)) {
|
||||
return false;
|
||||
}
|
||||
Metric other = (Metric) obj;
|
||||
EqualsBuilder equals = new EqualsBuilder();
|
||||
equals.append(this.metric, other.metric);
|
||||
equals.append(this.timestamp, other.timestamp);
|
||||
equals.append(this.value, other.value);
|
||||
equals.append(this.tags, other.tags);
|
||||
return equals.isEquals();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
package timely.api.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
||||
public class Tag implements Comparable<Tag>, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String key;
|
||||
private String value;
|
||||
|
||||
public Tag() {
|
||||
super();
|
||||
}
|
||||
|
||||
public Tag(String tagValue) {
|
||||
super();
|
||||
String[] tv = tagValue.split("=");
|
||||
if (tv.length != 2) {
|
||||
throw new IllegalArgumentException("Invalid tag format: " + tagValue);
|
||||
}
|
||||
this.key = tv[0];
|
||||
this.value = tv[1];
|
||||
}
|
||||
|
||||
public Tag(String key, String value) {
|
||||
super();
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
HashCodeBuilder hcb = new HashCodeBuilder();
|
||||
hcb.append(key);
|
||||
hcb.append(value);
|
||||
return hcb.toHashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (null == obj) {
|
||||
return false;
|
||||
}
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof Tag) {
|
||||
Tag other = (Tag) obj;
|
||||
EqualsBuilder eq = new EqualsBuilder();
|
||||
eq.append(this.key, other.key);
|
||||
eq.append(this.value, other.value);
|
||||
return eq.isEquals();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringBuilder tsb = new ToStringBuilder(this);
|
||||
tsb.append("key", key);
|
||||
tsb.append("value", value);
|
||||
return tsb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Tag o) {
|
||||
int result = key.compareTo(o.key);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
return value.compareTo(o.value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package timely.api.query.request;
|
||||
|
||||
import timely.api.AuthenticatedRequest;
|
||||
|
||||
public class AggregatorsRequest extends AuthenticatedRequest {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package timely.api.query.request;
|
||||
|
||||
import timely.api.Request;
|
||||
|
||||
public class BasicAuthLoginRequest implements Request {
|
||||
|
||||
private String username;
|
||||
private String password;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String user) {
|
||||
this.username = user;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate() {
|
||||
Request.super.validate();
|
||||
if (username == null || password == null) {
|
||||
throw new IllegalArgumentException("Username and password are required.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Username: " + username;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package timely.api.query.request;
|
||||
|
||||
import timely.api.AuthenticatedRequest;
|
||||
|
||||
public class MetricsRequest extends AuthenticatedRequest {
|
||||
|
||||
}
|
||||
@@ -1,474 +0,0 @@
|
||||
package timely.api.query.request;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
||||
import timely.api.AuthenticatedRequest;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
public class QueryRequest extends AuthenticatedRequest {
|
||||
|
||||
public static class RateOption {
|
||||
|
||||
private boolean counter = false;
|
||||
private long counterMax = Long.MAX_VALUE;
|
||||
private long resetValue = 0;
|
||||
|
||||
public RateOption() {
|
||||
}
|
||||
|
||||
public RateOption(String options) {
|
||||
String[] parts = options.split(",");
|
||||
if (parts.length == 1) {
|
||||
if (!parts[0].equals("")) {
|
||||
this.counter = Boolean.valueOf(parts[0]);
|
||||
}
|
||||
}
|
||||
if (parts.length == 2) {
|
||||
if (!parts[1].equals("")) {
|
||||
this.counterMax = Long.parseLong(parts[1]);
|
||||
}
|
||||
}
|
||||
if (parts.length == 3) {
|
||||
if (!parts[2].equals("")) {
|
||||
this.resetValue = Long.parseLong(parts[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCounter() {
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
public void setCounter(boolean counter) {
|
||||
|
||||
this.counter = counter;
|
||||
}
|
||||
|
||||
public long getCounterMax() {
|
||||
|
||||
return counterMax;
|
||||
}
|
||||
|
||||
public void setCounterMax(long counterMax) {
|
||||
|
||||
this.counterMax = counterMax;
|
||||
}
|
||||
|
||||
public long getResetValue() {
|
||||
|
||||
return resetValue;
|
||||
}
|
||||
|
||||
public void setResetValue(long resetValue) {
|
||||
|
||||
this.resetValue = resetValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringBuilder tsb = new ToStringBuilder(this);
|
||||
tsb.append("counter", counter);
|
||||
tsb.append("counterMax", counterMax);
|
||||
tsb.append("resetValue", resetValue);
|
||||
return tsb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
HashCodeBuilder hcb = new HashCodeBuilder();
|
||||
hcb.append(counter);
|
||||
hcb.append(counterMax);
|
||||
hcb.append(resetValue);
|
||||
return hcb.toHashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (null == obj) {
|
||||
return false;
|
||||
}
|
||||
if (this == obj) {
|
||||
return false;
|
||||
}
|
||||
if (obj instanceof RateOption) {
|
||||
RateOption other = (RateOption) obj;
|
||||
EqualsBuilder eq = new EqualsBuilder();
|
||||
eq.append(this.counter, other.counter);
|
||||
eq.append(this.counterMax, other.counterMax);
|
||||
eq.append(this.resetValue, other.resetValue);
|
||||
return eq.isEquals();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Filter {
|
||||
|
||||
private String type;
|
||||
private String tagk;
|
||||
private String filter;
|
||||
private boolean groupBy = false;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getTagk() {
|
||||
return tagk;
|
||||
}
|
||||
|
||||
public void setTagk(String tagk) {
|
||||
this.tagk = tagk;
|
||||
}
|
||||
|
||||
public String getFilter() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
public void setFilter(String filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
public boolean isGroupBy() {
|
||||
return groupBy;
|
||||
}
|
||||
|
||||
public void setGroupBy(boolean groupBy) {
|
||||
this.groupBy = groupBy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringBuilder tsb = new ToStringBuilder(this);
|
||||
tsb.append("tagk", tagk);
|
||||
tsb.append("type", type);
|
||||
tsb.append("filter", filter);
|
||||
tsb.append("groupBy", groupBy);
|
||||
return tsb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
HashCodeBuilder hcb = new HashCodeBuilder();
|
||||
hcb.append(filter);
|
||||
hcb.append(tagk);
|
||||
hcb.append(filter);
|
||||
hcb.append(groupBy);
|
||||
return hcb.toHashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (null == obj) {
|
||||
return false;
|
||||
}
|
||||
if (this == obj) {
|
||||
return false;
|
||||
}
|
||||
if (obj instanceof Filter) {
|
||||
Filter other = (Filter) obj;
|
||||
EqualsBuilder eq = new EqualsBuilder();
|
||||
eq.append(this.tagk, other.tagk);
|
||||
eq.append(this.type, other.type);
|
||||
eq.append(this.filter, other.filter);
|
||||
eq.append(this.groupBy, other.groupBy);
|
||||
return eq.isEquals();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class SubQuery {
|
||||
|
||||
private String aggregator;
|
||||
private String metric;
|
||||
private boolean rate = false;
|
||||
private RateOption rateOptions = null;
|
||||
private Optional<String> downsample = Optional.empty();
|
||||
private Map<String, String> tags = new HashMap<>();
|
||||
private Collection<Filter> filters = new ArrayList<>();
|
||||
private Collection<String> tsuids = new ArrayList<>();
|
||||
|
||||
public String getAggregator() {
|
||||
return aggregator;
|
||||
}
|
||||
|
||||
public void setAggregator(String aggregator) {
|
||||
this.aggregator = aggregator;
|
||||
}
|
||||
|
||||
public String getMetric() {
|
||||
return metric;
|
||||
}
|
||||
|
||||
public void setMetric(String metric) {
|
||||
this.metric = metric;
|
||||
}
|
||||
|
||||
public boolean isRate() {
|
||||
return rate;
|
||||
}
|
||||
|
||||
public void setRate(boolean rate) {
|
||||
this.rate = rate;
|
||||
}
|
||||
|
||||
public RateOption getRateOptions() {
|
||||
return rateOptions;
|
||||
}
|
||||
|
||||
public void setRateOptions(RateOption rateOptions) {
|
||||
this.rateOptions = rateOptions;
|
||||
}
|
||||
|
||||
public Optional<String> getDownsample() {
|
||||
return downsample;
|
||||
}
|
||||
|
||||
public void setDownsample(Optional<String> downsample) {
|
||||
this.downsample = downsample;
|
||||
}
|
||||
|
||||
public Map<String, String> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public void setTags(Map<String, String> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
public void addTag(String key, String value) {
|
||||
this.tags.put(key, value);
|
||||
}
|
||||
|
||||
public Collection<Filter> getFilters() {
|
||||
return filters;
|
||||
}
|
||||
|
||||
public void setFilters(Collection<Filter> filters) {
|
||||
this.filters = filters;
|
||||
}
|
||||
|
||||
public void addFilter(Filter f) {
|
||||
this.filters.add(f);
|
||||
}
|
||||
|
||||
public Collection<String> getTsuids() {
|
||||
return tsuids;
|
||||
}
|
||||
|
||||
public void setTsuids(Collection<String> tsuids) {
|
||||
this.tsuids = tsuids;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public boolean isTsuidQuery() {
|
||||
return (this.tsuids.size() > 0);
|
||||
}
|
||||
|
||||
public void addTsuid(String tsuid) {
|
||||
this.tsuids.add(tsuid);
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public boolean isMetricQuery() {
|
||||
return (null != this.metric);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringBuilder tsb = new ToStringBuilder(this);
|
||||
tsb.append("aggregator", aggregator);
|
||||
tsb.append("metric", metric);
|
||||
tsb.append("rate", tags);
|
||||
tsb.append("rateOptions", rateOptions);
|
||||
tsb.append("downsample", downsample);
|
||||
tsb.append("tags", tags);
|
||||
tsb.append("filters", filters);
|
||||
tsb.append("tsuids", tsuids);
|
||||
return tsb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
HashCodeBuilder hcb = new HashCodeBuilder();
|
||||
hcb.append(aggregator);
|
||||
hcb.append(downsample);
|
||||
hcb.append(filters);
|
||||
hcb.append(metric);
|
||||
hcb.append(rate);
|
||||
hcb.append(rateOptions);
|
||||
hcb.append(tags);
|
||||
hcb.append(tsuids);
|
||||
return hcb.toHashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (null == obj) {
|
||||
return false;
|
||||
}
|
||||
if (this == obj) {
|
||||
return false;
|
||||
}
|
||||
if (obj instanceof SubQuery) {
|
||||
SubQuery other = (SubQuery) obj;
|
||||
EqualsBuilder eq = new EqualsBuilder();
|
||||
eq.append(this.aggregator, other.aggregator);
|
||||
eq.append(this.downsample, other.downsample);
|
||||
eq.append(this.filters, other.filters);
|
||||
eq.append(this.metric, other.metric);
|
||||
eq.append(this.rate, other.rate);
|
||||
eq.append(this.rateOptions, other.rateOptions);
|
||||
eq.append(this.tags, other.tags);
|
||||
eq.append(this.tsuids, other.tsuids);
|
||||
return eq.isEquals();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long start = 0;
|
||||
private long end = System.currentTimeMillis();
|
||||
private Collection<SubQuery> queries = new ArrayList<>();
|
||||
private boolean noAnnotations = false;
|
||||
private boolean globalAnnotations = false;
|
||||
private boolean msResolution = false;
|
||||
private boolean showSummary = false;
|
||||
private boolean showQuery = false;
|
||||
private boolean delete = false;
|
||||
|
||||
public boolean isGlobalAnnotations() {
|
||||
return globalAnnotations;
|
||||
}
|
||||
|
||||
public void setGlobalAnnotations(boolean globalAnnotations) {
|
||||
this.globalAnnotations = globalAnnotations;
|
||||
}
|
||||
|
||||
public boolean isShowQuery() {
|
||||
return showQuery;
|
||||
}
|
||||
|
||||
public void setShowQuery(boolean showQuery) {
|
||||
this.showQuery = showQuery;
|
||||
}
|
||||
|
||||
public boolean isMsResolution() {
|
||||
return msResolution;
|
||||
}
|
||||
|
||||
public void setMsResolution(boolean msResolution) {
|
||||
this.msResolution = msResolution;
|
||||
}
|
||||
|
||||
public long getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public void setStart(long start) {
|
||||
this.start = start;
|
||||
}
|
||||
|
||||
public long getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
public void setEnd(long end) {
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
public Collection<SubQuery> getQueries() {
|
||||
return queries;
|
||||
}
|
||||
|
||||
public void setQueries(Collection<SubQuery> queries) {
|
||||
this.queries = queries;
|
||||
}
|
||||
|
||||
public void addQuery(SubQuery query) {
|
||||
this.queries.add(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate() {
|
||||
super.validate();
|
||||
if (queries.size() == 0) {
|
||||
throw new IllegalArgumentException("No query specified.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringBuilder tsb = new ToStringBuilder(this);
|
||||
tsb.append("start", start);
|
||||
tsb.append("end", end);
|
||||
tsb.append("queries", queries);
|
||||
tsb.append("noAnnotations", noAnnotations);
|
||||
tsb.append("globalAnnotations", globalAnnotations);
|
||||
tsb.append("msResolution", msResolution);
|
||||
tsb.append("showSummary", showSummary);
|
||||
tsb.append("showQuery", showQuery);
|
||||
tsb.append("delete", delete);
|
||||
return tsb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
HashCodeBuilder hcb = new HashCodeBuilder();
|
||||
hcb.append(start);
|
||||
hcb.append(end);
|
||||
hcb.append(queries);
|
||||
hcb.append(noAnnotations);
|
||||
hcb.append(globalAnnotations);
|
||||
hcb.append(msResolution);
|
||||
hcb.append(showSummary);
|
||||
hcb.append(showQuery);
|
||||
hcb.append(delete);
|
||||
return hcb.toHashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (null == obj) {
|
||||
return false;
|
||||
}
|
||||
if (this == obj) {
|
||||
return false;
|
||||
}
|
||||
if (obj instanceof QueryRequest) {
|
||||
QueryRequest other = (QueryRequest) obj;
|
||||
EqualsBuilder eq = new EqualsBuilder();
|
||||
eq.append(this.start, other.start);
|
||||
eq.append(this.end, other.end);
|
||||
eq.append(this.queries, other.queries);
|
||||
eq.append(this.noAnnotations, other.noAnnotations);
|
||||
eq.append(this.globalAnnotations, other.globalAnnotations);
|
||||
eq.append(this.msResolution, other.msResolution);
|
||||
eq.append(this.showSummary, other.showSummary);
|
||||
eq.append(this.showQuery, other.showQuery);
|
||||
eq.append(this.delete, other.delete);
|
||||
return eq.isEquals();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package timely.api.query.request;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
||||
import timely.api.AuthenticatedRequest;
|
||||
import timely.api.model.Tag;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class SearchLookupRequest extends AuthenticatedRequest {
|
||||
|
||||
@JsonProperty("metric")
|
||||
private String query;
|
||||
@JsonProperty("limit")
|
||||
private int limit = 25;
|
||||
@JsonProperty("tags")
|
||||
private Collection<Tag> tags = new ArrayList<>();
|
||||
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public void setQuery(String query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public int getLimit() {
|
||||
return limit;
|
||||
}
|
||||
|
||||
public void setLimit(int limit) {
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
public Collection<Tag> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public void setTags(Collection<Tag> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
public void addTag(Tag tag) {
|
||||
this.tags.add(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringBuilder tsb = new ToStringBuilder(this);
|
||||
tsb.append("query", query);
|
||||
tsb.append("limit", limit);
|
||||
tsb.append("tags", tags);
|
||||
return tsb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
HashCodeBuilder hcb = new HashCodeBuilder();
|
||||
hcb.append(query);
|
||||
hcb.append(limit);
|
||||
hcb.append(tags);
|
||||
return hcb.toHashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (null == obj) {
|
||||
return false;
|
||||
}
|
||||
if (this == obj) {
|
||||
return false;
|
||||
}
|
||||
if (obj instanceof SearchLookupRequest) {
|
||||
SearchLookupRequest other = (SearchLookupRequest) obj;
|
||||
EqualsBuilder eq = new EqualsBuilder();
|
||||
eq.append(this.query, other.query);
|
||||
eq.append(this.limit, other.limit);
|
||||
eq.append(this.tags, other.tags);
|
||||
return eq.isEquals();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package timely.api.query.request;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
||||
import timely.api.AuthenticatedRequest;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class SuggestRequest extends AuthenticatedRequest {
|
||||
|
||||
private static List<String> validTypes = new ArrayList<>();
|
||||
static {
|
||||
validTypes.add("metrics");
|
||||
validTypes.add("tagk");
|
||||
validTypes.add("tagv");
|
||||
}
|
||||
|
||||
private String type;
|
||||
@JsonProperty("q")
|
||||
private Optional<String> query = Optional.empty();
|
||||
private int max = 25;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Optional<String> getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public void setQuery(Optional<String> query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public int getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
public void setMax(int max) {
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public void validate() {
|
||||
super.validate();
|
||||
if (!validTypes.contains(this.type)) {
|
||||
throw new IllegalArgumentException("Type is not valid");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringBuilder tsb = new ToStringBuilder(this);
|
||||
tsb.append("query", query);
|
||||
tsb.append("type", type);
|
||||
tsb.append("max", max);
|
||||
return tsb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package timely.api.query.request;
|
||||
|
||||
import timely.api.Request;
|
||||
|
||||
public class X509LoginRequest implements Request {
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package timely.api.query.response;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
@JsonSerialize(using = AggregatorsResponse.class)
|
||||
public class AggregatorsResponse extends JsonSerializer<AggregatorsResponse> {
|
||||
|
||||
private List<String> aggregators = new ArrayList<>();
|
||||
|
||||
public List<String> getAggregators() {
|
||||
return aggregators;
|
||||
}
|
||||
|
||||
public void setAggregators(List<String> aggregators) {
|
||||
this.aggregators = aggregators;
|
||||
}
|
||||
|
||||
public void addAggregator(String agg) {
|
||||
this.aggregators.add(agg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(AggregatorsResponse value, JsonGenerator gen, SerializerProvider serializers)
|
||||
throws IOException, JsonProcessingException {
|
||||
gen.writeStartArray();
|
||||
for (String a : value.getAggregators()) {
|
||||
gen.writeString(a);
|
||||
}
|
||||
gen.writeEndArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
package timely.api.query.response;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import timely.Configuration;
|
||||
import timely.api.model.Meta;
|
||||
import timely.store.MetaCache;
|
||||
import timely.store.MetaCacheFactory;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
public class MetricsResponse {
|
||||
|
||||
private static final String DOCTYPE = "<!DOCTYPE html>\n";
|
||||
private static final String META = "<meta charset=\"UTF-8\">\n";
|
||||
private static final String HTML_START = "<html>\n";
|
||||
private static final String HTML_END = "</html>\n";
|
||||
private static final String HEAD_START = "<head>\n";
|
||||
private static final String HEAD_END = "</head>\n";
|
||||
private static final String TITLE = "<title>Timely Metric Information</title>\n";
|
||||
private static final String BODY_START = "<body>\n";
|
||||
private static final String BODY_END = "</body>\n";
|
||||
private static final String HEADER_START = "<header>\n";
|
||||
private static final String HEADER_END = "</header>\n";
|
||||
private static final String HEADER_CONTENT = "<h2>Timely Metric Information</h2>\n<p>This page represents the metrics that Timely has in its internal cache, which may not be the entire set of available metrics depending on the configuration. The following tags are not shown here but are available for query: ";
|
||||
private static final String TABLE_START = "<table>\n";
|
||||
private static final String TABLE_END = "</table>\n";
|
||||
private static final String TR_START = "<tr>\n";
|
||||
private static final String TR_END = "</tr>\n";
|
||||
private static final String TH_START = "<th>";
|
||||
private static final String TH_END = "</th>\n";
|
||||
private static final String TD_START = "<td>";
|
||||
private static final String TD_END = "</td>\n";
|
||||
|
||||
private Set<String> ignoredTags = Collections.emptySet();
|
||||
private Configuration conf = null;
|
||||
|
||||
public MetricsResponse() {
|
||||
}
|
||||
|
||||
public MetricsResponse(Configuration conf) {
|
||||
String tagsToIgnore = conf.get(Configuration.METRICS_IGNORED_TAGS);
|
||||
if (!StringUtils.isEmpty(tagsToIgnore)) {
|
||||
this.ignoredTags = new HashSet<>();
|
||||
for (String tag : tagsToIgnore.split(",")) {
|
||||
ignoredTags.add(tag);
|
||||
}
|
||||
}
|
||||
this.conf = conf;
|
||||
}
|
||||
|
||||
public StringBuilder generateHtml() {
|
||||
final MetaCache cache = MetaCacheFactory.getCache(conf);
|
||||
TreeSet<Meta> tree = new TreeSet<>();
|
||||
cache.forEach(m -> tree.add(m));
|
||||
final StringBuilder b = new StringBuilder();
|
||||
b.append(DOCTYPE);
|
||||
b.append(HTML_START);
|
||||
b.append(HEAD_START);
|
||||
b.append(META);
|
||||
b.append(TITLE);
|
||||
b.append(HEAD_END);
|
||||
b.append(HEADER_START);
|
||||
b.append(HEADER_CONTENT).append(ignoredTags.toString()).append("</p>\n");
|
||||
b.append(HEADER_END);
|
||||
b.append(BODY_START);
|
||||
b.append(TABLE_START);
|
||||
b.append(TR_START);
|
||||
b.append(TH_START).append("Metric").append(TH_END);
|
||||
b.append(TH_START).append("Available Tags").append(TH_END);
|
||||
b.append(TR_END);
|
||||
String prevMetric = null;
|
||||
StringBuilder tags = new StringBuilder();
|
||||
for (Meta m : tree) {
|
||||
if (prevMetric != null && !m.getMetric().equals(prevMetric)) {
|
||||
b.append(TR_START);
|
||||
b.append(TD_START).append(prevMetric).append(TD_END);
|
||||
b.append(TD_START).append(tags.toString()).append(TD_END);
|
||||
b.append(TR_END);
|
||||
prevMetric = m.getMetric();
|
||||
tags.delete(0, tags.length());
|
||||
}
|
||||
if (prevMetric == null) {
|
||||
prevMetric = m.getMetric();
|
||||
}
|
||||
if (!(this.ignoredTags.contains(m.getTagKey()))) {
|
||||
tags.append(m.getTagKey()).append("=").append(m.getTagValue()).append(" ");
|
||||
}
|
||||
}
|
||||
b.append(TR_START);
|
||||
b.append(TD_START).append(prevMetric).append(TD_END);
|
||||
b.append(TD_START).append(tags.toString()).append(TD_END);
|
||||
b.append(TR_END);
|
||||
tags = null;
|
||||
prevMetric = null;
|
||||
b.append(TABLE_END);
|
||||
b.append(BODY_END);
|
||||
b.append(HTML_END);
|
||||
return b;
|
||||
}
|
||||
|
||||
public String generateJson(final ObjectMapper mapper) throws JsonProcessingException {
|
||||
// map non-ignored metrics to their list of tags
|
||||
final MetaCache cache = MetaCacheFactory.getCache(conf);
|
||||
Map<String, List<JsonNode>> metricTagMap = new HashMap<>();
|
||||
cache.forEach(m -> {
|
||||
if (!metricTagMap.containsKey(m.getMetric())) {
|
||||
metricTagMap.put(m.getMetric(), new ArrayList<>());
|
||||
}
|
||||
if (!this.ignoredTags.contains(m.getTagKey())) {
|
||||
metricTagMap.get(m.getMetric()).add(createTag(m, mapper));
|
||||
}
|
||||
});
|
||||
|
||||
ObjectNode metricsNode = mapper.createObjectNode();
|
||||
ArrayNode metricsArray = mapper.createArrayNode();
|
||||
metricTagMap.forEach((metric, tags) -> {
|
||||
metricsArray.add(createMetric(metric, tags, mapper));
|
||||
});
|
||||
metricsNode.put("metrics", metricsArray);
|
||||
|
||||
return mapper.writeValueAsString(metricsNode);
|
||||
}
|
||||
|
||||
private JsonNode createMetric(String metric, List<JsonNode> tags, ObjectMapper mapper) {
|
||||
ObjectNode metricNode = mapper.createObjectNode();
|
||||
metricNode.put("metric", metric);
|
||||
ArrayNode tagsArray = mapper.createArrayNode();
|
||||
tags.forEach(tagsArray::add);
|
||||
metricNode.put("tags", tagsArray);
|
||||
return metricNode;
|
||||
}
|
||||
|
||||
private JsonNode createTag(Meta meta, ObjectMapper mapper) {
|
||||
ObjectNode tagNode = mapper.createObjectNode();
|
||||
tagNode.put("key", meta.getTagKey());
|
||||
tagNode.put("value", meta.getTagValue());
|
||||
return tagNode;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package timely.api.query.response;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
||||
public class QueryResponse {
|
||||
|
||||
private String metric;
|
||||
private Map<String, String> tags = new HashMap<>();
|
||||
private List<String> aggregatedTags = new ArrayList<>();
|
||||
private Map<String, Object> dps = new TreeMap<>();
|
||||
|
||||
public String getMetric() {
|
||||
return metric;
|
||||
}
|
||||
|
||||
public void setMetric(String metric) {
|
||||
this.metric = metric;
|
||||
}
|
||||
|
||||
public Map<String, String> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public void setTags(Map<String, String> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
public void putTag(String key, String value) {
|
||||
this.tags.put(key, value);
|
||||
}
|
||||
|
||||
public List<String> getAggregatedTags() {
|
||||
return aggregatedTags;
|
||||
}
|
||||
|
||||
public void setAggregatedTags(List<String> aggregatedTags) {
|
||||
this.aggregatedTags = aggregatedTags;
|
||||
}
|
||||
|
||||
public void addAggregatedTag(String tag) {
|
||||
this.aggregatedTags.add(tag);
|
||||
}
|
||||
|
||||
public Map<String, Object> getDps() {
|
||||
return dps;
|
||||
}
|
||||
|
||||
public void setDps(Map<String, Object> dps) {
|
||||
this.dps = dps;
|
||||
}
|
||||
|
||||
public void putDps(String key, Object value) {
|
||||
this.dps.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringBuilder tsb = new ToStringBuilder(this);
|
||||
tsb.append("metric", this.metric);
|
||||
tsb.append("tags", this.tags);
|
||||
tsb.append("aggregatedTags", this.aggregatedTags);
|
||||
tsb.append("dps", this.dps);
|
||||
return tsb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
package timely.api.query.response;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
|
||||
public class SearchLookupResponse {
|
||||
|
||||
public static class Result {
|
||||
|
||||
private Map<String, String> tags = new HashMap<>();
|
||||
private String metric;
|
||||
private String tsuid;
|
||||
|
||||
public Map<String, String> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public void setTags(Map<String, String> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
public void putTag(String key, String value) {
|
||||
this.tags.put(key, value);
|
||||
}
|
||||
|
||||
public String getMetric() {
|
||||
return metric;
|
||||
}
|
||||
|
||||
public void setMetric(String metric) {
|
||||
this.metric = metric;
|
||||
}
|
||||
|
||||
public String getTsuid() {
|
||||
return tsuid;
|
||||
}
|
||||
|
||||
public void setTsuid(String tsuid) {
|
||||
this.tsuid = tsuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (null == obj) {
|
||||
return false;
|
||||
}
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof Result) {
|
||||
Result other = (Result) obj;
|
||||
EqualsBuilder builder = new EqualsBuilder();
|
||||
builder.append(this.metric, other.metric);
|
||||
builder.append(this.tsuid, other.tsuid);
|
||||
builder.append(this.tags, other.tags);
|
||||
return builder.isEquals();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
HashCodeBuilder hcb = new HashCodeBuilder();
|
||||
hcb.append(this.metric);
|
||||
hcb.append(this.tsuid);
|
||||
hcb.append(this.tags);
|
||||
return hcb.toHashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String type;
|
||||
private String metric;
|
||||
private Map<String, String> tags = new HashMap<>();
|
||||
private int limit;
|
||||
private int time;
|
||||
private int totalResults;
|
||||
private List<Result> results = new ArrayList<>();
|
||||
private int startIndex = 0; // Does not change, here for serialization
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getMetric() {
|
||||
return metric;
|
||||
}
|
||||
|
||||
public void setMetric(String metric) {
|
||||
this.metric = metric;
|
||||
}
|
||||
|
||||
public Map<String, String> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public void setTags(Map<String, String> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
public void putTag(String key, String value) {
|
||||
this.tags.put(key, value);
|
||||
}
|
||||
|
||||
public int getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime(int time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public int getTotalResults() {
|
||||
return totalResults;
|
||||
}
|
||||
|
||||
public void setTotalResults(int totalResults) {
|
||||
this.totalResults = totalResults;
|
||||
}
|
||||
|
||||
public List<Result> getResults() {
|
||||
return results;
|
||||
}
|
||||
|
||||
public void setResults(List<Result> results) {
|
||||
this.results = results;
|
||||
}
|
||||
|
||||
public int getLimit() {
|
||||
return limit;
|
||||
}
|
||||
|
||||
public void setLimit(int limit) {
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (null == obj) {
|
||||
return false;
|
||||
}
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof SearchLookupResponse) {
|
||||
SearchLookupResponse other = (SearchLookupResponse) obj;
|
||||
EqualsBuilder builder = new EqualsBuilder();
|
||||
builder.append(this.type, other.type);
|
||||
builder.append(this.metric, other.metric);
|
||||
builder.append(this.time, other.time);
|
||||
builder.append(this.totalResults, other.totalResults);
|
||||
builder.append(this.tags, other.tags);
|
||||
builder.append(this.results, other.results);
|
||||
builder.append(this.startIndex, other.startIndex);
|
||||
builder.append(this.limit, other.limit);
|
||||
return builder.isEquals();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
HashCodeBuilder hcb = new HashCodeBuilder();
|
||||
hcb.append(this.type);
|
||||
hcb.append(this.metric);
|
||||
hcb.append(this.time);
|
||||
hcb.append(this.totalResults);
|
||||
hcb.append(this.tags);
|
||||
hcb.append(this.results);
|
||||
hcb.append(this.startIndex);
|
||||
hcb.append(this.limit);
|
||||
return hcb.toHashCode();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package timely.api.query.response;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
@JsonSerialize(using = SuggestResponse.class)
|
||||
public class SuggestResponse extends JsonSerializer<SuggestResponse> {
|
||||
|
||||
private List<String> suggestions = new ArrayList<>();
|
||||
|
||||
public List<String> getSuggestions() {
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
public void setSuggestions(List<String> suggestions) {
|
||||
this.suggestions = suggestions;
|
||||
}
|
||||
|
||||
public void addSuggestion(String suggestion) {
|
||||
this.suggestions.add(suggestion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(SuggestResponse value, JsonGenerator gen, SerializerProvider serializers) throws IOException,
|
||||
JsonProcessingException {
|
||||
gen.writeStartArray();
|
||||
for (String s : value.getSuggestions()) {
|
||||
gen.writeString(s);
|
||||
}
|
||||
gen.writeEndArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package timely.api.query.response;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class TimelyException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private int code = 500;
|
||||
private String details = null;
|
||||
private Map<String, String> responseHeaders = new HashMap<>();
|
||||
|
||||
public TimelyException(int code, String message, String details) {
|
||||
this(code, message, details, null);
|
||||
}
|
||||
|
||||
public TimelyException(int code, String message, String details, Throwable error) {
|
||||
super(message, error);
|
||||
this.code = code;
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getDetails() {
|
||||
return details;
|
||||
}
|
||||
|
||||
public void setDetails(String details) {
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
public void addResponseHeader(String name, String value) {
|
||||
this.responseHeaders.put(name, value);
|
||||
}
|
||||
|
||||
public Map<String, String> getResponseHeaders() {
|
||||
return this.responseHeaders;
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package timely.auth;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.accumulo.core.security.Authorizations;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import timely.Configuration;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
|
||||
public class AuthCache {
|
||||
|
||||
private static Cache<String, Authentication> CACHE = null;
|
||||
|
||||
private static Long sessionMaxAge = -1L;
|
||||
|
||||
/**
|
||||
* For tests only
|
||||
*/
|
||||
public static void resetSessionMaxAge() {
|
||||
sessionMaxAge = -1L;
|
||||
}
|
||||
|
||||
public static void setSessionMaxAge(Configuration config) {
|
||||
if (-1 != sessionMaxAge) {
|
||||
throw new IllegalStateException("Cache session max age already configured.");
|
||||
}
|
||||
sessionMaxAge = Long.parseLong(config.get(Configuration.SESSION_MAX_AGE));
|
||||
}
|
||||
|
||||
public static Cache<String, Authentication> getCache() {
|
||||
if (-1 == sessionMaxAge) {
|
||||
throw new IllegalStateException("Cache session max age not configured.");
|
||||
}
|
||||
if (null == CACHE) {
|
||||
CACHE = Caffeine.newBuilder().expireAfterAccess(sessionMaxAge, TimeUnit.SECONDS).build();
|
||||
}
|
||||
return CACHE;
|
||||
}
|
||||
|
||||
public static Authorizations getAuthorizations(String sessionId) {
|
||||
if (!StringUtils.isEmpty(sessionId)) {
|
||||
Collection<? extends GrantedAuthority> authorities = CACHE.asMap().get(sessionId).getAuthorities();
|
||||
String[] auths = new String[authorities.size()];
|
||||
final AtomicInteger i = new AtomicInteger(0);
|
||||
authorities.forEach(a -> {
|
||||
auths[i.getAndIncrement()] = a.getAuthority();
|
||||
});
|
||||
return new Authorizations(auths);
|
||||
} else {
|
||||
throw new IllegalArgumentException("session id cannot be null");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package timely.auth;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ServiceConfigurationError;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor;
|
||||
|
||||
public class AuthenticationService {
|
||||
|
||||
private static ApplicationContext springContext = null;
|
||||
private static AuthenticationManager authManager = null;
|
||||
private static SubjectDnX509PrincipalExtractor x509 = null;
|
||||
|
||||
static {
|
||||
try {
|
||||
springContext = new ClassPathXmlApplicationContext("security.xml");
|
||||
authManager = (AuthenticationManager) springContext.getBean("authenticationManager");
|
||||
x509 = (SubjectDnX509PrincipalExtractor) springContext.getBean("x509PrincipalExtractor");
|
||||
} catch (BeansException e) {
|
||||
throw new ServiceConfigurationError("Error setting up Authentication objects: " + e.getMessage(),
|
||||
e.getRootCause());
|
||||
}
|
||||
}
|
||||
|
||||
public static AuthenticationManager getAuthenticationManager() {
|
||||
return authManager;
|
||||
}
|
||||
|
||||
public static String extractDN(X509Certificate clientCert) throws Exception {
|
||||
return (String) x509.extractPrincipal(clientCert);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package timely.auth;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||
|
||||
public class UserDetailsService
|
||||
implements
|
||||
org.springframework.security.core.userdetails.AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {
|
||||
|
||||
private HashMap<String, List<String>> users;
|
||||
|
||||
public Map<String, List<String>> getUsers() {
|
||||
return users;
|
||||
}
|
||||
|
||||
public void setUsers(LinkedHashMap<String, List<String>> users) {
|
||||
this.users = new HashMap<>(users);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserDetails(final PreAuthenticatedAuthenticationToken token)
|
||||
throws UsernameNotFoundException {
|
||||
// Determine if the user is allowed to access the system, if not throw
|
||||
// UsernameNotFoundException
|
||||
String username = token.getName().toString();
|
||||
if (!users.containsKey(username)) {
|
||||
throw new UsernameNotFoundException(username + " not configured.");
|
||||
}
|
||||
// If allowed, populate the user details object with the authorities for
|
||||
// the user.
|
||||
return new UserDetails() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Collection<SimpleGrantedAuthority> getAuthorities() {
|
||||
final Collection<SimpleGrantedAuthority> auths = new ArrayList<>();
|
||||
users.get(username).forEach(a -> {
|
||||
auths.add(new SimpleGrantedAuthority(a));
|
||||
});
|
||||
return auths;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package timely.auth;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import org.apache.accumulo.core.security.ColumnVisibility;
|
||||
import timely.Configuration;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class VisibilityCache {
|
||||
|
||||
private static volatile Cache<String, ColumnVisibility> CACHE = null;
|
||||
|
||||
/** Used for testing */
|
||||
public static synchronized void init() {
|
||||
if (CACHE == null) {
|
||||
long defaultExpiration = Configuration.VISIBILITY_EXPIRATION_DEFAULT;
|
||||
int initialCapacity = Configuration.VISIBILITY_CACHE_INITIAL_CAPACITY_DEFAULT;
|
||||
long maxCapacity = Configuration.VISIBILITY_CACHE_MAX_CAPACITY_DEFAULT;
|
||||
CACHE = Caffeine.newBuilder().expireAfterAccess(defaultExpiration, TimeUnit.MINUTES)
|
||||
.initialCapacity(initialCapacity).maximumSize(maxCapacity).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized void init(Configuration config) {
|
||||
if (CACHE == null) {
|
||||
long expireMinutes = Long.parseLong(config.get(Configuration.VISIBILITY_CACHE_EXPIRATION));
|
||||
int initialCapacity = Integer.parseInt(config.get(Configuration.VISIBILITY_CACHE_INITIAL_CAPACITY));
|
||||
long maxCapacity = Long.parseLong(config.get(Configuration.VISIBILITY_CACHE_MAX_CAPACITY));
|
||||
CACHE = Caffeine.newBuilder().expireAfterAccess(expireMinutes, TimeUnit.MINUTES)
|
||||
.initialCapacity(initialCapacity).maximumSize(maxCapacity).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static ColumnVisibility getColumnVisibility(String visibilityString) {
|
||||
return CACHE.get(visibilityString, key -> generateNormalizedVisibility(key));
|
||||
}
|
||||
|
||||
private static final ColumnVisibility generateNormalizedVisibility(String visibilityString) {
|
||||
// it would be nice if Accumulo would make it easier to do this.
|
||||
return new ColumnVisibility(new ColumnVisibility(visibilityString).flatten());
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package timely.netty;
|
||||
|
||||
public class Constants {
|
||||
|
||||
public static final String ERR_WRITING_RESPONSE = "Error writing response to pipeline: {}";
|
||||
public static final String HTML_TYPE = "text/html";
|
||||
public static final String JSON_TYPE = "application/json";
|
||||
public static final String LOG_RETURNING_RESPONSE = "Returning response {}";
|
||||
public static final String COOKIE_NAME = "TSESSIONID";
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package timely.netty.http;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
|
||||
import timely.api.query.request.AggregatorsRequest;
|
||||
import timely.api.query.response.AggregatorsResponse;
|
||||
import timely.netty.Constants;
|
||||
import timely.sample.aggregators.Avg;
|
||||
import timely.sample.aggregators.Count;
|
||||
import timely.sample.aggregators.Dev;
|
||||
import timely.sample.aggregators.Max;
|
||||
import timely.sample.aggregators.Min;
|
||||
import timely.sample.aggregators.Sum;
|
||||
import timely.util.JsonUtil;
|
||||
|
||||
public class HttpAggregatorsRequestHandler extends SimpleChannelInboundHandler<AggregatorsRequest> implements
|
||||
TimelyHttpHandler {
|
||||
|
||||
private static final AggregatorsResponse RESPONSE = new AggregatorsResponse();
|
||||
|
||||
static {
|
||||
RESPONSE.addAggregator(Avg.class.getSimpleName().toLowerCase());
|
||||
RESPONSE.addAggregator(Dev.class.getSimpleName().toLowerCase());
|
||||
RESPONSE.addAggregator(Max.class.getSimpleName().toLowerCase());
|
||||
RESPONSE.addAggregator(Min.class.getSimpleName().toLowerCase());
|
||||
RESPONSE.addAggregator(Sum.class.getSimpleName().toLowerCase());
|
||||
RESPONSE.addAggregator(Count.class.getSimpleName().toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, AggregatorsRequest msg) throws Exception {
|
||||
byte[] buf = JsonUtil.getObjectMapper().writeValueAsBytes(RESPONSE);
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
|
||||
Unpooled.copiedBuffer(buf));
|
||||
response.headers().set(Names.CONTENT_TYPE, Constants.JSON_TYPE);
|
||||
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes());
|
||||
sendResponse(ctx, response);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package timely.netty.http;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import timely.Configuration;
|
||||
import timely.api.query.request.MetricsRequest;
|
||||
import timely.api.query.response.MetricsResponse;
|
||||
import timely.netty.Constants;
|
||||
import timely.util.JsonUtil;
|
||||
|
||||
public class HttpMetricsRequestHandler extends SimpleChannelInboundHandler<MetricsRequest> implements TimelyHttpHandler {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HttpMetricsRequestHandler.class);
|
||||
private Configuration conf = null;
|
||||
|
||||
public HttpMetricsRequestHandler(Configuration conf) {
|
||||
this.conf = conf;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, MetricsRequest msg) throws Exception {
|
||||
MetricsResponse r = new MetricsResponse(conf);
|
||||
String acceptValue = msg.getRequestHeaders().get(Names.ACCEPT);
|
||||
MediaType negotiatedType = MediaType.TEXT_HTML;
|
||||
if (null != acceptValue) {
|
||||
List<MediaType> requestedTypes = MediaType.parseMediaTypes(acceptValue);
|
||||
MediaType.sortBySpecificityAndQuality(requestedTypes);
|
||||
LOG.trace("Acceptable response types: {}", MediaType.toString(requestedTypes));
|
||||
for (MediaType t : requestedTypes) {
|
||||
if (t.includes(MediaType.TEXT_HTML)) {
|
||||
negotiatedType = MediaType.TEXT_HTML;
|
||||
LOG.trace("{} allows HTML", t.toString());
|
||||
break;
|
||||
}
|
||||
if (t.includes(MediaType.APPLICATION_JSON)) {
|
||||
negotiatedType = MediaType.APPLICATION_JSON;
|
||||
LOG.trace("{} allows JSON", t.toString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
byte[] buf = null;
|
||||
Object responseType = Constants.HTML_TYPE;
|
||||
if (negotiatedType.equals(MediaType.APPLICATION_JSON)) {
|
||||
buf = r.generateJson(JsonUtil.getObjectMapper()).getBytes(StandardCharsets.UTF_8);
|
||||
responseType = Constants.JSON_TYPE;
|
||||
} else {
|
||||
buf = r.generateHtml().toString().getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
|
||||
Unpooled.copiedBuffer(buf));
|
||||
response.headers().set(Names.CONTENT_TYPE, responseType);
|
||||
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes());
|
||||
sendResponse(ctx, response);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,327 +0,0 @@
|
||||
package timely.netty.http;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||
import io.netty.handler.codec.http.HttpMethod;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.QueryStringDecoder;
|
||||
import io.netty.handler.codec.http.cookie.ServerCookieDecoder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import timely.Configuration;
|
||||
import timely.api.AuthenticatedRequest;
|
||||
import timely.api.Request;
|
||||
import timely.api.model.Tag;
|
||||
import timely.api.query.request.AggregatorsRequest;
|
||||
import timely.api.query.request.BasicAuthLoginRequest;
|
||||
import timely.api.query.request.MetricsRequest;
|
||||
import timely.api.query.request.QueryRequest;
|
||||
import timely.api.query.request.QueryRequest.Filter;
|
||||
import timely.api.query.request.QueryRequest.RateOption;
|
||||
import timely.api.query.request.QueryRequest.SubQuery;
|
||||
import timely.api.query.request.SearchLookupRequest;
|
||||
import timely.api.query.request.SuggestRequest;
|
||||
import timely.api.query.request.X509LoginRequest;
|
||||
import timely.netty.Constants;
|
||||
import timely.api.query.response.TimelyException;
|
||||
import timely.auth.AuthCache;
|
||||
import timely.util.JsonUtil;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
|
||||
public class HttpQueryDecoder extends MessageToMessageDecoder<FullHttpRequest> implements TimelyHttpHandler {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HttpQueryDecoder.class);
|
||||
private static final String LOG_RECEIVED_REQUEST = "Received HTTP request {}";
|
||||
private static final String LOG_PARSED_REQUEST = "Parsed request {}";
|
||||
private static final String API_AGG = "/api/aggregators";
|
||||
private static final String API_QUERY = "/api/query";
|
||||
private static final String API_SEARCH_LOOKUP = "/api/search/lookup";
|
||||
private static final String API_SUGGEST = "/api/suggest";
|
||||
private static final String API_METRICS = "/api/metrics";
|
||||
private static final String API_LOGIN = "/login";
|
||||
private static final String NO_AUTHORIZATIONS = "";
|
||||
|
||||
private boolean anonymousAccessAllowed = false;
|
||||
|
||||
public HttpQueryDecoder(Configuration config) {
|
||||
this.anonymousAccessAllowed = config.getBoolean(Configuration.ALLOW_ANONYMOUS_ACCESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, FullHttpRequest msg, List<Object> out) throws Exception {
|
||||
|
||||
LOG.trace(LOG_RECEIVED_REQUEST, msg);
|
||||
|
||||
final StringBuilder buf = new StringBuilder();
|
||||
msg.headers().getAll(Names.COOKIE).forEach(h -> {
|
||||
ServerCookieDecoder.STRICT.decode(h).forEach(c -> {
|
||||
if (c.name().equals(Constants.COOKIE_NAME)) {
|
||||
if (buf.length() == 0) {
|
||||
buf.append(c.value());
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
String sId = buf.toString();
|
||||
if (sId.length() == 0 && this.anonymousAccessAllowed) {
|
||||
sId = NO_AUTHORIZATIONS;
|
||||
} else if (sId.length() == 0) {
|
||||
sId = null;
|
||||
}
|
||||
final String sessionId = sId;
|
||||
LOG.trace("SessionID: " + sessionId);
|
||||
|
||||
if (msg.getMethod().equals(HttpMethod.GET)) {
|
||||
final Collection<Request> r = parseURI(msg.getUri());
|
||||
r.forEach(req -> {
|
||||
if (req instanceof AuthenticatedRequest && sessionId != null) {
|
||||
((AuthenticatedRequest) req).setSessionId(sessionId);
|
||||
((AuthenticatedRequest) req).addHeaders(msg.headers().entries());
|
||||
}
|
||||
LOG.trace(LOG_PARSED_REQUEST, req);
|
||||
req.validate();
|
||||
out.add(req);
|
||||
});
|
||||
} else if (msg.getMethod().equals(HttpMethod.POST)) {
|
||||
final Collection<Request> r = parsePOST(msg.getUri(), msg.content().toString(StandardCharsets.UTF_8));
|
||||
r.forEach(req -> {
|
||||
if (req instanceof AuthenticatedRequest && sessionId != null) {
|
||||
((AuthenticatedRequest) req).setSessionId(sessionId);
|
||||
((AuthenticatedRequest) req).addHeaders(msg.headers().entries());
|
||||
}
|
||||
LOG.trace(LOG_PARSED_REQUEST, req);
|
||||
req.validate();
|
||||
out.add(req);
|
||||
});
|
||||
} else {
|
||||
TimelyException e = new TimelyException(HttpResponseStatus.METHOD_NOT_ALLOWED.code(),
|
||||
"unhandled method type", "");
|
||||
e.addResponseHeader(Names.ALLOW, HttpMethod.GET.name() + "," + HttpMethod.POST.name());
|
||||
LOG.warn("Unhandled HTTP request type {}", msg.getMethod());
|
||||
throw e;
|
||||
}
|
||||
for (Object r : out) {
|
||||
try {
|
||||
enforceAccess((Request) r);
|
||||
} catch (Exception e) {
|
||||
out.clear();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void enforceAccess(Request r) throws Exception {
|
||||
if (!this.anonymousAccessAllowed && (r instanceof AuthenticatedRequest)) {
|
||||
AuthenticatedRequest ar = (AuthenticatedRequest) r;
|
||||
if (StringUtils.isEmpty(ar.getSessionId())) {
|
||||
throw new TimelyException(HttpResponseStatus.UNAUTHORIZED.code(), "User must log in",
|
||||
"Anonymous access is disabled, log in first");
|
||||
}
|
||||
if (!AuthCache.getCache().asMap().containsKey(ar.getSessionId())) {
|
||||
throw new TimelyException(HttpResponseStatus.UNAUTHORIZED.code(), "User must log in",
|
||||
"Unknown session id was submitted, log in again");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<Request> parsePOST(String uri, String content) throws JsonParseException, JsonMappingException,
|
||||
IOException, TimelyException {
|
||||
LOG.trace("Recevied POST content: {}", content);
|
||||
final List<Request> requests = new ArrayList<>();
|
||||
final QueryStringDecoder decoder = new QueryStringDecoder(uri);
|
||||
if (decoder.path().equals(API_AGG)) {
|
||||
// no Content
|
||||
requests.add(new AggregatorsRequest());
|
||||
} else if (decoder.path().equals(API_QUERY)) {
|
||||
requests.add(JsonUtil.getObjectMapper().readValue(content, QueryRequest.class));
|
||||
} else if (decoder.path().equals(API_SEARCH_LOOKUP)) {
|
||||
requests.add(JsonUtil.getObjectMapper().readValue(content, SearchLookupRequest.class));
|
||||
} else if (decoder.path().equals(API_SUGGEST)) {
|
||||
requests.add(JsonUtil.getObjectMapper().readValue(content, SuggestRequest.class));
|
||||
} else if (decoder.path().equals(API_LOGIN)) {
|
||||
requests.add(JsonUtil.getObjectMapper().readValue(content, BasicAuthLoginRequest.class));
|
||||
} else {
|
||||
throw new TimelyException(HttpResponseStatus.NOT_FOUND.code(), "Unhandled query uri: " + decoder.path(), "");
|
||||
}
|
||||
LOG.trace("Parsed POST request {}", requests);
|
||||
return requests;
|
||||
}
|
||||
|
||||
public Collection<Request> parseURI(String uri) throws TimelyException {
|
||||
final List<Request> requests = new ArrayList<>();
|
||||
final QueryStringDecoder decoder = new QueryStringDecoder(uri);
|
||||
if (decoder.path().equals(API_AGG)) {
|
||||
requests.add(new AggregatorsRequest());
|
||||
} else if (decoder.path().equals(API_QUERY)) {
|
||||
final QueryRequest query = new QueryRequest();
|
||||
query.setStart(Long.parseLong(decoder.parameters().get("start").get(0)));
|
||||
if (decoder.parameters().containsKey("end")) {
|
||||
query.setEnd(Long.parseLong(decoder.parameters().get("end").get(0)));
|
||||
}
|
||||
if (decoder.parameters().containsKey("m")) {
|
||||
decoder.parameters()
|
||||
.get("m")
|
||||
.forEach(m -> {
|
||||
final SubQuery sub = new SubQuery();
|
||||
final String[] mParts = m.split(":");
|
||||
if (mParts.length < 2) {
|
||||
throw new IllegalArgumentException("Too few parameters for metric query");
|
||||
}
|
||||
if (mParts.length > 5) {
|
||||
throw new IllegalArgumentException("Too many parameters for metric query");
|
||||
}
|
||||
// Aggregator is required, it's in the first section
|
||||
sub.setAggregator(mParts[0]);
|
||||
// Parse the rates from the 2nd through
|
||||
// next to last sections
|
||||
for (int i = 1; i < mParts.length - 1; i++) {
|
||||
if (mParts[i].startsWith("rate")) {
|
||||
sub.setRate(true);
|
||||
final RateOption options = new RateOption();
|
||||
if (mParts[i].equals("rate")) {
|
||||
sub.setRateOptions(options);
|
||||
} else {
|
||||
final String rate = mParts[i].substring(5, mParts[i].length() - 1);
|
||||
final String[] rateOptions = rate.split(",");
|
||||
for (int x = 0; x < rateOptions.length; x++) {
|
||||
switch (x) {
|
||||
case 0:
|
||||
options.setCounter(rateOptions[x].endsWith("counter"));
|
||||
break;
|
||||
case 1:
|
||||
options.setCounterMax(Long.parseLong(rateOptions[x]));
|
||||
break;
|
||||
case 2:
|
||||
options.setResetValue(Long.parseLong(rateOptions[x]));
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
sub.setRateOptions(options);
|
||||
}
|
||||
} else {
|
||||
// downsample
|
||||
sub.setDownsample(Optional.of(mParts[i]));
|
||||
}
|
||||
}
|
||||
// Parse metric name and tags from last
|
||||
// section
|
||||
final String metricAndTags = mParts[mParts.length - 1];
|
||||
final int idx = metricAndTags.indexOf('{');
|
||||
if (-1 == idx) {
|
||||
// metric only
|
||||
sub.setMetric(metricAndTags);
|
||||
} else {
|
||||
sub.setMetric(metricAndTags.substring(0, idx));
|
||||
// Only supporting one set of {} as we
|
||||
// are not supporting filters right now.
|
||||
if (!metricAndTags.endsWith("}")) {
|
||||
throw new IllegalArgumentException("Tag section does not end with '}'");
|
||||
}
|
||||
final String[] tags = metricAndTags.substring(idx).split("}");
|
||||
if (tags.length > 0) {
|
||||
// Process the first set of tags, which
|
||||
// are groupBy
|
||||
final String groupingTags = tags[0];
|
||||
final String[] gTags = groupingTags.substring(1, groupingTags.length()).split(
|
||||
",");
|
||||
for (final String tag : gTags) {
|
||||
final String[] tParts = tag.split("=");
|
||||
final Filter f = new Filter();
|
||||
f.setGroupBy(true);
|
||||
f.setTagk(tParts[0]);
|
||||
f.setFilter(tParts[1]);
|
||||
sub.addFilter(f);
|
||||
}
|
||||
if (tags.length > 1) {
|
||||
// Process the first set of tags,
|
||||
// which are groupBy
|
||||
final String nonGroupingTags = tags[1];
|
||||
final String[] ngTags = nonGroupingTags.substring(1,
|
||||
nonGroupingTags.length()).split(",");
|
||||
for (final String tag : ngTags) {
|
||||
final String[] tParts = tag.split("=");
|
||||
sub.addTag(tParts[0], tParts[1]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
query.addQuery(sub);
|
||||
});
|
||||
}
|
||||
if (decoder.parameters().containsKey("tsuid")) {
|
||||
decoder.parameters().get("tsuid").forEach(ts -> {
|
||||
final SubQuery sub = new SubQuery();
|
||||
final int colon = ts.indexOf(':');
|
||||
if (-1 != colon) {
|
||||
sub.setAggregator(ts.substring(0, colon));
|
||||
}
|
||||
for (final String tsuid : ts.substring(colon + 1).split(",")) {
|
||||
sub.addTsuid(tsuid);
|
||||
}
|
||||
query.addQuery(sub);
|
||||
});
|
||||
}
|
||||
requests.add(query);
|
||||
|
||||
} else if (decoder.path().equals(API_SEARCH_LOOKUP)) {
|
||||
final SearchLookupRequest search = new SearchLookupRequest();
|
||||
if (!decoder.parameters().containsKey("m")) {
|
||||
throw new IllegalArgumentException("m parameter is required for lookup");
|
||||
}
|
||||
final String m = decoder.parameters().get("m").get(0);
|
||||
final int tagIdx = m.indexOf("{");
|
||||
if (-1 == tagIdx) {
|
||||
search.setQuery(m);
|
||||
} else {
|
||||
search.setQuery(m.substring(0, tagIdx));
|
||||
final String[] tags = m.substring(tagIdx + 1, m.length() - 1).split(",");
|
||||
for (final String tag : tags) {
|
||||
final String[] tParts = tag.split("=");
|
||||
final Tag t = new Tag(tParts[0], tParts[1]);
|
||||
search.addTag(t);
|
||||
}
|
||||
}
|
||||
if (decoder.parameters().containsKey("limit")) {
|
||||
search.setLimit(Integer.parseInt(decoder.parameters().get("limit").get(0)));
|
||||
}
|
||||
requests.add(search);
|
||||
} else if (decoder.path().equals(API_SUGGEST)) {
|
||||
final SuggestRequest suggest = new SuggestRequest();
|
||||
suggest.setType(decoder.parameters().get("type").get(0));
|
||||
if (decoder.parameters().containsKey("q")) {
|
||||
suggest.setQuery(Optional.of(decoder.parameters().get("q").get(0)));
|
||||
}
|
||||
if (decoder.parameters().containsKey("max")) {
|
||||
suggest.setMax(Integer.parseInt(decoder.parameters().get("max").get(0)));
|
||||
}
|
||||
requests.add(suggest);
|
||||
} else if (decoder.path().equals(API_METRICS)) {
|
||||
requests.add(new MetricsRequest());
|
||||
} else if (decoder.path().equals(API_LOGIN)) {
|
||||
requests.add(new X509LoginRequest());
|
||||
} else {
|
||||
throw new TimelyException(HttpResponseStatus.NOT_FOUND.code(), "Unhandled query uri: " + decoder.path(), "");
|
||||
}
|
||||
LOG.trace("Parsed GET request {}", requests);
|
||||
return requests;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package timely.netty.http;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import timely.api.query.request.QueryRequest;
|
||||
import timely.api.query.response.TimelyException;
|
||||
import timely.netty.Constants;
|
||||
import timely.store.DataStore;
|
||||
import timely.util.JsonUtil;
|
||||
|
||||
public class HttpQueryRequestHandler extends SimpleChannelInboundHandler<QueryRequest> implements TimelyHttpHandler {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HttpQueryRequestHandler.class);
|
||||
private final DataStore dataStore;
|
||||
|
||||
public HttpQueryRequestHandler(DataStore dataStore) {
|
||||
this.dataStore = dataStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, QueryRequest msg) throws Exception {
|
||||
byte[] buf = null;
|
||||
try {
|
||||
buf = JsonUtil.getObjectMapper().writeValueAsBytes(dataStore.query(msg));
|
||||
} catch (TimelyException e) {
|
||||
if (e.getMessage().contains("No matching tags")) {
|
||||
LOG.trace(e.getMessage());
|
||||
} else {
|
||||
LOG.error(e.getMessage(), e);
|
||||
}
|
||||
this.sendHttpError(ctx, e);
|
||||
return;
|
||||
}
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
|
||||
Unpooled.copiedBuffer(buf));
|
||||
response.headers().set(Names.CONTENT_TYPE, Constants.JSON_TYPE);
|
||||
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes());
|
||||
sendResponse(ctx, response);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package timely.netty.http;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import timely.api.query.request.SearchLookupRequest;
|
||||
import timely.api.query.response.TimelyException;
|
||||
import timely.netty.Constants;
|
||||
import timely.store.DataStore;
|
||||
import timely.util.JsonUtil;
|
||||
|
||||
public class HttpSearchLookupRequestHandler extends SimpleChannelInboundHandler<SearchLookupRequest> implements
|
||||
TimelyHttpHandler {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HttpSearchLookupRequestHandler.class);
|
||||
private DataStore dataStore;
|
||||
|
||||
public HttpSearchLookupRequestHandler(DataStore dataStore) {
|
||||
this.dataStore = dataStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, SearchLookupRequest msg) throws Exception {
|
||||
byte[] buf = null;
|
||||
try {
|
||||
buf = JsonUtil.getObjectMapper().writeValueAsBytes(dataStore.lookup(msg));
|
||||
} catch (TimelyException e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
this.sendHttpError(ctx, e);
|
||||
return;
|
||||
}
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
|
||||
Unpooled.copiedBuffer(buf));
|
||||
response.headers().set(Names.CONTENT_TYPE, Constants.JSON_TYPE);
|
||||
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes());
|
||||
sendResponse(ctx, response);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package timely.netty.http;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import timely.api.query.request.SuggestRequest;
|
||||
import timely.api.query.response.TimelyException;
|
||||
import timely.netty.Constants;
|
||||
import timely.store.DataStore;
|
||||
import timely.util.JsonUtil;
|
||||
|
||||
public class HttpSuggestRequestHandler extends SimpleChannelInboundHandler<SuggestRequest> implements TimelyHttpHandler {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HttpSuggestRequestHandler.class);
|
||||
private final DataStore dataStore;
|
||||
|
||||
public HttpSuggestRequestHandler(DataStore dataStore) {
|
||||
this.dataStore = dataStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, SuggestRequest msg) throws Exception {
|
||||
byte[] buf = null;
|
||||
try {
|
||||
buf = JsonUtil.getObjectMapper().writeValueAsBytes(dataStore.suggest(msg));
|
||||
} catch (TimelyException e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
this.sendHttpError(ctx, e);
|
||||
return;
|
||||
}
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
|
||||
Unpooled.copiedBuffer(buf));
|
||||
response.headers().set(Names.CONTENT_TYPE, Constants.JSON_TYPE);
|
||||
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes());
|
||||
sendResponse(ctx, response);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package timely.netty.http;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import timely.api.query.response.TimelyException;
|
||||
|
||||
public class TimelyExceptionHandler extends SimpleChannelInboundHandler<TimelyException> implements TimelyHttpHandler {
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, TimelyException msg) throws Exception {
|
||||
this.sendHttpError(ctx, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
if (cause instanceof TimelyException) {
|
||||
this.sendHttpError(ctx, (TimelyException) cause);
|
||||
} else if (null != cause.getCause() && cause.getCause() instanceof TimelyException) {
|
||||
this.sendHttpError(ctx, (TimelyException) cause.getCause());
|
||||
} else {
|
||||
TimelyException e = new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(),
|
||||
cause.getMessage(), "");
|
||||
this.sendHttpError(ctx, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package timely.netty.http;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import timely.api.query.response.TimelyException;
|
||||
import timely.netty.Constants;
|
||||
import timely.util.JsonUtil;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
|
||||
public interface TimelyHttpHandler {
|
||||
|
||||
Logger LOG = LoggerFactory.getLogger(TimelyHttpHandler.class);
|
||||
|
||||
default void sendHttpError(ChannelHandlerContext ctx, TimelyException e) throws JsonProcessingException {
|
||||
LOG.error("Error in pipeline, response code: " + e.getCode(), e);
|
||||
byte[] buf = JsonUtil.getObjectMapper().writeValueAsBytes(
|
||||
"ResponseCode: " + e.getCode() + " Message: " + e.getMessage());
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(e
|
||||
.getCode()), Unpooled.copiedBuffer(buf));
|
||||
response.headers().set(Names.CONTENT_TYPE, Constants.JSON_TYPE);
|
||||
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes());
|
||||
e.getResponseHeaders().entrySet().forEach(entry -> response.headers().set(entry.getKey(), entry.getValue()));
|
||||
// Send the error response
|
||||
sendResponse(ctx, response);
|
||||
}
|
||||
|
||||
default void sendResponse(ChannelHandlerContext ctx, Object msg) {
|
||||
ChannelFuture f = ctx.writeAndFlush(msg);
|
||||
LOG.trace(Constants.LOG_RETURNING_RESPONSE, msg);
|
||||
if (!f.isSuccess()) {
|
||||
LOG.error(Constants.ERR_WRITING_RESPONSE, f.cause());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package timely.netty.http.login;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
||||
import timely.Configuration;
|
||||
import timely.api.query.request.BasicAuthLoginRequest;
|
||||
import timely.auth.AuthenticationService;
|
||||
|
||||
public class BasicAuthLoginRequestHandler extends TimelyLoginRequestHandler<BasicAuthLoginRequest> {
|
||||
|
||||
public BasicAuthLoginRequestHandler(Configuration conf) {
|
||||
super(conf);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Authentication authenticate(ChannelHandlerContext ctx, BasicAuthLoginRequest msg)
|
||||
throws AuthenticationException {
|
||||
// Perform the login process using username/password
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(msg.getUsername(),
|
||||
msg.getPassword());
|
||||
return AuthenticationService.getAuthenticationManager().authenticate(token);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
package timely.netty.http.login;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
import io.netty.handler.codec.http.cookie.DefaultCookie;
|
||||
import io.netty.handler.codec.http.cookie.ServerCookieEncoder;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
import timely.Configuration;
|
||||
import timely.auth.AuthCache;
|
||||
import timely.netty.Constants;
|
||||
import timely.netty.http.TimelyHttpHandler;
|
||||
|
||||
public abstract class TimelyLoginRequestHandler<T> extends SimpleChannelInboundHandler<T> implements TimelyHttpHandler {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TimelyLoginRequestHandler.class);
|
||||
|
||||
private long maxAge = Configuration.SESSION_MAX_AGE_DEFAULT;
|
||||
private String domain = null;
|
||||
private String redirect = null;
|
||||
|
||||
public TimelyLoginRequestHandler(Configuration conf) {
|
||||
String ma = conf.get(Configuration.SESSION_MAX_AGE);
|
||||
if (null != ma) {
|
||||
maxAge = Long.parseLong(ma);
|
||||
}
|
||||
domain = conf.get(Configuration.TIMELY_HTTP_ADDRESS);
|
||||
redirect = conf.get(Configuration.GRAFANA_HTTP_ADDRESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void channelRead0(ChannelHandlerContext ctx, T loginRequest) throws Exception {
|
||||
try {
|
||||
LOG.trace("Authenticating {}", loginRequest);
|
||||
Authentication auth = authenticate(ctx, loginRequest);
|
||||
LOG.trace("Authenticated {}", auth);
|
||||
String sessionId = URLEncoder.encode(UUID.randomUUID().toString(), StandardCharsets.UTF_8.name());
|
||||
AuthCache.getCache().put(sessionId, auth);
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||
HttpResponseStatus.TEMPORARY_REDIRECT);
|
||||
response.headers().set(Names.CONTENT_TYPE, Constants.JSON_TYPE);
|
||||
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes());
|
||||
response.headers().set(Names.LOCATION, redirect);
|
||||
DefaultCookie cookie = new DefaultCookie(Constants.COOKIE_NAME, sessionId);
|
||||
cookie.setDomain(domain);
|
||||
cookie.setMaxAge(maxAge);
|
||||
cookie.setHttpOnly(true);
|
||||
cookie.setSecure(true);
|
||||
response.headers().set(Names.SET_COOKIE, ServerCookieEncoder.STRICT.encode(cookie));
|
||||
sendResponse(ctx, response);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Login failure", e);
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||
HttpResponseStatus.UNAUTHORIZED);
|
||||
response.headers().set(Names.CONTENT_TYPE, Constants.JSON_TYPE);
|
||||
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes());
|
||||
sendResponse(ctx, response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected abstract Authentication authenticate(ChannelHandlerContext ctx, T loginRequest) throws Exception;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package timely.netty.http.login;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.ssl.SslHandler;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||
|
||||
import timely.Configuration;
|
||||
import timely.api.query.request.X509LoginRequest;
|
||||
import timely.auth.AuthenticationService;
|
||||
|
||||
public class X509LoginRequestHandler extends TimelyLoginRequestHandler<X509LoginRequest> {
|
||||
|
||||
public X509LoginRequestHandler(Configuration conf) {
|
||||
super(conf);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Authentication authenticate(ChannelHandlerContext ctx, X509LoginRequest loginRequest) throws Exception {
|
||||
// If we are operating in 2 way SSL, then get the subjectDN from the
|
||||
// client certificate and perform the login process.
|
||||
SslHandler sslHandler = (SslHandler) ctx.channel().pipeline().get("ssl");
|
||||
if (null != sslHandler) {
|
||||
X509Certificate clientCert = (X509Certificate) sslHandler.engine().getSession().getPeerCertificates()[0];
|
||||
String subjectDN = AuthenticationService.extractDN(clientCert);
|
||||
PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(subjectDN, clientCert);
|
||||
return AuthenticationService.getAuthenticationManager().authenticate(token);
|
||||
} else {
|
||||
throw new IllegalStateException("The expected SSL handler is not in the pipeline.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package timely.netty.tcp;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import timely.api.model.Metric;
|
||||
import timely.api.model.Tag;
|
||||
import timely.auth.VisibilityCache;
|
||||
|
||||
public class TcpPutDecoder extends ByteToMessageDecoder {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TcpPutDecoder.class);
|
||||
|
||||
private static final String VISIBILITY_TAG = "viz=";
|
||||
private static final int VISIBILITY_TAG_LENGTH = VISIBILITY_TAG.length();
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
|
||||
// put specification
|
||||
//
|
||||
// put <metricName> <timestamp> <value> <tagK=tagV> <tagK=tagV> ...
|
||||
ByteBuf buf = in.readBytes(in.readableBytes());
|
||||
if (buf == Unpooled.EMPTY_BUFFER) {
|
||||
return;
|
||||
}
|
||||
String input = new String(buf.array(), UTF_8);
|
||||
LOG.trace("Received input {}", input);
|
||||
try {
|
||||
String[] parts = input.split(" ");
|
||||
if (parts.length == 1 && parts[0].toLowerCase().startsWith("version")) {
|
||||
final ByteBuf response = ctx.alloc().buffer();
|
||||
response.writeBytes("0.0.1\n".getBytes(UTF_8));
|
||||
ctx.writeAndFlush(response);
|
||||
return;
|
||||
}
|
||||
Metric put = new Metric();
|
||||
put.setMetric(parts[1]);
|
||||
long ts = Long.parseLong(parts[2]);
|
||||
if (ts < 9999999999L) {
|
||||
ts *= 1000;
|
||||
}
|
||||
put.setTimestamp(ts);
|
||||
put.setValue(Double.valueOf(parts[3]));
|
||||
String part;
|
||||
for (int i = 4; i < parts.length; i++) {
|
||||
part = parts[i];
|
||||
if (part.startsWith(VISIBILITY_TAG) && part.length() > VISIBILITY_TAG_LENGTH) {
|
||||
put.setVisibility(VisibilityCache.getColumnVisibility(part.substring(VISIBILITY_TAG_LENGTH)));
|
||||
} else if (!part.isEmpty()) {
|
||||
put.addTag(new Tag(parts[i]));
|
||||
}
|
||||
}
|
||||
out.add(put);
|
||||
LOG.trace("Converted {} to {}", input, put);
|
||||
} catch (NumberFormatException e) {
|
||||
LOG.error("Error converting input '{}' to put metric", input, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package timely.netty.tcp;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import timely.api.model.Metric;
|
||||
import timely.netty.Constants;
|
||||
import timely.store.DataStore;
|
||||
|
||||
public class TcpPutHandler extends SimpleChannelInboundHandler<Metric> {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TcpPutHandler.class);
|
||||
private static final String LOG_ERR_MSG = "Error storing put metric: {}";
|
||||
private static final String ERR_MSG = "Error storing put metric: ";
|
||||
private DataStore store;
|
||||
|
||||
public TcpPutHandler(DataStore store) {
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, Metric msg) throws Exception {
|
||||
LOG.trace("Received PutMetric {}", msg);
|
||||
try {
|
||||
store.store(msg);
|
||||
} catch (Exception e) {
|
||||
LOG.error(LOG_ERR_MSG, msg, e);
|
||||
ChannelFuture cf = ctx.writeAndFlush(Unpooled.copiedBuffer((ERR_MSG + e.getMessage() + "\n")
|
||||
.getBytes(StandardCharsets.UTF_8)));
|
||||
if (!cf.isSuccess()) {
|
||||
LOG.error(Constants.ERR_WRITING_RESPONSE, cf.cause());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package timely.sample;
|
||||
|
||||
import timely.sample.aggregators.Avg;
|
||||
import timely.sample.aggregators.Count;
|
||||
import timely.sample.aggregators.Dev;
|
||||
import timely.sample.aggregators.Max;
|
||||
import timely.sample.aggregators.Min;
|
||||
import timely.sample.aggregators.Sum;
|
||||
|
||||
public interface Aggregator {
|
||||
|
||||
/**
|
||||
* Given the current value, and the current count, incorporate the new value
|
||||
* into current and return the result.
|
||||
*
|
||||
* @param current
|
||||
* last returned value from aggregate, or zero
|
||||
* @param count
|
||||
* the number of items aggregated previously into current
|
||||
* @param update
|
||||
* a new value to incorporate
|
||||
* @return the combination of update and current
|
||||
*/
|
||||
double aggregate(double current, int count, double update);
|
||||
|
||||
/**
|
||||
* Compute the final value given the aggregated amount and the number of
|
||||
* items in the aggregate.
|
||||
*
|
||||
* @param current
|
||||
* the last value returned from aggregate
|
||||
* @param count
|
||||
* the number of values in the aggregate
|
||||
* @return the combination of the current value given the count
|
||||
*/
|
||||
double last(double current, int count);
|
||||
|
||||
static Class<? extends Aggregator> getAggregator(String aggregatorName) {
|
||||
switch (aggregatorName) {
|
||||
case "sum":
|
||||
return Sum.class;
|
||||
case "max":
|
||||
return Max.class;
|
||||
case "dev":
|
||||
return Dev.class;
|
||||
case "min":
|
||||
return Min.class;
|
||||
case "avg":
|
||||
return Avg.class;
|
||||
case "count":
|
||||
return Count.class;
|
||||
default:
|
||||
return Avg.class;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
package timely.sample;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import timely.api.query.request.QueryRequest.RateOption;
|
||||
|
||||
public class Downsample implements Iterable<Sample>, Serializable {
|
||||
|
||||
private static Logger LOG = LoggerFactory.getLogger(Downsample.class);
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private transient Aggregator aggregator;
|
||||
private final String aggregatorClassname;
|
||||
private final long start;
|
||||
private final long period;
|
||||
private final double[] values;
|
||||
private final int[] counts;
|
||||
|
||||
public Downsample(long start, long end, long period, Aggregator agg) {
|
||||
Preconditions.checkArgument(start < end, "Start must be < end");
|
||||
Preconditions.checkArgument(period >= 1, "period cannot be < 1");
|
||||
Preconditions.checkNotNull(agg, "Aggregator object cannot be null");
|
||||
this.start = start;
|
||||
this.period = period;
|
||||
this.aggregator = agg;
|
||||
this.aggregatorClassname = agg.getClass().getName();
|
||||
long buckets = (end - start) / period + 1;
|
||||
if (buckets > Integer.MAX_VALUE) {
|
||||
throw new RuntimeException("Cannot downsample to " + buckets + " items");
|
||||
}
|
||||
values = new double[(int) buckets];
|
||||
counts = new int[(int) buckets];
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
in.defaultReadObject();
|
||||
try {
|
||||
aggregator = (Aggregator) getClass().getClassLoader().loadClass(aggregatorClassname).newInstance();
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void add(long ts, double value) {
|
||||
if (ts < start) {
|
||||
throw new IllegalArgumentException("timestamp is out of range");
|
||||
}
|
||||
int bucket = (int) ((ts - start) / period);
|
||||
if (bucket > values.length) {
|
||||
throw new IllegalArgumentException("timestamp is out of range");
|
||||
}
|
||||
values[bucket] = aggregator.aggregate(values[bucket], counts[bucket], value);
|
||||
counts[bucket]++;
|
||||
}
|
||||
|
||||
public void merge(Downsample other) {
|
||||
for (int i = 0; i < other.counts.length; i++) {
|
||||
if (other.counts[i] > 0) {
|
||||
counts[i] += other.counts[i];
|
||||
values[i] = aggregator.aggregate(values[i], counts[i], other.values[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Sample> iterator() {
|
||||
return new Iterator<Sample>() {
|
||||
|
||||
int next = 0;
|
||||
Sample sample = new Sample();
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
while (next < values.length && counts[next] == 0) {
|
||||
next++;
|
||||
}
|
||||
return next < values.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sample next() {
|
||||
double value = aggregator.last(values[next], counts[next]);
|
||||
sample.set(start + next * period, value);
|
||||
next++;
|
||||
return sample;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringBuilder tsb = new ToStringBuilder(this);
|
||||
tsb.append("aggregator", this.aggregatorClassname);
|
||||
tsb.append("counts", this.counts);
|
||||
tsb.append("period", this.period);
|
||||
tsb.append("start", this.start);
|
||||
tsb.append("values", this.values);
|
||||
return tsb.toString();
|
||||
}
|
||||
|
||||
public static Downsample combine(Collection<Downsample> samples, RateOption rateOption) {
|
||||
List<Downsample> sampleList = new ArrayList<>(samples.size());
|
||||
if (samples.isEmpty()) {
|
||||
throw new IllegalArgumentException("Empty samples to combine");
|
||||
}
|
||||
for (Downsample ds : samples) {
|
||||
if (rateOption != null && rateOption.isCounter()) {
|
||||
ds = convertCountToRate(ds, rateOption.getResetValue(), rateOption.getCounterMax());
|
||||
}
|
||||
ds = interpolateMissingValues(ds);
|
||||
sampleList.add(ds);
|
||||
}
|
||||
Iterator<Downsample> iter = sampleList.iterator();
|
||||
Downsample result = iter.next();
|
||||
while (iter.hasNext()) {
|
||||
result.merge(iter.next());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Downsample interpolateMissingValues(Downsample ds) {
|
||||
Downsample result = new Downsample(ds.start, ds.start + ds.counts.length * ds.period, ds.period, ds.aggregator);
|
||||
int lastPos = -1;
|
||||
for (int i = 0; i < ds.counts.length; i++) {
|
||||
if (ds.counts[i] != 0) {
|
||||
result.values[i] = ds.values[i];
|
||||
result.counts[i] = ds.counts[i];
|
||||
if (lastPos >= 0) {
|
||||
double diff = ds.values[i] - ds.values[lastPos];
|
||||
for (int j = lastPos + 1; j < i; j++) {
|
||||
result.values[j] = result.values[j - 1] + diff / (i - lastPos);
|
||||
result.counts[j] = 1;
|
||||
}
|
||||
}
|
||||
lastPos = i;
|
||||
}
|
||||
}
|
||||
LOG.debug("interpolate: {}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Downsample convertCountToRate(Downsample ds, long resetValue, long counterMax) {
|
||||
Downsample result = new Downsample(ds.start + ds.period, ds.start + ds.counts.length * ds.period, ds.period,
|
||||
ds.aggregator);
|
||||
int lastPos = 0;
|
||||
for (int i = 1; i < ds.counts.length; i++) {
|
||||
if (ds.counts[i] != 0) {
|
||||
if (((Long) counterMax).equals(((Double) ds.values[i]).longValue())) {
|
||||
result.values[i - 1] = resetValue;
|
||||
result.counts[i - 1] = 1;
|
||||
} else {
|
||||
double diff = ds.values[i] - ds.values[lastPos];
|
||||
if (diff < 0) {
|
||||
// wrap around formula from
|
||||
// https://collectd.org/wiki/index.php/Data_source says
|
||||
// to do
|
||||
// the following:
|
||||
// double wrapValue = (counterMax - ds.values[lastPos] +
|
||||
// ds.values[i]) / (ds.period);
|
||||
// When counterMax is not defined it defaults to
|
||||
// Long.MAX_VALUE which leaves wrapValue
|
||||
// very close to counterMax. This renders the resulting
|
||||
// graph useless as it blows up the
|
||||
// scale, we are going to set the value to -1. If we
|
||||
// have a constantly decreasing counter,
|
||||
// which in theory should not happen, then all of the
|
||||
// values will be -1.
|
||||
double wrapValue = -1.0;
|
||||
result.values[i - 1] = wrapValue;
|
||||
result.counts[i - 1] = 1;
|
||||
} else {
|
||||
result.values[i - 1] = diff;
|
||||
result.counts[i - 1] = 1;
|
||||
}
|
||||
}
|
||||
lastPos = i;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package timely.sample;
|
||||
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import timely.api.query.response.TimelyException;
|
||||
|
||||
public class DownsampleFactory {
|
||||
|
||||
private final long start;
|
||||
private final long end;
|
||||
private final long period;
|
||||
private final Class<? extends Aggregator> aggClass;
|
||||
|
||||
public DownsampleFactory(long start, long end, long period, Class<? extends Aggregator> aggClass) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.period = period;
|
||||
this.aggClass = aggClass;
|
||||
}
|
||||
|
||||
public Downsample create() throws TimelyException {
|
||||
try {
|
||||
return new Downsample(start, end, period, aggClass.newInstance());
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
throw new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(),
|
||||
"Error creating aggregator class: " + aggClass, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package timely.sample;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
||||
public class Sample {
|
||||
|
||||
public long timestamp;
|
||||
public double value;
|
||||
|
||||
public Sample() {
|
||||
}
|
||||
|
||||
public void set(long timestamp, double value) {
|
||||
this.timestamp = timestamp;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringBuilder tsb = new ToStringBuilder(this);
|
||||
tsb.append("timestamp", this.timestamp);
|
||||
tsb.append("value", this.value);
|
||||
return tsb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package timely.sample.aggregators;
|
||||
|
||||
import timely.sample.Aggregator;
|
||||
|
||||
public class Avg implements Aggregator {
|
||||
|
||||
@Override
|
||||
public double aggregate(double current, int count, double update) {
|
||||
return current + update;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double last(double current, int count) {
|
||||
return current / count;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package timely.sample.aggregators;
|
||||
|
||||
import timely.sample.Aggregator;
|
||||
|
||||
public class Count implements Aggregator {
|
||||
|
||||
@Override
|
||||
public double aggregate(double current, int count, double update) {
|
||||
return 0.;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double last(double current, int count) {
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package timely.sample.aggregators;
|
||||
|
||||
import timely.sample.Aggregator;
|
||||
|
||||
public class Dev implements Aggregator {
|
||||
|
||||
@Override
|
||||
public double aggregate(double current, int count, double update) {
|
||||
return current + (update * update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double last(double current, int count) {
|
||||
if (count == 1) {
|
||||
return Math.sqrt(current);
|
||||
}
|
||||
return Math.sqrt(current / (count - 1));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package timely.sample.aggregators;
|
||||
|
||||
import timely.sample.Aggregator;
|
||||
|
||||
public class Max implements Aggregator {
|
||||
|
||||
@Override
|
||||
public double aggregate(double current, int count, double update) {
|
||||
if (count == 0) {
|
||||
return update;
|
||||
}
|
||||
return Math.max(current, update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double last(double current, int count) {
|
||||
return current;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package timely.sample.aggregators;
|
||||
|
||||
import timely.sample.Aggregator;
|
||||
|
||||
public class Min implements Aggregator {
|
||||
|
||||
@Override
|
||||
public double aggregate(double current, int count, double update) {
|
||||
if (count == 0) {
|
||||
return update;
|
||||
}
|
||||
return Math.min(current, update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double last(double current, int count) {
|
||||
return current;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package timely.sample.aggregators;
|
||||
|
||||
import timely.sample.Aggregator;
|
||||
|
||||
public class Sum implements Aggregator {
|
||||
|
||||
@Override
|
||||
public double aggregate(double current, int count, double update) {
|
||||
return current + update;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double last(double current, int count) {
|
||||
return current;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
package timely.sample.iterators;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.accumulo.core.client.IteratorSetting;
|
||||
import org.apache.accumulo.core.data.Key;
|
||||
import org.apache.accumulo.core.data.Value;
|
||||
import org.apache.accumulo.core.iterators.IteratorEnvironment;
|
||||
import org.apache.accumulo.core.iterators.SortedKeyValueIterator;
|
||||
import org.apache.accumulo.core.iterators.WrappingIterator;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import timely.api.model.Metric;
|
||||
import timely.api.model.Tag;
|
||||
import timely.api.query.response.TimelyException;
|
||||
import timely.sample.Aggregator;
|
||||
import timely.sample.Downsample;
|
||||
import timely.sample.DownsampleFactory;
|
||||
|
||||
public class DownsampleIterator extends WrappingIterator {
|
||||
|
||||
private static final String START = "downsample.start";
|
||||
private static final String END = "downsample.end";
|
||||
private static final String PERIOD = "downsample.period";
|
||||
private static final String QUERYTAGS = "downsample.querytags";
|
||||
private static final String AGGCLASS = "downsample.aggclass";
|
||||
|
||||
private DownsampleFactory factory;
|
||||
private final Map<Set<Tag>, Downsample> value = new HashMap<>();
|
||||
private Key last;
|
||||
private final Set<String> queryTags = new HashSet<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void init(SortedKeyValueIterator<Key, Value> source, Map<String, String> options, IteratorEnvironment env)
|
||||
throws IOException {
|
||||
super.init(source, options, env);
|
||||
long start = Long.parseLong(options.get(START));
|
||||
long end = Long.parseLong(options.get(END));
|
||||
long period = Long.parseLong(options.get(PERIOD));
|
||||
String aggClassname = options.get(AGGCLASS);
|
||||
queryTags.addAll(Arrays.asList(options.get(QUERYTAGS).split(",")));
|
||||
Class<?> aggClass;
|
||||
try {
|
||||
aggClass = this.getClass().getClassLoader().loadClass(aggClassname);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
factory = new DownsampleFactory(start, end, period, (Class<? extends Aggregator>) aggClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTop() {
|
||||
while (super.hasTop()) {
|
||||
last = super.getTopKey();
|
||||
Metric metric = Metric.parse(super.getTopKey(), super.getTopValue());
|
||||
Set<Tag> tags = new HashSet<Tag>(metric.getTags());
|
||||
Downsample sample = value.get(tags);
|
||||
if (sample == null) {
|
||||
try {
|
||||
value.put(tags, sample = factory.create());
|
||||
} catch (TimelyException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
sample.add(metric.getTimestamp(), metric.getValue());
|
||||
try {
|
||||
super.next();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return last != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key getTopKey() {
|
||||
return last;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value getTopValue() {
|
||||
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ObjectOutputStream out = new ObjectOutputStream(bos)) {
|
||||
out.writeObject(value);
|
||||
out.flush();
|
||||
return new Value(bos.toByteArray());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void next() throws IOException {
|
||||
last = null;
|
||||
}
|
||||
|
||||
public static void setDownsampleOptions(IteratorSetting is, long start, long end, long period,
|
||||
Collection<String> queryTags, String classname) {
|
||||
is.addOption(START, "" + start);
|
||||
is.addOption(END, "" + end);
|
||||
is.addOption(PERIOD, "" + period);
|
||||
is.addOption(QUERYTAGS, StringUtils.join(queryTags, ","));
|
||||
is.addOption(AGGCLASS, classname);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Map<Set<Tag>, Downsample> decodeValue(Value value) throws IOException, ClassNotFoundException {
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(value.get());
|
||||
ObjectInputStream ois = new ObjectInputStream(bis);
|
||||
return (Map<Set<Tag>, Downsample>) ois.readObject();
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package timely.store;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import timely.api.model.Metric;
|
||||
import timely.api.query.request.QueryRequest;
|
||||
import timely.api.query.request.SearchLookupRequest;
|
||||
import timely.api.query.request.SuggestRequest;
|
||||
import timely.api.query.response.QueryResponse;
|
||||
import timely.api.query.response.SearchLookupResponse;
|
||||
import timely.api.query.response.SuggestResponse;
|
||||
import timely.api.query.response.TimelyException;
|
||||
|
||||
public interface DataStore {
|
||||
|
||||
void store(Metric metric) throws TimelyException;
|
||||
|
||||
SuggestResponse suggest(SuggestRequest query) throws TimelyException;
|
||||
|
||||
SearchLookupResponse lookup(SearchLookupRequest msg) throws TimelyException;
|
||||
|
||||
List<QueryResponse> query(QueryRequest msg) throws TimelyException;
|
||||
|
||||
void flush() throws TimelyException;
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package timely.store;
|
||||
|
||||
import timely.Configuration;
|
||||
import timely.api.query.response.TimelyException;
|
||||
|
||||
public class DataStoreFactory {
|
||||
|
||||
public static DataStore create(Configuration conf, int numWriteThreads) throws TimelyException {
|
||||
|
||||
return new DataStoreImpl(conf, numWriteThreads);
|
||||
}
|
||||
}
|
||||
@@ -1,707 +0,0 @@
|
||||
package timely.store;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.apache.accumulo.core.conf.AccumuloConfiguration.getMemoryInBytes;
|
||||
import static org.apache.accumulo.core.conf.AccumuloConfiguration.getTimeInMillis;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.accumulo.core.client.BatchScanner;
|
||||
import org.apache.accumulo.core.client.BatchWriter;
|
||||
import org.apache.accumulo.core.client.BatchWriterConfig;
|
||||
import org.apache.accumulo.core.client.ClientConfiguration;
|
||||
import org.apache.accumulo.core.client.Connector;
|
||||
import org.apache.accumulo.core.client.Instance;
|
||||
import org.apache.accumulo.core.client.IteratorSetting;
|
||||
import org.apache.accumulo.core.client.MutationsRejectedException;
|
||||
import org.apache.accumulo.core.client.NamespaceExistsException;
|
||||
import org.apache.accumulo.core.client.Scanner;
|
||||
import org.apache.accumulo.core.client.TableExistsException;
|
||||
import org.apache.accumulo.core.client.TableNotFoundException;
|
||||
import org.apache.accumulo.core.client.ZooKeeperInstance;
|
||||
import org.apache.accumulo.core.client.security.tokens.PasswordToken;
|
||||
import org.apache.accumulo.core.data.Key;
|
||||
import org.apache.accumulo.core.data.Mutation;
|
||||
import org.apache.accumulo.core.data.PartialKey;
|
||||
import org.apache.accumulo.core.data.Range;
|
||||
import org.apache.accumulo.core.data.Value;
|
||||
import org.apache.accumulo.core.iterators.IteratorUtil.IteratorScope;
|
||||
import org.apache.accumulo.core.iterators.user.AgeOffFilter;
|
||||
import org.apache.accumulo.core.iterators.user.RegExFilter;
|
||||
import org.apache.accumulo.core.security.Authorizations;
|
||||
import org.apache.accumulo.core.util.Pair;
|
||||
import org.apache.commons.configuration.BaseConfiguration;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hadoop.io.Text;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import timely.Configuration;
|
||||
import timely.Server;
|
||||
import timely.api.AuthenticatedRequest;
|
||||
import timely.api.model.Meta;
|
||||
import timely.api.model.Metric;
|
||||
import timely.api.model.Tag;
|
||||
import timely.api.query.request.QueryRequest;
|
||||
import timely.api.query.request.QueryRequest.RateOption;
|
||||
import timely.api.query.request.QueryRequest.SubQuery;
|
||||
import timely.api.query.request.SearchLookupRequest;
|
||||
import timely.api.query.request.SuggestRequest;
|
||||
import timely.api.query.response.QueryResponse;
|
||||
import timely.api.query.response.SearchLookupResponse;
|
||||
import timely.api.query.response.SearchLookupResponse.Result;
|
||||
import timely.api.query.response.SuggestResponse;
|
||||
import timely.api.query.response.TimelyException;
|
||||
import timely.auth.AuthCache;
|
||||
import timely.sample.Aggregator;
|
||||
import timely.sample.Downsample;
|
||||
import timely.sample.Sample;
|
||||
import timely.sample.iterators.DownsampleIterator;
|
||||
import timely.util.MetaKeySet;
|
||||
|
||||
public class DataStoreImpl implements DataStore {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DataStoreImpl.class);
|
||||
|
||||
private static final long METRICS_PERIOD = 30000;
|
||||
private static final long DEFAULT_DOWNSAMPLE_MS = 60000;
|
||||
|
||||
/*
|
||||
* Pair doesn't implement Comparable
|
||||
*/
|
||||
private static class MetricTagK extends Pair<String, String> implements Comparable<MetricTagK> {
|
||||
|
||||
public MetricTagK(String f, String s) {
|
||||
super(f, s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(MetricTagK o) {
|
||||
int result = getFirst().compareTo(o.getFirst());
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
return getSecond().compareTo(o.getSecond());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Configuration conf;
|
||||
private final Connector connector;
|
||||
private MetaCache metaCache = null;
|
||||
private final AtomicLong lastCountTime = new AtomicLong(System.currentTimeMillis());
|
||||
private final AtomicReference<SortedMap<MetricTagK, Integer>> metaCounts = new AtomicReference<>(new TreeMap<>());
|
||||
private final String metricsTable;
|
||||
private final String metaTable;
|
||||
private final InternalMetrics internalMetrics = new InternalMetrics();
|
||||
private final Timer internalMetricsTimer = new Timer(true);
|
||||
private final int scannerThreads;
|
||||
private final BatchWriterConfig bwConfig;
|
||||
private final List<BatchWriter> writers = new ArrayList<>();
|
||||
private final ThreadLocal<BatchWriter> metaWriter = new ThreadLocal<>();
|
||||
private final ThreadLocal<BatchWriter> batchWriter = new ThreadLocal<>();
|
||||
private boolean anonAccessAllowed = false;
|
||||
|
||||
public DataStoreImpl(Configuration conf, int numWriteThreads) throws TimelyException {
|
||||
|
||||
try {
|
||||
final BaseConfiguration apacheConf = new BaseConfiguration();
|
||||
apacheConf.setProperty("instance.name", conf.get(Configuration.INSTANCE_NAME));
|
||||
apacheConf.setProperty("instance.zookeeper.host", conf.get(Configuration.ZOOKEEPERS));
|
||||
final ClientConfiguration aconf = new ClientConfiguration(Collections.singletonList(apacheConf));
|
||||
final Instance instance = new ZooKeeperInstance(aconf);
|
||||
final byte[] passwd = conf.get(Configuration.PASSWORD).getBytes(UTF_8);
|
||||
connector = instance.getConnector(conf.get(Configuration.USERNAME), new PasswordToken(passwd));
|
||||
bwConfig = new BatchWriterConfig();
|
||||
bwConfig.setMaxLatency(getTimeInMillis(conf.get(Configuration.MAX_LATENCY)), TimeUnit.MILLISECONDS);
|
||||
bwConfig.setMaxMemory(getMemoryInBytes(conf.get(Configuration.WRITE_BUFFER_SIZE)) / numWriteThreads);
|
||||
bwConfig.setMaxWriteThreads(Integer.parseInt(conf.get(Configuration.WRITE_THREADS)));
|
||||
scannerThreads = Integer.parseInt(conf.get(Configuration.SCANNER_THREADS));
|
||||
anonAccessAllowed = conf.getBoolean(Configuration.ALLOW_ANONYMOUS_ACCESS);
|
||||
|
||||
String ageoff = Long.toString(Integer.parseInt(conf.get(Configuration.METRICS_AGEOFF_DAYS)) * 86400000L);
|
||||
Map<String, String> ageOffOptions = new HashMap<>();
|
||||
ageOffOptions.put("ttl", ageoff);
|
||||
IteratorSetting ageOffIteratorSettings = new IteratorSetting(100, "ageoff", AgeOffFilter.class,
|
||||
ageOffOptions);
|
||||
EnumSet<IteratorScope> ageOffIteratorScope = EnumSet.allOf(IteratorScope.class);
|
||||
|
||||
metricsTable = conf.get(Configuration.METRICS_TABLE);
|
||||
if (metricsTable.contains(".")) {
|
||||
final String[] parts = metricsTable.split("\\.", 2);
|
||||
final String namespace = parts[0];
|
||||
if (!connector.namespaceOperations().exists(namespace)) {
|
||||
try {
|
||||
LOG.info("Creating namespace " + namespace);
|
||||
connector.namespaceOperations().create(namespace);
|
||||
} catch (final NamespaceExistsException ex) {
|
||||
// don't care
|
||||
}
|
||||
}
|
||||
}
|
||||
final Map<String, String> tableIdMap = connector.tableOperations().tableIdMap();
|
||||
if (!tableIdMap.containsKey(metricsTable)) {
|
||||
try {
|
||||
LOG.info("Creating table " + metricsTable);
|
||||
connector.tableOperations().create(metricsTable);
|
||||
connector.tableOperations().attachIterator(metricsTable, ageOffIteratorSettings,
|
||||
ageOffIteratorScope);
|
||||
} catch (final TableExistsException ex) {
|
||||
// don't care
|
||||
}
|
||||
} else {
|
||||
for (IteratorScope scope : IteratorScope.values()) {
|
||||
if (connector.tableOperations().getIteratorSetting(metricsTable, "ageoff", scope) == null) {
|
||||
connector.tableOperations().attachIterator(metricsTable, ageOffIteratorSettings,
|
||||
EnumSet.of(scope));
|
||||
}
|
||||
}
|
||||
}
|
||||
metaTable = conf.get(Configuration.META_TABLE);
|
||||
if (!tableIdMap.containsKey(metaTable)) {
|
||||
try {
|
||||
LOG.info("Creating table " + metaTable);
|
||||
connector.tableOperations().create(metaTable);
|
||||
connector.tableOperations().attachIterator(metaTable, ageOffIteratorSettings, ageOffIteratorScope);
|
||||
} catch (final TableExistsException ex) {
|
||||
// don't care
|
||||
}
|
||||
} else {
|
||||
for (IteratorScope scope : IteratorScope.values()) {
|
||||
if (connector.tableOperations().getIteratorSetting(metaTable, "ageoff", scope) == null) {
|
||||
connector.tableOperations()
|
||||
.attachIterator(metaTable, ageOffIteratorSettings, EnumSet.of(scope));
|
||||
}
|
||||
}
|
||||
}
|
||||
internalMetricsTimer.schedule(new TimerTask() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
internalMetrics.getMetricsAndReset().forEach(m -> store(m));
|
||||
}
|
||||
|
||||
}, METRICS_PERIOD, METRICS_PERIOD);
|
||||
this.metaCache = MetaCacheFactory.getCache(conf);
|
||||
} catch (Exception e) {
|
||||
throw new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(), "Error creating DataStoreImpl",
|
||||
e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(Metric metric) {
|
||||
if (null == metaWriter.get()) {
|
||||
try {
|
||||
BatchWriter w = connector.createBatchWriter(metaTable, bwConfig);
|
||||
metaWriter.set(w);
|
||||
writers.add(w);
|
||||
} catch (TableNotFoundException e) {
|
||||
LOG.error("Error creating meta batch writer", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (null == batchWriter.get()) {
|
||||
try {
|
||||
BatchWriter w = connector.createBatchWriter(metricsTable, bwConfig);
|
||||
batchWriter.set(w);
|
||||
writers.add(w);
|
||||
} catch (TableNotFoundException e) {
|
||||
LOG.error("Error creating metric batch writer", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
internalMetrics.incrementMetricsReceived(1);
|
||||
List<Meta> toCache = new ArrayList<>(metric.getTags().size());
|
||||
for (final Tag tag : metric.getTags()) {
|
||||
Meta key = new Meta(metric.getMetric(), tag.getKey(), tag.getValue());
|
||||
if (!metaCache.contains(key)) {
|
||||
toCache.add(key);
|
||||
}
|
||||
}
|
||||
if (!toCache.isEmpty()) {
|
||||
final Set<Mutation> muts = new TreeSet<>(new Comparator<Mutation>() {
|
||||
|
||||
@Override
|
||||
public int compare(Mutation o1, Mutation o2) {
|
||||
if (o1.equals(o2)) {
|
||||
return 0;
|
||||
} else {
|
||||
if (o1.hashCode() < o2.hashCode()) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
MetaKeySet mks = new MetaKeySet();
|
||||
toCache.forEach(m -> mks.addAll(m.toKeys()));
|
||||
internalMetrics.incrementMetaKeysInserted(mks.size());
|
||||
muts.addAll(mks.toMutations());
|
||||
try {
|
||||
metaWriter.get().addMutations(muts);
|
||||
} catch (MutationsRejectedException e) {
|
||||
LOG.error("Unable to write to meta table", e);
|
||||
try {
|
||||
try {
|
||||
final BatchWriter w = metaWriter.get();
|
||||
metaWriter.remove();
|
||||
writers.remove(w);
|
||||
w.close();
|
||||
} catch (MutationsRejectedException e1) {
|
||||
LOG.error("Error closing meta writer", e1);
|
||||
}
|
||||
final BatchWriter w = connector.createBatchWriter(metaTable, bwConfig);
|
||||
metaWriter.set(w);
|
||||
writers.add(w);
|
||||
} catch (TableNotFoundException e1) {
|
||||
Server.fatal("Unexpected error recreating meta batch writer, shutting down Timely server", e1);
|
||||
}
|
||||
}
|
||||
metaCache.addAll(toCache);
|
||||
}
|
||||
try {
|
||||
batchWriter.get().addMutation(metric.toMutation());
|
||||
internalMetrics.incrementMetricKeysInserted(metric.getTags().size());
|
||||
} catch (MutationsRejectedException e) {
|
||||
LOG.error("Unable to write to metrics table", e);
|
||||
try {
|
||||
try {
|
||||
final BatchWriter w = batchWriter.get();
|
||||
batchWriter.remove();
|
||||
writers.remove(w);
|
||||
w.close();
|
||||
} catch (MutationsRejectedException e1) {
|
||||
LOG.error("Error closing metric writer", e1);
|
||||
}
|
||||
final BatchWriter w = connector.createBatchWriter(metricsTable, bwConfig);
|
||||
batchWriter.set(w);
|
||||
writers.add(w);
|
||||
} catch (TableNotFoundException e1) {
|
||||
Server.fatal("Unexpected error recreating metrics batch writer, shutting down Timely server", e1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateMetricCounts() {
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - lastCountTime.get() > 5 * 60 * 1000) {
|
||||
this.lastCountTime.set(now);
|
||||
SortedMap<MetricTagK, Integer> update = new TreeMap<>();
|
||||
for (Meta meta : this.metaCache) {
|
||||
MetricTagK key = new MetricTagK(meta.getMetric(), meta.getTagKey());
|
||||
Integer count = update.getOrDefault(key, Integer.valueOf(0));
|
||||
update.put(key, Integer.valueOf(count.intValue() + 1));
|
||||
}
|
||||
this.metaCounts.set(update);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuggestResponse suggest(SuggestRequest request) throws TimelyException {
|
||||
SuggestResponse result = new SuggestResponse();
|
||||
try {
|
||||
if (request.getType().equals("metrics")) {
|
||||
Range range;
|
||||
if (request.getQuery().isPresent()) {
|
||||
Text start = new Text(Meta.METRIC_PREFIX + request.getQuery().get());
|
||||
Text endRow = new Text(start);
|
||||
endRow.append(new byte[] { (byte) 0xff }, 0, 1);
|
||||
range = new Range(start, endRow);
|
||||
} else {
|
||||
// kind of a hack, maybe someone wants a metric with >100
|
||||
// 0xff bytes?
|
||||
Text start = new Text(Meta.METRIC_PREFIX);
|
||||
byte last = (byte) 0xff;
|
||||
byte[] lastBytes = new byte[100];
|
||||
Arrays.fill(lastBytes, last);
|
||||
Text end = new Text(Meta.METRIC_PREFIX);
|
||||
end.append(lastBytes, 0, lastBytes.length);
|
||||
range = new Range(start, end);
|
||||
}
|
||||
Scanner scanner = connector.createScanner(metaTable, Authorizations.EMPTY);
|
||||
scanner.setRange(range);
|
||||
List<String> metrics = new ArrayList<>();
|
||||
for (Entry<Key, Value> metric : scanner) {
|
||||
metrics.add(metric.getKey().getRow().toString().substring(Meta.METRIC_PREFIX.length()));
|
||||
if (metrics.size() >= request.getMax()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
result.setSuggestions(metrics);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOG.error("Error during suggest: " + ex.getMessage(), ex);
|
||||
throw new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(), "Error during suggest: "
|
||||
+ ex.getMessage(), ex.getMessage(), ex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
internalMetricsTimer.cancel();
|
||||
writers.forEach(w -> {
|
||||
try {
|
||||
w.close();
|
||||
} catch (final Exception ex) {
|
||||
LOG.warn("Error shutting down batchwriter", ex);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchLookupResponse lookup(SearchLookupRequest msg) throws TimelyException {
|
||||
long startMillis = System.currentTimeMillis();
|
||||
SearchLookupResponse result = new SearchLookupResponse();
|
||||
result.setType("LOOKUP");
|
||||
result.setMetric(msg.getQuery());
|
||||
Map<String, String> tags = new TreeMap<>();
|
||||
for (Tag tag : msg.getTags()) {
|
||||
tags.put(tag.getKey(), tag.getValue());
|
||||
}
|
||||
result.setTags(tags);
|
||||
result.setLimit(msg.getLimit());
|
||||
try {
|
||||
List<Result> resultField = new ArrayList<>();
|
||||
Scanner scanner = connector.createScanner(metaTable, Authorizations.EMPTY);
|
||||
Key start = new Key(Meta.VALUE_PREFIX + msg.getQuery());
|
||||
Key end = start.followingKey(PartialKey.ROW);
|
||||
Range range = new Range(start, end);
|
||||
scanner.setRange(range);
|
||||
// TODO compute columns to fetch
|
||||
int total = 0;
|
||||
for (Entry<Key, Value> entry : scanner) {
|
||||
Meta metaEntry = Meta.parse(entry.getKey(), entry.getValue());
|
||||
if (matches(metaEntry.getTagKey(), metaEntry.getTagValue(), tags)) {
|
||||
if (resultField.size() < msg.getLimit()) {
|
||||
Result r = new Result();
|
||||
r.putTag(metaEntry.getTagKey(), metaEntry.getTagValue());
|
||||
resultField.add(r);
|
||||
}
|
||||
total++;
|
||||
}
|
||||
}
|
||||
result.setResults(resultField);
|
||||
result.setTotalResults(total);
|
||||
result.setTime((int) (System.currentTimeMillis() - startMillis));
|
||||
} catch (Exception ex) {
|
||||
LOG.error("Error during lookup: " + ex.getMessage(), ex);
|
||||
throw new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(), "Error during lookup: "
|
||||
+ ex.getMessage(), ex.getMessage(), ex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean matches(String tagk, String tagv, Map<String, String> tags) {
|
||||
// first, match keys...
|
||||
for (Entry<String, String> entry : tags.entrySet()) {
|
||||
String k = entry.getKey();
|
||||
String v = entry.getValue();
|
||||
if (("*".equals(k) || tagk.equals(k)) && ("*".equals(v) || tagv.equals(v))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<QueryResponse> query(QueryRequest msg) throws TimelyException {
|
||||
List<QueryResponse> result = new ArrayList<>();
|
||||
long startTs = msg.getStart();
|
||||
long endTs = msg.getEnd();
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
for (SubQuery query : msg.getQueries()) {
|
||||
Map<Set<Tag>, List<Downsample>> allSeries = new HashMap<>();
|
||||
String metric = query.getMetric();
|
||||
BatchScanner scanner = connector.createBatchScanner(metricsTable, getSessionAuthorizations(msg),
|
||||
scannerThreads);
|
||||
try {
|
||||
setQueryRange(scanner, metric, startTs, endTs);
|
||||
setQueryColumns(scanner, metric, query.getTags());
|
||||
long downsample = getDownsamplePeriod(query);
|
||||
LOG.trace("Downsample period {}", downsample);
|
||||
Class<? extends Aggregator> aggClass = getAggregator(query);
|
||||
LOG.trace("Aggregator type {}", aggClass.getSimpleName());
|
||||
IteratorSetting is = new IteratorSetting(500, DownsampleIterator.class);
|
||||
DownsampleIterator.setDownsampleOptions(is, startTs, endTs, downsample, prioritizeTags(query),
|
||||
aggClass.getName());
|
||||
scanner.addScanIterator(is);
|
||||
// tag -> array of results by period starting at start
|
||||
for (Entry<Key, Value> encoded : scanner) {
|
||||
Map<Set<Tag>, Downsample> samples = DownsampleIterator.decodeValue(encoded.getValue());
|
||||
for (Entry<Set<Tag>, Downsample> entry : samples.entrySet()) {
|
||||
Set<Tag> key = new HashSet<>();
|
||||
for (Tag tag : entry.getKey()) {
|
||||
if (query.getTags().keySet().contains(tag.getKey())) {
|
||||
key.add(tag);
|
||||
}
|
||||
}
|
||||
List<Downsample> downsamples = allSeries.getOrDefault(key, new ArrayList<>());
|
||||
downsamples.add(entry.getValue());
|
||||
allSeries.put(key, downsamples);
|
||||
}
|
||||
}
|
||||
LOG.trace("allSeries: {}", allSeries);
|
||||
} finally {
|
||||
scanner.close();
|
||||
}
|
||||
|
||||
// TODO groupby here?
|
||||
long tsDivisor = msg.isMsResolution() ? 1 : 1000;
|
||||
for (Entry<Set<Tag>, List<Downsample>> entry : allSeries.entrySet()) {
|
||||
result.add(convertToQueryResponse(query, entry.getKey(), entry.getValue(), tsDivisor));
|
||||
}
|
||||
}
|
||||
LOG.debug("Query time: {}", (System.currentTimeMillis() - now));
|
||||
return result;
|
||||
} catch (ClassNotFoundException | IOException | TableNotFoundException ex) {
|
||||
LOG.error("Error during query: " + ex.getMessage(), ex);
|
||||
throw new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(), "Error during query: "
|
||||
+ ex.getMessage(), ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> prioritizeTags(SubQuery query) {
|
||||
// trivial cases
|
||||
Map<String, String> tags = query.getTags();
|
||||
if (tags.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (tags.size() == 1) {
|
||||
return Collections.singletonList(tags.keySet().iterator().next());
|
||||
}
|
||||
// favor tags with fewer values
|
||||
Map<String, Integer> priority = new HashMap<>();
|
||||
String metric = query.getMetric();
|
||||
// Count matching tags
|
||||
updateMetricCounts();
|
||||
for (Entry<String, String> entry : tags.entrySet()) {
|
||||
String tagk = entry.getKey();
|
||||
String tagv = entry.getValue();
|
||||
int wildcard = tagv.lastIndexOf('*');
|
||||
if (wildcard < 0) {
|
||||
// no wildcard: can only match one tag
|
||||
priority.put(tagk, 1);
|
||||
continue;
|
||||
}
|
||||
String prefix = tagv.substring(0, wildcard);
|
||||
int count = 0;
|
||||
MetricTagK start = new MetricTagK(metric, prefix);
|
||||
for (Entry<MetricTagK, Integer> metricCount : metaCounts.get().tailMap(start).entrySet()) {
|
||||
Pair<String, String> metricTagk = metricCount.getKey();
|
||||
if (!metricTagk.getFirst().equals(metric) || !metricTagk.getSecond().startsWith(prefix)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
priority.put(tagk, count);
|
||||
}
|
||||
List<String> result = new ArrayList<>(tags.keySet());
|
||||
Collections.sort(result, new Comparator<String>() {
|
||||
|
||||
@Override
|
||||
public int compare(String o1, String o2) {
|
||||
// greater count lowers priority
|
||||
return priority.get(o1).intValue() - priority.get(o2).intValue();
|
||||
}
|
||||
});
|
||||
LOG.trace("Tag priority {}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private QueryResponse convertToQueryResponse(SubQuery query, Set<Tag> tags, Collection<Downsample> values,
|
||||
long tsDivisor) {
|
||||
QueryResponse response = new QueryResponse();
|
||||
response.setMetric(query.getMetric());
|
||||
for (Tag tag : tags) {
|
||||
response.putTag(tag.getKey(), tag.getValue());
|
||||
}
|
||||
RateOption rateOptions = query.getRateOptions();
|
||||
Downsample combined = Downsample.combine(values, rateOptions);
|
||||
for (Sample entry : combined) {
|
||||
long ts = entry.timestamp / tsDivisor;
|
||||
response.putDps(Long.toString(ts), entry.value);
|
||||
}
|
||||
LOG.trace("Created query response {}", response);
|
||||
return response;
|
||||
}
|
||||
|
||||
private void setQueryColumns(BatchScanner scanner, String metric, Map<String, String> tags)
|
||||
throws TableNotFoundException, TimelyException {
|
||||
LOG.trace("Looking for requested tags: {}", tags);
|
||||
Scanner meta = connector.createScanner(metaTable, Authorizations.EMPTY);
|
||||
Text start = new Text(Meta.VALUE_PREFIX + metric);
|
||||
Text end = new Text(Meta.VALUE_PREFIX + metric + "\\x0000");
|
||||
end.append(new byte[] { (byte) 0xff }, 0, 1);
|
||||
meta.setRange(new Range(start, end));
|
||||
// Only look for the meta entries that match our tags, if any
|
||||
boolean onlyFirstRow = false;
|
||||
Entry<String, String> first = null;
|
||||
Iterator<Entry<String, String>> tagIter = tags.entrySet().iterator();
|
||||
if (tagIter.hasNext()) {
|
||||
first = tagIter.next();
|
||||
if (first.getValue().endsWith("*")) {
|
||||
meta.fetchColumnFamily(new Text(first.getKey()));
|
||||
} else {
|
||||
meta.fetchColumn(new Text(first.getKey()), new Text(first.getValue()));
|
||||
}
|
||||
} else {
|
||||
// only grab the first meta entry for this metric
|
||||
onlyFirstRow = true;
|
||||
}
|
||||
final boolean ONLY_RETURN_FIRST_ROW = onlyFirstRow;
|
||||
Iterator<Entry<Key, Value>> iter = meta.iterator();
|
||||
Iterator<Pair<String, String>> knownKeyValues = new Iterator<Pair<String, String>>() {
|
||||
|
||||
boolean returnedFirstRecord = false;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
if (ONLY_RETURN_FIRST_ROW) {
|
||||
if (this.returnedFirstRecord) {
|
||||
return false;
|
||||
} else {
|
||||
return iter.hasNext();
|
||||
}
|
||||
} else {
|
||||
return iter.hasNext();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<String, String> next() {
|
||||
Entry<Key, Value> entry = iter.next();
|
||||
this.returnedFirstRecord = true;
|
||||
LOG.trace("Returning tag {}={}", entry.getKey().getColumnFamily().toString(), entry.getKey()
|
||||
.getColumnQualifier().toString());
|
||||
return new Pair<>(entry.getKey().getColumnFamily().toString(), entry.getKey().getColumnQualifier()
|
||||
.toString());
|
||||
}
|
||||
};
|
||||
Set<Tag> concrete = expandTagValues(first, knownKeyValues);
|
||||
if (concrete.size() == 0) {
|
||||
throw new TimelyException(HttpResponseStatus.BAD_REQUEST.code(), "No matching tags", "No tags were found "
|
||||
+ " that matched the submitted tags. Please fix and retry");
|
||||
}
|
||||
LOG.trace("Found matching tags: {}", concrete);
|
||||
for (Tag tag : concrete) {
|
||||
Text colf = new Text(tag.getKey() + "=" + tag.getValue());
|
||||
scanner.fetchColumnFamily(colf);
|
||||
LOG.trace("Fetching column family: {}", colf);
|
||||
}
|
||||
// Add the regular expression to filter the other tags
|
||||
int priority = 100;
|
||||
while (tagIter.hasNext()) {
|
||||
Entry<String, String> tag = tagIter.next();
|
||||
LOG.trace("Additional tag {}", tag);
|
||||
StringBuffer pattern = new StringBuffer();
|
||||
pattern.append("(^|,)").append(Pattern.quote(tag.getKey())).append("=");
|
||||
String value = tag.getValue();
|
||||
if (tag.getValue().endsWith("*")) {
|
||||
value = value.substring(0, value.length() - 1);
|
||||
pattern.append(Pattern.quote(value));
|
||||
pattern.append("[^,]*");
|
||||
} else {
|
||||
pattern.append(Pattern.quote(value));
|
||||
pattern.append("(,|$)");
|
||||
}
|
||||
IteratorSetting setting = new IteratorSetting(priority++, tag.getKey() + " tag filter", RegExFilter.class);
|
||||
LOG.trace("Using {} additional filter on tags", pattern);
|
||||
RegExFilter.setRegexs(setting, null, null, pattern.toString(), null, false, true);
|
||||
scanner.addScanIterator(setting);
|
||||
}
|
||||
}
|
||||
|
||||
private Set<Tag> expandTagValues(Entry<String, String> firstTag, Iterator<Pair<String, String>> knownKeyValues) {
|
||||
Set<Tag> result = new HashSet<>();
|
||||
while (knownKeyValues.hasNext()) {
|
||||
Pair<String, String> knownKeyValue = knownKeyValues.next();
|
||||
if (firstTag == null) {
|
||||
LOG.trace("Adding tag {}={}", knownKeyValue.getFirst(), knownKeyValue.getSecond());
|
||||
result.add(new Tag(knownKeyValue.getFirst(), knownKeyValue.getSecond()));
|
||||
} else {
|
||||
LOG.trace("Testing requested tag {}={}", firstTag.getKey(), firstTag.getValue());
|
||||
if (firstTag.getKey().equals(knownKeyValue.getFirst())) {
|
||||
if (firstTag.getValue().endsWith("*")) {
|
||||
String prefix = firstTag.getValue().substring(0, firstTag.getValue().length() - 1);
|
||||
if (knownKeyValue.getSecond().startsWith(prefix)) {
|
||||
LOG.trace("Adding tag {}={}", knownKeyValue.getFirst(), knownKeyValue.getSecond());
|
||||
result.add(new Tag(knownKeyValue.getFirst(), knownKeyValue.getSecond()));
|
||||
}
|
||||
} else {
|
||||
LOG.trace("Adding tag {}={}", knownKeyValue.getFirst(), knownKeyValue.getSecond());
|
||||
result.add(new Tag(knownKeyValue.getFirst(), knownKeyValue.getSecond()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void setQueryRange(BatchScanner scanner, String metric, long start, long end) {
|
||||
final byte[] start_row = Metric.encodeRowKey(metric, start);
|
||||
LOG.trace("Start key for metric {} and time {} is {}", metric, start, start_row);
|
||||
final byte[] end_row = Metric.encodeRowKey(metric, end);
|
||||
LOG.trace("End key for metric {} and time {} is {}", metric, end, end_row);
|
||||
Range range = new Range(new Text(start_row), new Text(end_row));
|
||||
LOG.trace("Set query range to {}", range);
|
||||
scanner.setRanges(Collections.singletonList(range));
|
||||
}
|
||||
|
||||
private Class<? extends Aggregator> getAggregator(SubQuery query) {
|
||||
String aggregatorName = "avg";
|
||||
if (query.getDownsample().isPresent()) {
|
||||
String parts[] = query.getDownsample().get().split("-");
|
||||
aggregatorName = parts[1];
|
||||
}
|
||||
return Aggregator.getAggregator(aggregatorName);
|
||||
}
|
||||
|
||||
private long getDownsamplePeriod(SubQuery query) {
|
||||
if (!query.getDownsample().isPresent()) {
|
||||
return DEFAULT_DOWNSAMPLE_MS;
|
||||
}
|
||||
String parts[] = query.getDownsample().get().split("-");
|
||||
return getTimeInMillis(parts[0]);
|
||||
}
|
||||
|
||||
private Authorizations getSessionAuthorizations(AuthenticatedRequest request) {
|
||||
String sessionId = request.getSessionId();
|
||||
if (!StringUtils.isEmpty(sessionId)) {
|
||||
return AuthCache.getAuthorizations(sessionId);
|
||||
} else if (!anonAccessAllowed) {
|
||||
throw new RuntimeException("Anonymous user attempting to query");
|
||||
} else {
|
||||
return Authorizations.EMPTY;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package timely.store;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import timely.api.model.Metric;
|
||||
import timely.api.model.Tag;
|
||||
|
||||
import com.google.common.util.concurrent.AtomicDouble;
|
||||
|
||||
public class InternalMetrics {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(InternalMetrics.class);
|
||||
|
||||
private static final String METRICS_RECEIVED_METRIC = "timely.metrics.received";
|
||||
private static final String META_KEYS_METRIC = "timely.keys.meta.inserted";
|
||||
private static final String METRIC_KEYS_METRIC = "timely.keys.metric.inserted";
|
||||
private static final String HOSTNAME_TAG = "host";
|
||||
|
||||
private String hostName = "localhost";
|
||||
private AtomicDouble numMetricsReceived = new AtomicDouble(0);
|
||||
private AtomicDouble numMetaKeysInserted = new AtomicDouble(0);
|
||||
private AtomicDouble numMetricKeysInserted = new AtomicDouble(0);
|
||||
private List<Tag> tags = new ArrayList<Tag>();
|
||||
|
||||
public InternalMetrics() {
|
||||
super();
|
||||
try {
|
||||
hostName = InetAddress.getLocalHost().getHostName();
|
||||
} catch (UnknownHostException e) {
|
||||
LOG.error("Error getting hostname", e);
|
||||
}
|
||||
tags.add(new Tag(HOSTNAME_TAG, hostName));
|
||||
}
|
||||
|
||||
public void incrementMetricsReceived(long num) {
|
||||
numMetricsReceived.addAndGet(num);
|
||||
}
|
||||
|
||||
public void incrementMetaKeysInserted(long num) {
|
||||
numMetaKeysInserted.addAndGet(num);
|
||||
}
|
||||
|
||||
public void incrementMetricKeysInserted(long num) {
|
||||
numMetricKeysInserted.addAndGet(num);
|
||||
}
|
||||
|
||||
public List<Metric> getMetricsAndReset() {
|
||||
List<Metric> metrics = new ArrayList<Metric>();
|
||||
long ts = System.currentTimeMillis();
|
||||
metrics.add(new Metric(METRICS_RECEIVED_METRIC, ts, numMetricsReceived.get(), tags));
|
||||
numMetricsReceived.set(0);
|
||||
metrics.add(new Metric(META_KEYS_METRIC, ts, numMetaKeysInserted.get(), tags));
|
||||
numMetaKeysInserted.set(0);
|
||||
metrics.add(new Metric(METRIC_KEYS_METRIC, ts, numMetricKeysInserted.get(), tags));
|
||||
numMetricKeysInserted.set(0);
|
||||
return metrics;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package timely.store;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
import timely.Configuration;
|
||||
import timely.api.model.Meta;
|
||||
|
||||
public interface MetaCache extends Iterable<Meta> {
|
||||
|
||||
void init(Configuration config);
|
||||
|
||||
void add(Meta meta);
|
||||
|
||||
boolean contains(Meta meta);
|
||||
|
||||
void addAll(Collection<Meta> c);
|
||||
|
||||
Iterator<Meta> iterator();
|
||||
|
||||
void close();
|
||||
|
||||
boolean isClosed();
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package timely.store;
|
||||
|
||||
import timely.Configuration;
|
||||
|
||||
public class MetaCacheFactory {
|
||||
|
||||
private static MetaCache cache = null;
|
||||
|
||||
public static MetaCache getCache() {
|
||||
if (null != cache) {
|
||||
return cache;
|
||||
} else {
|
||||
throw new RuntimeException("MetaCache not initialized.");
|
||||
}
|
||||
}
|
||||
|
||||
public static final synchronized MetaCache getCache(Configuration conf) {
|
||||
if (null == cache || cache.isClosed()) {
|
||||
if (null == conf) {
|
||||
throw new RuntimeException("Configuration cannot be null");
|
||||
}
|
||||
cache = new MetaCacheImpl();
|
||||
cache.init(conf);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
public static final synchronized void close() {
|
||||
if (null != cache) {
|
||||
cache.close();
|
||||
}
|
||||
cache = null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package timely.store;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import timely.Configuration;
|
||||
import timely.api.model.Meta;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
|
||||
public class MetaCacheImpl implements MetaCache {
|
||||
|
||||
private static final Object DUMMY = new Object();
|
||||
private volatile boolean closed = false;
|
||||
private Cache<Meta, Object> cache = null;
|
||||
|
||||
@Override
|
||||
public void init(Configuration config) {
|
||||
long defaultExpiration = Long.parseLong(config.get(Configuration.META_CACHE_EXPIRATION));
|
||||
int initialCapacity = Integer.parseInt(config.get(Configuration.META_CACHE_INITIAL_CAPACITY));
|
||||
long maxCapacity = Long.parseLong(config.get(Configuration.META_CACHE_MAX_CAPACITY));
|
||||
cache = Caffeine.newBuilder().expireAfterAccess(defaultExpiration, TimeUnit.MINUTES)
|
||||
.initialCapacity(initialCapacity).maximumSize(maxCapacity).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(Meta meta) {
|
||||
cache.put(meta, DUMMY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Meta meta) {
|
||||
return cache.asMap().containsKey(meta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAll(Collection<Meta> c) {
|
||||
c.forEach(m -> cache.put(m, DUMMY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Meta> iterator() {
|
||||
return cache.asMap().keySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.closed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
package timely.util;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import timely.netty.tcp.TcpPutDecoder;
|
||||
|
||||
public class InsertTestData {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(InsertTestData.class);
|
||||
|
||||
private static final List<String> METRICS = new ArrayList<>();
|
||||
static {
|
||||
METRICS.add("sys.cpu0.user");
|
||||
METRICS.add("sys.cpu0.idle");
|
||||
METRICS.add("sys.cpu0.system");
|
||||
METRICS.add("sys.cpu0.wait");
|
||||
METRICS.add("sys.memory.free");
|
||||
METRICS.add("sys.memory.used");
|
||||
METRICS.add("sys.eth0.rx-errors");
|
||||
METRICS.add("sys.eth0.rx-dropped");
|
||||
METRICS.add("sys.eth0.rx-packets");
|
||||
METRICS.add("sys.eth0.tx-errors");
|
||||
METRICS.add("sys.eth0.tx-dropped");
|
||||
METRICS.add("sys.eth0.tx-packets");
|
||||
METRICS.add("sys.swap.free");
|
||||
METRICS.add("sys.swap.used");
|
||||
}
|
||||
|
||||
private static final List<String> HOSTS = new ArrayList<>();
|
||||
|
||||
static {
|
||||
HOSTS.add("n01");
|
||||
HOSTS.add("n02");
|
||||
}
|
||||
|
||||
private static final List<String> RACKS = new ArrayList<>();
|
||||
|
||||
static {
|
||||
RACKS.add("r01");
|
||||
RACKS.add("r02");
|
||||
}
|
||||
|
||||
private static final Map<String, String> VISIBILITIES = new HashMap<>();
|
||||
static {
|
||||
VISIBILITIES.put("sys.eth0.rx-errors", "A");
|
||||
VISIBILITIES.put("sys.eth0.rx-dropped", "B");
|
||||
VISIBILITIES.put("sys.eth0.rx-packets", "C");
|
||||
VISIBILITIES.put("sys.eth0.tx-errors", "D");
|
||||
VISIBILITIES.put("sys.eth0.tx-dropped", "E");
|
||||
VISIBILITIES.put("sys.eth0.tx-packets", "F");
|
||||
}
|
||||
|
||||
private static final String FMT = "put {0} {1,number,#} {2,number} host={4}{3} rack={4}";
|
||||
|
||||
private static String usage() {
|
||||
return "InsertTestData <host> <port> [--fast]";
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length < 2) {
|
||||
System.err.println(usage());
|
||||
System.exit(1);
|
||||
}
|
||||
String hostname = args[0];
|
||||
String port = args[1];
|
||||
boolean goFast = false;
|
||||
if (args.length == 3) {
|
||||
if (args[2].equalsIgnoreCase("--fast")) {
|
||||
goFast = true;
|
||||
}
|
||||
}
|
||||
|
||||
try (Socket sock = new Socket(hostname, Integer.parseInt(port));
|
||||
PrintStream writer = new PrintStream(sock.getOutputStream(), true, StandardCharsets.UTF_8.name());) {
|
||||
while (true) {
|
||||
long time = System.currentTimeMillis();
|
||||
METRICS.forEach(m -> {
|
||||
final String viz = VISIBILITIES.get(m);
|
||||
RACKS.forEach(rack -> {
|
||||
HOSTS.forEach(host -> {
|
||||
String put = MessageFormat.format(FMT, m, time,
|
||||
ThreadLocalRandom.current().nextDouble(0.0D, 100.0D), host, rack);
|
||||
if (viz != null)
|
||||
put += " viz=" + viz;
|
||||
writer.println(put);
|
||||
LOG.info(put);
|
||||
});
|
||||
});
|
||||
});
|
||||
writer.flush();
|
||||
if (!goFast) {
|
||||
Thread.sleep(10 * 1000);
|
||||
} else {
|
||||
Thread.sleep(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package timely.util;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
||||
|
||||
public class JsonUtil {
|
||||
|
||||
private static ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
static {
|
||||
mapper.registerModule(new Jdk8Module());
|
||||
mapper.configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true);
|
||||
mapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
|
||||
// mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
|
||||
mapper.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true);
|
||||
}
|
||||
|
||||
public static ObjectMapper getObjectMapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
}
|
||||
Loaded 100 of 188 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user