├── .github
└── workflows
│ ├── codeql-analysis.yml
│ └── test.yml
├── .gitignore
├── .travis.yml
├── CHANGES.markdown
├── LICENSE
├── README.markdown
├── SECURITY.md
├── metrics2-riemann-reporter
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── yammer
│ │ └── metrics
│ │ └── reporting
│ │ └── RiemannReporter.java
│ └── test
│ └── java
│ └── riemann
│ └── java
│ └── client
│ └── tests
│ └── RiemannReporterConfigTest.java
├── metrics3-riemann-reporter
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── codahale
│ │ └── metrics
│ │ └── riemann
│ │ ├── Riemann.java
│ │ ├── RiemannReporter.java
│ │ ├── ValueFilter.java
│ │ └── ValueFilterMap.java
│ └── test
│ └── java
│ └── com
│ └── codahale
│ └── metrics
│ └── riemann
│ ├── RiemannReporterTest.java
│ ├── RiemannTest.java
│ └── ValueFilterTest.java
├── metrics4-riemann-reporter
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── codahale
│ │ └── metrics
│ │ └── riemann
│ │ ├── Riemann.java
│ │ ├── RiemannReporter.java
│ │ ├── ValueFilter.java
│ │ └── ValueFilterMap.java
│ └── test
│ └── java
│ └── com
│ └── codahale
│ └── metrics
│ └── riemann
│ ├── RiemannReporterTest.java
│ ├── RiemannTest.java
│ └── ValueFilterTest.java
├── pom.xml
├── riemann-java-client
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ ├── clojure
│ │ │ └── lang
│ │ │ │ ├── IBlockingDeref.java
│ │ │ │ └── IDeref.java
│ │ └── io
│ │ │ └── riemann
│ │ │ └── riemann
│ │ │ └── client
│ │ │ ├── AsynchronizeTransport.java
│ │ │ ├── AsynchronousTransport.java
│ │ │ ├── ChainPromise.java
│ │ │ ├── EventBuilder.java
│ │ │ ├── EventDSL.java
│ │ │ ├── ExceptionReporter.java
│ │ │ ├── Fn2.java
│ │ │ ├── IPromise.java
│ │ │ ├── IRiemannClient.java
│ │ │ ├── MapPromise.java
│ │ │ ├── MsgTooLargeException.java
│ │ │ ├── MsgValidator.java
│ │ │ ├── Null.java
│ │ │ ├── OverloadedException.java
│ │ │ ├── Promise.java
│ │ │ ├── ReconnectHandler.java
│ │ │ ├── RiemannBatchClient.java
│ │ │ ├── RiemannClient.java
│ │ │ ├── RiemannScheduler.java
│ │ │ ├── SSL.java
│ │ │ ├── ServerError.java
│ │ │ ├── SimpleUdpTransport.java
│ │ │ ├── SynchronousTransport.java
│ │ │ ├── TcpHandler.java
│ │ │ ├── TcpTransport.java
│ │ │ ├── Transport.java
│ │ │ ├── UdpTransport.java
│ │ │ ├── UnsupportedJVMException.java
│ │ │ ├── Write.java
│ │ │ └── WriteQueue.java
│ └── proto
│ │ └── riemann
│ │ └── proto.proto
│ └── test
│ └── java
│ └── riemann
│ └── java
│ └── client
│ └── tests
│ ├── BatchClientPromiseTest.java
│ ├── ChainPromiseTest.java
│ ├── EchoServer.java
│ ├── EventBuilderTest.java
│ ├── OkServer.java
│ ├── PromiseTest.java
│ ├── Server.java
│ ├── SimpleUdpClientTest.java
│ ├── TcpClientTest.java
│ └── Util.java
└── scripts
└── install-protobuf.sh
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ master ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ master ]
20 | schedule:
21 | - cron: '17 9 * * 0'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'java' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support
38 |
39 | steps:
40 | - name: Checkout repository
41 | uses: actions/checkout@v2
42 |
43 | # Initializes the CodeQL tools for scanning.
44 | - name: Initialize CodeQL
45 | uses: github/codeql-action/init@v1
46 | with:
47 | languages: ${{ matrix.language }}
48 | # If you wish to specify custom queries, you can do so here or in a config file.
49 | # By default, queries listed here will override any specified in a config file.
50 | # Prefix the list here with "+" to use these queries and those in the config file.
51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
52 |
53 | # ℹ️ Command-line programs to run using the OS shell.
54 | # 📚 ht
55 |
56 | - name: Installed protobuf
57 | run: |
58 | ./scripts/install-protobuf.sh
59 |
60 | - name: Add protoc to path
61 | run: |
62 | echo "$HOME/bin" >> $GITHUB_PATH
63 |
64 | - name: install deps
65 | run: |
66 | mvn -DskipTests clean install dependency:resolve-plugins dependency:go-offline
67 |
68 | - name: Perform CodeQL Analysis
69 | uses: github/codeql-action/analyze@v1
70 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 |
2 | name: Riemann Clojure Client testing
3 |
4 | on:
5 | push:
6 | workflow_dispatch:
7 |
8 | jobs:
9 | test:
10 |
11 | strategy:
12 | matrix:
13 | java: [ '8', '11', '13', '15', '16', '17' ]
14 | fail-fast: false
15 | name: Java ${{ matrix.Java }}
16 |
17 | runs-on: ubuntu-latest
18 |
19 | env:
20 | JVM_OPTS: -Xmx3200m
21 |
22 | steps:
23 | - uses: actions/checkout@v3
24 | - name: Setup Java
25 | uses: actions/setup-java@v3
26 | with:
27 | distribution: 'zulu'
28 | java-version: ${{ matrix.java }}
29 | - name: Install protobuf
30 | run: |
31 | cd /tmp
32 | wget https://github.com/google/protobuf/releases/download/v3.16.1/protoc-3.16.1-linux-x86_64.zip
33 | unzip protoc-3.16.1-linux-x86_64.zip
34 | mv /tmp/bin/protoc /usr/local/bin/
35 | protoc --version
36 | - name: Install deps
37 | run: mvn -DskipTests clean install dependency:resolve-plugins dependency:go-offline
38 | - name: Cache dependencies
39 | uses: actions/cache@v3
40 | with:
41 | path: ~/.m2/repository
42 | key: ${{ matrix.java }}-maven-${{ hashFiles('**/pom.xml') }}
43 | - name: Run tests
44 | run: mvn package
45 | - name: Upload Test Results
46 | if: always()
47 | uses: actions/upload-artifact@v3
48 | with:
49 | name: Test Results (Java ${{ matrix.java }})
50 | path: '**/target/surefire-reports/'
51 |
52 | publish-test-results:
53 | name: "Publish Tests Results"
54 | needs: test
55 | runs-on: ubuntu-latest
56 | permissions:
57 | checks: write
58 | pull-requests: write
59 | if: always()
60 |
61 | steps:
62 | - name: Download Artifacts
63 | uses: actions/download-artifact@v4.1.7
64 | with:
65 | path: artifacts
66 |
67 | - name: Publish Test Results
68 | uses: EnricoMi/publish-unit-test-result-action@v1
69 | with:
70 | files: "artifacts/**/*.xml"
71 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .classpath
3 | .vscode
4 | .settings
5 | .project
6 | target
7 | bin
8 | repl-port
9 | *.iml
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 |
3 | language: java
4 |
5 | cache:
6 | directories:
7 | - $HOME/protobuf
8 | - $home/.m2
9 |
10 | before_install:
11 | - ./scripts/install-protobuf.sh
12 | - export PATH=$PATH:$HOME/bin
13 |
--------------------------------------------------------------------------------
/CHANGES.markdown:
--------------------------------------------------------------------------------
1 | 0.5.1
2 | =====
3 |
4 | * Upgraded dropwizard metrics to 4.0.2
5 |
6 | 0.5.0
7 | =====
8 |
9 | * Upgrade to Netty 4
10 |
11 | NOTES:
12 |
13 | - See [Netty 4](http://netty.io/wiki/new-and-noteworthy-in-4.0.html) upgrade notes
14 |
15 |
16 | - As [mentioned](http://netty.io/wiki/new-and-noteworthy-in-4.0.html#write-does-not-flush-automatically)
17 | Netty 4 won't flush sends automatically. By default this client will flush after each message,
18 | this can be controlled via the `autoFlush` field on the Transports.
19 |
20 | - `Resolver` class was removed. Netty will resolve on each new channel when the
21 | supplied address is created via `InetSocketAddress.createUnresolved`.
22 |
23 | - use of `HashedWheelTimer` has been replaced by the Executor provided by the
24 | `ChannelHandlerContext`
25 |
26 | - Previous versions used to explicitly disable SSL TLS renegotiation. This no longer appears to be part of Netty.
27 | [Stack Overflow](https://stackoverflow.com/questions/31418644/is-it-possible-to-disable-tls-renegotiation-in-netty-4)
28 | suggests setting the JDK8+ System Property `jdk.tls.rejectClientInitiatedRenegotiation`
29 |
30 | - Use individual Netty Jars rather than netty-all. Use of netty-all can hinder
31 | dependency resolution when different projects use Netty on the same classpath.
32 | See example from [Aleph](https://github.com/ztellman/aleph/issues/335)
33 |
34 | - Remove `ChannelGroupHandler` the Transport connect add the channel at each connection attempt.
35 | The Netty `Channels` class automatically removes channels as they close.
36 |
37 | * Enable metrics reports to include state values based on the values of
38 | the metrics. This adds support for computation and reporting of metric
39 | states. The implementation associates ranges of values with user-defined
40 | states, defaulting to `ok` if no filters have been set up or none of the
41 | associated filters applies.
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 | # Getting started
2 |
3 | This project encompasses:
4 |
5 | 1. A Java client for the Riemann protocol
6 | 2. The Riemann protocol buffer definition, and
7 | 3. Its corresponding (auto-generated) Java classes
8 |
9 | [](https://clojars.org/io.riemann/riemann-java-client)
11 |
12 | [](https://github.com/riemann/riemann-java-client/actions/workflows/test.yml)
13 |
14 | ## Artifacts
15 |
16 | Artifacts are available through
17 | [clojars](https://clojars.org/io.riemann/riemann-java-client) which you can add
18 | to your maven repository like so:
19 |
20 | ```xml
21 |
22 | clojars.org
23 | http://clojars.org/repo
24 |
25 | ```
26 |
27 | **Note:**
28 | The namespace for the client was previously `com.aphyr` but has been
29 | renamed to `io.riemann` since the 0.4.2 release. You need to update your
30 | dependencies.
31 |
32 | ## Example
33 |
34 | ``` java
35 | RiemannClient c = RiemannClient.tcp("my.riemann.server", 5555);
36 | c.connect();
37 | c.event().
38 | service("fridge").
39 | state("running").
40 | metric(5.3).
41 | tags("appliance", "cold").
42 | send().
43 | deref(5000, java.util.concurrent.TimeUnit.MILLISECONDS);
44 |
45 | c.query("tagged \"cold\" and metric > 0").deref(); // => List;
46 | c.close();
47 | ```
48 |
49 | Clients will automatically attempt to reconnect every 5 seconds. Writes
50 | will fail instantaneously when no connection is available.
51 |
52 | `.send()` proceeds asynchronously and returns as soon as Netty flushes
53 | the write possible. `.send()` returns a
54 | `io.riemann.riemann.client.IPromise` containing the response from the
55 | write (which also supports Clojure's Deref protocol). If you do not
56 | deref this promise, the client makes *no* guarantees about event
57 | delivery: it will, for example, discard writes when there are too many
58 | messages outstanding on the wire, when Riemann cannot keep up with load,
59 | and so on. You *should* deref sends at some point, if for no other
60 | reason than to handle backpressure.
61 |
62 | Calling `.deref()` will throw a ServerError if the server responds with
63 | an error, or other Runtime/IOExceptions for error conditions, like a
64 | channel being disconnected, etc.
65 |
66 | ```java
67 | try {
68 | if (!c.event().
69 | service("fridge").
70 | state("running").
71 | send().
72 | deref(1, java.util.concurrent.TimeUnit.SECONDS)) {
73 | throw new IOException("Timed out.")
74 | }
75 | } catch (Exception e) {
76 | retry();
77 | }
78 | ```
79 |
80 | This code blocks for 1 second before retrying, returns a Message if the
81 | write succeeded, null if the promise is still outstanding, and throws if
82 | a failure is known to have occurred. This means you can send multiple
83 | copies of an event if latencies exceed 1000 ms. There is no reliable way
84 | to distinguish between failure and delay in an asynchronous network, so
85 | think ahead. `.deref()` blocks indefinitely, but will return as soon as
86 | the Netty connection fails, so it may be the safest option when
87 | arbitrary delays are acceptable.
88 |
89 | Each client allows thousands of outstanding concurrent requests at any
90 | time, so a small number of threads can efficiently pipeline many
91 | operations over the same client. I suggest performing writes on a
92 | special monitoring thread or threads, and pushing the response futures
93 | onto a threadpoolexecutor for `deref`ing.
94 |
95 | For higher performance (by orders of magnitude) you can also send
96 | multiple events batched in a single message. Use
97 | `RiemannClient.sendEvents(...)` to send multiple events at once.
98 |
99 | To automatically batch events, wrap any IRiemannClient in a
100 | RiemannBatchClient, which automatically bundles events together into
101 | messages for you.
102 |
103 | # Release and deployment
104 |
105 | 1. Increment the version.
106 |
107 | 2. Compile the client.
108 |
109 | ```sh
110 | mvn compile -rf :riemann-java-client
111 | ```
112 |
113 | 3. Deploy the client.
114 |
115 | ```sh
116 | mvn deploy -tf :riemann-java-client-parent
117 | mvn deploy -rf :riemann-java-client
118 | ```
119 |
120 | # Hacking
121 |
122 | You'll need [protobuf 3.16.1](https://github.com/google/protobuf) - you
123 | can just install the `protoc` binary. After that, `mvn package` should
124 | build a JAR, and `mvn install` will drop it in your local repository.
125 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Riemann Security and Disclosure Information
2 | This page describes Riemann security and disclosure information.
3 |
4 | ## Supported Versions
5 |
6 | The currently supported version of Riemann for security-patching purposes is always the latest version.
7 |
8 | ## Security Announcements
9 |
10 | Will be made on the [Riemann mailing list](https://groups.google.com/g/riemann-users?pli=1).
11 |
12 | ## Report a Vulnerability
13 |
14 | We're extremely grateful for security researchers and users that report vulnerabilities to Riemann. All reports are thoroughly investigated by the maintainers.
15 |
16 | To make a report, you should email the private security@riemann.io list with the details.
17 |
18 | ## When Should I Report a Vulnerability?
19 |
20 | * You think you discovered a potential security vulnerability in Riemann.
21 | * You are unsure how a vulnerability affects Riemann.
22 | * You think you discovered a vulnerability in another project that Riemann depends on
23 |
24 | For projects with their own vulnerability reporting and disclosure process, please report it directly there.
25 |
26 | ## When Should I NOT Report a Vulnerability?
27 |
28 | * You need help tuning Riemann components for security
29 | * You need help applying security related updates
30 | * Your issue is not security related
31 |
32 | ## Security Vulnerability Response
33 |
34 | Each report is acknowledged and analyzed within 5 working days.
35 |
36 | Any vulnerability information shared stays within Riemann project and will not be disseminated to other projects unless it is necessary to get the issue fixed.
37 |
38 | As the security issue moves from triage, to identified fix, to release planning we will keep the reporter updated.
39 |
40 | ## Public Disclosure Timing
41 |
42 | A public disclosure date is negotiated by the Riemann maintainers nd the bug submitter. We prefer to fully disclose the bug as soon as possible once a user mitigation is available. It is reasonable to delay disclosure when the bug or the fix is not yet fully understood, the solution is not well-tested, or for vendor coordination. The timeframe for disclosure is from immediate (especially if it's already publicly known) to a few weeks. For a vulnerability with a straightforward mitigation, we expect report date to disclosure date to be on the order of 7 days. The Riemann maintainers hold the final say when setting a disclosure date.
43 |
--------------------------------------------------------------------------------
/metrics2-riemann-reporter/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | 4.0.0
7 | metrics2-riemann-reporter
8 |
9 | riemann-java-client-parent
10 | io.riemann
11 | 0.5.4-SNAPSHOT
12 |
13 |
14 |
15 |
16 |
17 | com.yammer.metrics
18 | metrics-core
19 | 2.1.2
20 |
21 |
22 | ${project.groupId}
23 | riemann-java-client
24 | ${project.version}
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/metrics2-riemann-reporter/src/main/java/com/yammer/metrics/reporting/RiemannReporter.java:
--------------------------------------------------------------------------------
1 | package com.yammer.metrics.reporting;
2 |
3 | import io.riemann.riemann.client.EventDSL;
4 | import io.riemann.riemann.client.IRiemannClient;
5 | import io.riemann.riemann.client.RiemannClient;
6 | import com.yammer.metrics.Metrics;
7 | import com.yammer.metrics.core.*;
8 | import com.yammer.metrics.stats.Snapshot;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | import java.io.IOException;
13 | import java.net.InetSocketAddress;
14 | import java.util.ArrayList;
15 | import java.util.Arrays;
16 | import java.util.Collection;
17 | import java.util.List;
18 | import java.util.Map.Entry;
19 | import java.util.SortedMap;
20 | import java.util.concurrent.TimeUnit;
21 |
22 | public class RiemannReporter extends AbstractPollingReporter implements MetricProcessor {
23 | private static final Logger LOG = LoggerFactory.getLogger(RiemannReporter.class);
24 | public final IRiemannClient riemann;
25 | public final Config c;
26 |
27 | public static class Config {
28 |
29 | public final MetricsRegistry metricsRegistry;
30 | public final MetricPredicate predicate;
31 | public final boolean printVMMetrics;
32 | public final String host;
33 | public final int port;
34 | public final long period;
35 | public final TimeUnit unit;
36 | public final String prefix;
37 | public final String separator;
38 | public final VirtualMachineMetrics vm = VirtualMachineMetrics.getInstance();
39 | public final Clock clock;
40 | public final String name;
41 | public final String localHost;
42 | public final List tags;
43 |
44 | private Config(MetricsRegistry metricsRegistry, MetricPredicate predicate, boolean printVMMetrics,
45 | String host, int port, long period, TimeUnit unit, String prefix, String separator,
46 | Clock clock, String name, String localHost, List tags) {
47 | this.metricsRegistry = metricsRegistry;
48 | this.predicate = predicate;
49 | this.printVMMetrics = printVMMetrics;
50 | this.host = host;
51 | this.port = port;
52 | this.period = period;
53 | this.unit = unit;
54 | this.prefix = prefix;
55 | this.separator = separator;
56 | this.clock = clock;
57 | this.name = name;
58 | this.localHost = localHost;
59 | this.tags = tags;
60 |
61 | }
62 |
63 | public static ConfigBuilder newBuilder() {
64 | return new ConfigBuilder();
65 | }
66 |
67 | @Override
68 | public String toString() {
69 | return "Config{" +
70 | "print_metrics:" + printVMMetrics + ", host:" + host + ", port:" + port + ", period:" + period +
71 | ", unit:" + unit + ", prefix:" + prefix + ", separator:" + separator + ", clock:" + clock +
72 | ", name:" + name + ", localhost:" + localHost + ", metrics_registry:" + metricsRegistry +
73 | ", predicate:" + predicate + ", tags:" + Arrays.toString(tags.toArray()) + "}";
74 | }
75 | }
76 |
77 | public static final class ConfigBuilder {
78 | // Default values for Config
79 | private MetricsRegistry metricsRegistry = Metrics.defaultRegistry();
80 | private MetricPredicate predicate = MetricPredicate.ALL;
81 | private boolean printVMMetrics = true;
82 | private String host = "localhost";
83 | private int port = 5555;
84 | private long period = 60;
85 | private TimeUnit unit = TimeUnit.SECONDS;
86 | private String prefix = null;
87 | private String separator = " ";
88 | private final VirtualMachineMetrics vm = VirtualMachineMetrics.getInstance();
89 | private Clock clock = Clock.defaultClock();
90 | private String name = "riemann-reporter";
91 | private String localHost = null;
92 | private List tags = new ArrayList();
93 |
94 | private ConfigBuilder() { }
95 |
96 | public Config build(){
97 | return new Config(metricsRegistry, predicate, printVMMetrics, host, port,
98 | period, unit, prefix, separator, clock, name, localHost, tags);
99 | }
100 |
101 | public ConfigBuilder metricsRegistry(MetricsRegistry r) { metricsRegistry = r; return this; }
102 | public ConfigBuilder predicate(MetricPredicate p) { predicate = p; return this; }
103 | public ConfigBuilder printVMMetrics(Boolean p) { printVMMetrics = p; return this; }
104 | public ConfigBuilder host(String h) { host = h; return this; }
105 | public ConfigBuilder port(int p) { port = p; return this; }
106 | public ConfigBuilder period(long p) { period = p; return this; }
107 | public ConfigBuilder unit(TimeUnit t) { unit = t; return this; }
108 | public ConfigBuilder prefix(String p) { prefix = p; return this; }
109 | public ConfigBuilder separator(String s) { separator = s; return this; }
110 | public ConfigBuilder clock(Clock c) { clock = c; return this; }
111 | public ConfigBuilder name(String n) { name = n; return this; }
112 | public ConfigBuilder localHost(String l) { localHost = l; return this; }
113 | public ConfigBuilder tags(Collection ts) { tags.clear(); tags.addAll(ts); return this; }
114 | }
115 |
116 | public static void enable(final Config config) {
117 | try {
118 | if (config == null)
119 | throw new IllegalArgumentException("Config cannot be null");
120 |
121 | final RiemannReporter reporter = new RiemannReporter(config);
122 | reporter.start(config.period, config.unit);
123 | } catch (Exception e) {
124 | LOG.error("Error creating/starting Riemann reporter: ", e);
125 | }
126 | }
127 |
128 | public RiemannReporter(final Config c) throws IOException {
129 | this(c, RiemannClient.tcp(new InetSocketAddress(c.host, c.port)));
130 | riemann.connect();
131 | }
132 |
133 | public RiemannReporter(final Config c, final IRiemannClient riemann) {
134 | super(c.metricsRegistry, c.name);
135 | this.riemann = riemann;
136 | this.c = c;
137 | }
138 |
139 | @Override
140 | public void run() {
141 | try {
142 | final long epoch = c.clock.time() / 1000;
143 | if (c.printVMMetrics) {
144 | sendVMMetrics(epoch);
145 | }
146 |
147 | sendRegularMetrics(epoch);
148 | } catch (Exception e) {
149 | LOG.warn("Error writing to Riemann", e);
150 | }
151 | }
152 |
153 | protected void sendRegularMetrics(final Long epoch) {
154 | for (Entry> entry : getMetricsRegistry().groupedMetrics(c.predicate).entrySet()) {
155 | for (Entry subEntry : entry.getValue().entrySet()) {
156 | final Metric metric = subEntry.getValue();
157 | if (metric != null) {
158 | try {
159 | metric.processWith(this, subEntry.getKey(), epoch);
160 | } catch (Exception ignored) {
161 | LOG.error("Error sending regular metric:", ignored);
162 | }
163 | }
164 | }
165 | }
166 | }
167 |
168 | private EventDSL newEvent() {
169 | EventDSL event = riemann.event();
170 | if (c.localHost != null) {
171 | event.host(c.localHost);
172 | }
173 | if (!c.tags.isEmpty()) {
174 | event.tags(c.tags);
175 | }
176 | return event;
177 | }
178 |
179 | // The service name for a given metric.
180 | public String service(MetricName name, String... rest) {
181 | final StringBuilder sb = new StringBuilder();
182 | if (null != c.prefix) {
183 | sb.append(c.prefix).append(c.separator);
184 | }
185 | sb.append(name.getGroup())
186 | .append(c.separator)
187 | .append(name.getType())
188 | .append(c.separator);
189 | if (name.hasScope()) {
190 | sb.append(name.getScope())
191 | .append(c.separator);
192 | }
193 | sb.append(name.getName());
194 | for (String part : rest) {
195 | sb.append(c.separator);
196 | sb.append(part);
197 | }
198 | return sb.toString();
199 | }
200 |
201 | public String service(String... parts) {
202 | final StringBuilder sb = new StringBuilder();
203 | if (null != c.prefix) {
204 | sb.append(c.prefix).append(c.separator);
205 | }
206 |
207 | for (String p : parts) {
208 | sb.append(p).append(c.separator);
209 | }
210 |
211 | return sb.substring(0, sb.length() - c.separator.length());
212 | }
213 |
214 | @Override
215 | public void processGauge(MetricName name, Gauge> gauge, Long epoch) {
216 | Object v = gauge.value();
217 | EventDSL e = newEvent().service(service(name)).time(epoch);
218 | if (v instanceof Integer) {
219 | e.metric((Integer) v).send();
220 | } else if (v instanceof Long) {
221 | e.metric((Long) v).send();
222 | } else if (v instanceof Double) {
223 | e.metric((Double) v).send();
224 | } else if (v instanceof Float) {
225 | e.metric((Float) v).send();
226 | } else if (v instanceof Number) {
227 | e.metric(((Number) v).floatValue()).send();
228 | }
229 | }
230 |
231 | @Override
232 | public void processCounter(MetricName name, Counter counter, Long epoch) {
233 | newEvent()
234 | .service(service(name))
235 | .metric(counter.count())
236 | .time(epoch)
237 | .send();
238 | }
239 |
240 | @Override
241 | public void processMeter(MetricName name, Metered meter, Long epoch) {
242 | newEvent()
243 | .service(service(name))
244 | .metric(meter.oneMinuteRate())
245 | .time(epoch)
246 | .send();
247 | }
248 |
249 | @Override
250 | public void processHistogram(MetricName name, Histogram histogram, Long epoch) throws IOException {
251 | final String service = service(name);
252 | sendSummary(name, histogram, epoch);
253 | sendSample(name, histogram, epoch);
254 | }
255 |
256 | @Override
257 | public void processTimer(MetricName name, Timer timer, Long epoch) {
258 | processMeter(name, timer, epoch);
259 | sendSummary(name, timer, epoch);
260 | sendSample(name, timer, epoch);
261 | }
262 |
263 | protected void sendVMMetrics(long epoch) {
264 | newEvent().time(epoch).service(service("jvm", "memory", "heap-usage")).metric(c.vm.heapUsage()).send();
265 | newEvent().time(epoch).service(service("jvm", "memory", "non-heap-usage")).metric(c.vm.nonHeapUsage()).send();
266 | for (Entry pool : c.vm.memoryPoolUsage().entrySet()) {
267 | newEvent().time(epoch).service(service("jvm", "memory", "pool-usage", pool.getKey())).metric(pool.getValue()).send();
268 | }
269 | newEvent().time(epoch).service(service("jvm", "thread", "daemon-count")).metric(c.vm.daemonThreadCount()).send();
270 | newEvent().time(epoch).service(service("jvm", "thread", "count")).metric(c.vm.threadCount()).send();
271 | newEvent().time(epoch).service(service("jvm", "uptime")).metric(c.vm.uptime()).send();
272 | newEvent().time(epoch).service(service("jvm", "fd-usage")).metric(c.vm.fileDescriptorUsage()).send();
273 |
274 | for(Entry entry : c.vm.threadStatePercentages().entrySet()) {
275 | newEvent().time(epoch).service(service("jvm", "thread", "state", entry.getKey().toString().toLowerCase())).metric(entry.getValue()).send();
276 | }
277 |
278 | for(Entry entry : c.vm.garbageCollectors().entrySet()) {
279 | newEvent().time(epoch).service(service("jvm", "gc", entry.getKey(), "time")).metric(entry.getValue().getTime(TimeUnit.MILLISECONDS)).send();
280 | newEvent().time(epoch).service(service("jvm", "gc", entry.getKey(), "runs")).metric(entry.getValue().getRuns()).send();
281 | }
282 | }
283 |
284 | public void sendSummary(MetricName name, Summarizable metric, Long epoch) {
285 | newEvent().time(epoch).service(service(name, "min")).metric(metric.min()).send();
286 | newEvent().time(epoch).service(service(name, "max")).metric(metric.max()).send();
287 | newEvent().time(epoch).service(service(name, "mean")).metric(metric.mean()).send();
288 | newEvent().time(epoch).service(service(name, "stddev")).metric(metric.stdDev()).send();
289 | }
290 |
291 | public void sendSample(MetricName name, Sampling metric, Long epoch) {
292 | final Snapshot s = metric.getSnapshot();
293 | newEvent().time(epoch).service(service(name, ".5")).metric(s.getMedian()).send();
294 | newEvent().time(epoch).service(service(name, ".75")).metric(s.get75thPercentile()).send();
295 | newEvent().time(epoch).service(service(name, ".95")).metric(s.get95thPercentile()).send();
296 | newEvent().time(epoch).service(service(name, ".98")).metric(s.get98thPercentile()).send();
297 | newEvent().time(epoch).service(service(name, ".99")).metric(s.get99thPercentile()).send();
298 | newEvent().time(epoch).service(service(name, ".999")).metric(s.get999thPercentile()).send();
299 | }
300 | }
301 |
--------------------------------------------------------------------------------
/metrics2-riemann-reporter/src/test/java/riemann/java/client/tests/RiemannReporterConfigTest.java:
--------------------------------------------------------------------------------
1 | package riemann.java.client.tests;
2 |
3 | import com.yammer.metrics.reporting.RiemannReporter;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 |
7 | import java.util.HashSet;
8 | import java.util.concurrent.TimeUnit;
9 |
10 | import static org.junit.Assert.assertEquals;
11 | import static org.junit.Assert.assertTrue;
12 |
13 | public class RiemannReporterConfigTest {
14 |
15 | private RiemannReporter.ConfigBuilder builder;
16 |
17 | @Before
18 | public void setUp() {
19 | builder = RiemannReporter.Config.newBuilder();
20 | }
21 |
22 | @Test
23 | public void testDefaultConfigBuilding() {
24 | RiemannReporter.Config c = builder.build();
25 | assertEquals(c.port, 5555);
26 | assertEquals(c.unit, TimeUnit.SECONDS);
27 | assertEquals(c.separator, " ");
28 | assertEquals(c.host, "localhost");
29 | assertTrue(c.tags.isEmpty());
30 | }
31 |
32 | @Test
33 | public void testConfigBuilding() {
34 | RiemannReporter.Config c = builder.host("127.0.0.1")
35 | .unit(TimeUnit.MILLISECONDS)
36 | .port(9999)
37 | .separator("#")
38 | .host("riemann")
39 | .build();
40 |
41 | assertEquals(c.port, 9999);
42 | assertEquals(c.unit, TimeUnit.MILLISECONDS);
43 | assertEquals(c.separator, "#");
44 | assertEquals(c.host, "riemann");
45 |
46 | }
47 |
48 | @Test
49 | public void testTagsConfigBuilding() {
50 | RiemannReporter.Config c = builder.tags(new HashSet(){{ add("abc"); add("123"); }})
51 | .build();
52 | assertTrue(c.tags.contains("abc"));
53 | assertTrue(c.tags.contains("123"));
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/metrics3-riemann-reporter/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | 4.0.0
7 | metrics3-riemann-reporter
8 |
9 | riemann-java-client-parent
10 | io.riemann
11 | 0.5.4-SNAPSHOT
12 |
13 |
14 |
15 |
16 | io.dropwizard.metrics
17 | metrics-core
18 | 3.2.6
19 |
20 |
21 | ${project.groupId}
22 | riemann-java-client
23 | ${project.version}
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/metrics3-riemann-reporter/src/main/java/com/codahale/metrics/riemann/Riemann.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Dominic LoBue
3 | * Copyright 2014 Brandon Seibel
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.codahale.metrics.riemann;
19 |
20 | import io.riemann.riemann.client.IRiemannClient;
21 | import io.riemann.riemann.client.RiemannClient;
22 | import io.riemann.riemann.client.RiemannBatchClient;
23 | import io.riemann.riemann.client.UnsupportedJVMException;
24 |
25 |
26 | import java.io.Closeable;
27 | import java.io.IOException;
28 |
29 |
30 | public class Riemann implements Closeable {
31 |
32 | IRiemannClient client;
33 |
34 | public Riemann(String host, Integer port) throws IOException {
35 | this(host, port, 10);
36 | }
37 |
38 | public Riemann(String host, Integer port, int batchSize) throws IOException {
39 | this(getClient(host, port, batchSize));
40 | }
41 |
42 | private static IRiemannClient getClient(String host, Integer port, int batchSize) throws IOException {
43 | IRiemannClient c = RiemannClient.tcp(host, port);
44 | try {
45 | return new RiemannBatchClient(c, batchSize);
46 | } catch (UnsupportedJVMException e) {
47 | return c;
48 | }
49 | }
50 |
51 | public Riemann(IRiemannClient client) {
52 | this.client = client;
53 | }
54 |
55 | public void connect() throws IOException {
56 | if (!client.isConnected()) {
57 | client.connect();
58 | }
59 | }
60 |
61 | @Override
62 | public void close() throws IOException {
63 | if (client != null) {
64 | client.close();
65 | }
66 |
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/metrics3-riemann-reporter/src/main/java/com/codahale/metrics/riemann/ValueFilter.java:
--------------------------------------------------------------------------------
1 | package com.codahale.metrics.riemann;
2 |
3 | /**
4 | * Filters values to determine if they fall into a given range via the
5 | * {@link #applies(double)} method. Ranges are intervals, with endpoint
6 | * inclusion configurable. The {@code state} property implicitly binds values in
7 | * the represented range to that state for reporting.
8 | *
9 | * Instances are created using {@link ValueFilter.Builder}. Endpoints not
10 | * specified default to positive or negative infinity, respectively. For
11 | * example, {@code new ValueFilter.Builder("critical").withLower(50).build())}
12 | * creates a ValueFilter instance that binds "critical" to all values greater
13 | * than or equal to 50 and {@code new ValueFilter.Builder("warn")
14 | * .withUpperExclusive(300)
15 | * .withLower(100).build())} creates an instance
16 | * binding "warn" to values greater than or equal to 100 and strictly less than
17 | * 300.
18 | */
19 | public class ValueFilter {
20 |
21 | /** Lower bound of the interval */
22 | private final double lower;
23 |
24 | /** Upper bound of the interval */
25 | private final double upper;
26 |
27 | /** Whether or not the upper bound is included */
28 | private final boolean upperIncluded;
29 |
30 | /** Whether or not the lower bound is included */
31 | private final boolean lowerIncluded;
32 |
33 | /** State bound to values in this interval */
34 | private final String state;
35 |
36 | /** Create a ValueFilter instance using the given builder */
37 | private ValueFilter(Builder builder) {
38 | this.lower = builder.lower;
39 | this.upper = builder.upper;
40 | this.upperIncluded = builder.upperIncluded;
41 | this.lowerIncluded = builder.lowerIncluded;
42 | this.state = builder.state;
43 | }
44 |
45 | /**
46 | * Determine whether or not the given value falls within the range
47 | * associated with this ValueFilter.
48 | *
49 | * @param value value to test
50 | * @return true iff value is within the target interval
51 | */
52 | public boolean applies(double value) {
53 | boolean ret = upperIncluded ? value <= upper : value < upper;
54 | return ret && (lowerIncluded ? lower <= value : lower < value);
55 | }
56 |
57 | /**
58 | * @return the state bound to values in this ValueFilter
59 | */
60 | public String getState() {
61 | return state;
62 | }
63 |
64 | /**
65 | * Builder for ValueFilter instances
66 | */
67 | public static class Builder {
68 |
69 | /** Lower bound for the interval */
70 | private double lower = Double.NEGATIVE_INFINITY;
71 |
72 | /** Upper bound for the interval */
73 | private double upper = Double.POSITIVE_INFINITY;
74 |
75 | /** Whether or not the upper bound is included in the interval */
76 | private boolean upperIncluded = true;
77 |
78 | /** Whether or not the lower bound is included in the interval */
79 | private boolean lowerIncluded = true;
80 |
81 | /** State bound to values in the interval */
82 | private final String state;
83 |
84 | /**
85 | * Create a new builder for ValueFilters bound to the given state
86 | *
87 | * @param state state bound to values in the interval
88 | */
89 | public Builder(String state) {
90 | this.state = state;
91 | }
92 |
93 | /**
94 | * @param lower inclusive lower bound for the interval
95 | * @return Builder instance with lower set inclusively (i.e. lower bound
96 | * is included in the interval)
97 | */
98 | public Builder withLower(double lower) {
99 | this.lower = lower;
100 | return this;
101 | }
102 |
103 | /**
104 | * @param upper inclusive upper bound for the interval
105 | * @return Builder instance with upper set inclusively (i.e. upper bound
106 | * is included in the interval)
107 | */
108 | public Builder withUpper(double upper) {
109 | this.upper = upper;
110 | return this;
111 | }
112 |
113 | /**
114 | * @param lower exclusive lower bound for the interval
115 | * @return Builder instance with lower set exclusively (i.e. lower bound
116 | * is not included in the interval)
117 | */
118 | public Builder withLowerExclusive(double lower) {
119 | this.lower = lower;
120 | this.lowerIncluded = false;
121 | return this;
122 | };
123 |
124 | /**
125 | * @param upper exclusive upper bound for the interval
126 | * @return Builder instance with upper set exclusively (i.e. upper bound
127 | * is not included in the interval)
128 | */
129 | public Builder withUpperExclusive(double upper) {
130 | this.upper = upper;
131 | this.upperIncluded = false;
132 | return this;
133 | }
134 |
135 | /**
136 | * Build a ValueFilter using this Builder instance
137 | *
138 | * @return ValueFilter with properties determined by those of this
139 | * Builder
140 | */
141 | public ValueFilter build() {
142 | return new ValueFilter(this);
143 | }
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/metrics3-riemann-reporter/src/main/java/com/codahale/metrics/riemann/ValueFilterMap.java:
--------------------------------------------------------------------------------
1 | package com.codahale.metrics.riemann;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashMap;
5 | import java.util.List;
6 | import java.util.Map;
7 | import java.util.Map.Entry;
8 | import java.util.concurrent.ConcurrentHashMap;
9 | import java.util.concurrent.TimeUnit;
10 |
11 | import com.codahale.metrics.Counter;
12 | import com.codahale.metrics.Histogram;
13 | import com.codahale.metrics.Metered;
14 | import com.codahale.metrics.Metric;
15 | import com.codahale.metrics.Snapshot;
16 | import com.codahale.metrics.Timer;
17 |
18 | /**
19 | * Enables {@link ValueFilter}s to be associated with measures reported by
20 | * Metrics so that state can be computed based on values at report-generation
21 | * time. The {@link #addFilter(Metric, String, ValueFilter)} method associates a
22 | * {@link ValueFilter} with a measure and the {@code state} methods compute what
23 | * state should be reported based on the value of a measure.
24 | *
25 | * For example, {@code
26 | * ValueFilterMap valueFilterMap = new ValueFilterMap();
27 | * valueFilterMap
28 | .addFilter(timer, ValueFilterMap.MAX,
29 | new ValueFilter.Builder("critical").withLower(50).build())
30 | .addFilter(timer, ValueFilterMap.MEAN, new ValueFilter.Builder("warn")
31 | .withUpperExclusive(200).withLower(100).build());
32 | * } Attaches filters to the mean and max values reported by timers so that
33 | * {@code state(timer, "max")} will return "critical" if the max value reported
34 | * by the timer is greater than or equal to 50 and {@code state(timer, "mean")}
35 | * will return "warn" if the mean value is between 100 (inclusive) and 200
36 | * (exclusive). Filters are applied in the order they are added and the last one
37 | * that applies wins. If no filter applies, state methods return "ok".
38 | */
39 | public class ValueFilterMap {
40 |
41 | // Names for values reported by metrics - call these "measures"
42 | public static final String MAX = "max";
43 |
44 | public static final String MEAN = "mean";
45 |
46 | public static final String MIN = "min";
47 |
48 | public static final String STDDEV = "stddev";
49 |
50 | public static final String P50 = "p50";
51 |
52 | public static final String P75 = "p75";
53 |
54 | public static final String P95 = "p95";
55 |
56 | public static final String P98 = "p98";
57 |
58 | public static final String P99 = "p99";
59 |
60 | public static final String P999 = "p999";
61 |
62 | public static final String COUNT = "count";
63 |
64 | public static final String M1_RATE = "m1_rate";
65 |
66 | public static final String M5_RATE = "m5_rate";
67 |
68 | public static final String M15_RATE = "m15_rate";
69 |
70 | public static final String MEAN_RATE = "mean_rate";
71 |
72 | // TODO: when Java 8 is available, replace this bloated setup with lambdas
73 | //
74 | /** Implementations extract specific values from snapshots */
75 | interface SnapValue {
76 |
77 | /** @return the snapshot value that this lamba-like thing is keyed on */
78 | double value(Snapshot snapshot);
79 | }
80 |
81 | /** Implementations extract specific statistics from metered impls */
82 | interface MeteredValue {
83 |
84 | /** @return the metered value that this lamba-like thing is keyed on */
85 | double value(Metered metered);
86 | }
87 |
88 | /**
89 | * Map with keys equal to snapshot statistics names and values lambda-like
90 | * objects that extract the named statistic - statically initialized
91 | */
92 | private static final Map snapValueMap = new HashMap();
93 |
94 | /**
95 | * Map with keys equal to metered value names and values lambda-like objects
96 | * that extract the named metrics - statically initialized
97 | */
98 | private static final Map meteredValueMap = new HashMap();
99 |
100 | /**
101 | * Loads lambda-like functions into maps used to extract values from metrics
102 | */
103 | static {
104 | snapValueMap.put(MAX, new SnapValue() {
105 |
106 | public double value(Snapshot snapshot) {
107 | return snapshot.getMax();
108 | }
109 | });
110 | snapValueMap.put(MEAN, new SnapValue() {
111 |
112 | public double value(Snapshot snapshot) {
113 | return snapshot.getMean();
114 | }
115 | });
116 | snapValueMap.put(MIN, new SnapValue() {
117 |
118 | public double value(Snapshot snapshot) {
119 | return snapshot.getMin();
120 | }
121 | });
122 | snapValueMap.put(STDDEV, new SnapValue() {
123 |
124 | public double value(Snapshot snapshot) {
125 | return snapshot.getStdDev();
126 | }
127 | });
128 | snapValueMap.put(P50, new SnapValue() {
129 |
130 | public double value(Snapshot snapshot) {
131 | return snapshot.getMedian();
132 | }
133 | });
134 | snapValueMap.put(P75, new SnapValue() {
135 |
136 | public double value(Snapshot snapshot) {
137 | return snapshot.get75thPercentile();
138 | }
139 | });
140 | snapValueMap.put(P95, new SnapValue() {
141 |
142 | public double value(Snapshot snapshot) {
143 | return snapshot.get95thPercentile();
144 | }
145 | });
146 | snapValueMap.put(P98, new SnapValue() {
147 |
148 | public double value(Snapshot snapshot) {
149 | return snapshot.get98thPercentile();
150 | }
151 | });
152 | snapValueMap.put(P99, new SnapValue() {
153 |
154 | public double value(Snapshot snapshot) {
155 | return snapshot.get99thPercentile();
156 | }
157 | });
158 | snapValueMap.put(P999, new SnapValue() {
159 |
160 | public double value(Snapshot snapshot) {
161 | return snapshot.get999thPercentile();
162 | }
163 | });
164 |
165 | meteredValueMap.put(COUNT, new MeteredValue() {
166 |
167 | public double value(Metered metered) {
168 | return metered.getCount();
169 | }
170 | });
171 | meteredValueMap.put(M1_RATE, new MeteredValue() {
172 |
173 | public double value(Metered metered) {
174 | return metered.getOneMinuteRate();
175 | }
176 | });
177 | meteredValueMap.put(M5_RATE, new MeteredValue() {
178 |
179 | public double value(Metered metered) {
180 | return metered.getFiveMinuteRate();
181 | }
182 | });
183 | meteredValueMap.put(M15_RATE, new MeteredValue() {
184 |
185 | public double value(Metered metered) {
186 | return metered.getFifteenMinuteRate();
187 | }
188 | });
189 | meteredValueMap.put(MEAN_RATE, new MeteredValue() {
190 |
191 | public double value(Metered metered) {
192 | return metered.getMeanRate();
193 | }
194 | });
195 | }
196 |
197 | /**
198 | * If m is a Metric, then filterMapMap.get(m) is a map with keys equal to
199 | * measures (constants above) and values equal to ValueFilter instances.
200 | * ValueFilters mapped to measures are applied to determine the state
201 | * associated with the given measure reported to Riemann in m's report. For
202 | * example, if t is a timer, filterMapMap(t) is a map of lists of filters.
203 | * The keys to that map are "max", "mean", "min", etc. - all of the measures
204 | * that t has getters for. The associated value is a list of filters. The
205 | * last filter that applies determines what the reported state is for the
206 | * measure.
207 | */
208 | private final Map>> filterMapMap = new ConcurrentHashMap>>();
209 |
210 | public ValueFilterMap() {
211 | super();
212 | }
213 |
214 | /**
215 | * Add a filter associated with the given measure for the given metric.
216 | *
217 | * @param metric metric instance reporting the measure that the filter
218 | * applies to
219 | * @param measure name of the value reported by the metric that the filter
220 | * applies to
221 | * @param filter ValueFilter instance that, if it applies, may determine the
222 | * state reported for the measure
223 | * @return a reference to *this (for fluent activation)
224 | */
225 | public ValueFilterMap addFilter(Metric metric, String measure,
226 | ValueFilter filter) {
227 | Map> filterMap = filterMapMap.get(metric);
228 | List filterList;
229 | if (filterMap == null) {
230 | filterMap = new HashMap>();
231 | filterMapMap.put(metric, filterMap);
232 | }
233 | filterList = filterMap.get(measure);
234 | if (filterList == null) {
235 | filterList = new ArrayList();
236 | filterMap.put(measure, filterList);
237 | }
238 | filterList.add(filter);
239 | return this;
240 | }
241 |
242 | /**
243 | * Returns the list of filters that have been added to the given metric,
244 | * measure pair.
245 | *
246 | * @param metric metric the measure belongs to
247 | * @param measure name of one of the values reported by the metric
248 | * @return list of filters that should be applied when determining the state
249 | * reported with the given measure
250 | */
251 | public List getFilterList(Metric metric, String measure) {
252 | final Map> filterMap = filterMapMap
253 | .get(metric);
254 | if (filterMap == null) {
255 | return null;
256 | }
257 | return filterMap.get(measure);
258 | }
259 |
260 | /**
261 | * Determines the state that should be reported with the given measure for
262 | * the given timer instance.
263 | *
264 | * @param timer timer instance
265 | * @param measure name of the value reported by the timer whose reported
266 | * state is sought
267 | * @param durationUnit TimeUnit to convert timer's native (nanosecond)
268 | * measurements to
269 | * @return state corresponding to the given measure
270 | */
271 | public String state(Timer timer, String measure, TimeUnit durationUnit) {
272 | final Snapshot snap = timer.getSnapshot();
273 | final double value = snapValueMap.get(measure).value(snap) /
274 | durationUnit.toNanos(1);
275 | return state(getFilterList(timer, measure), value);
276 | }
277 |
278 | /**
279 | * Return the state that should be reported for the given measure, histogram
280 | * pair.
281 | *
282 | * @param histogram histogram instance
283 | * @param measure name of a measure included in the histogram report
284 | * @return the state associated to the current value of the measure in the
285 | * histogram, or "ok" if there is no state mapped to this measure,
286 | * histogram pair.
287 | */
288 | public String state(Histogram histogram, String measure) {
289 | double value;
290 | if (measure.equals(COUNT)) {
291 | value = histogram.getCount();
292 | } else {
293 | final Snapshot snap = histogram.getSnapshot();
294 | value = snapValueMap.get(measure).value(snap);
295 | }
296 | return state(getFilterList(histogram, measure), value);
297 | }
298 |
299 | /**
300 | * Return the state that should be reported for the given measure, metered
301 | * instance pair.
302 | *
303 | * @param metered metered instance
304 | * @param measure name of a measure included in the metered instance report
305 | * @return the state associated to the current value of the measure in the
306 | * metered instance, or "ok" if there is no state mapped to this
307 | * measure, metered instance pair.
308 | */
309 | public String state(Metered metered, String measure) {
310 | final double value = meteredValueMap.get(measure).value(metered);
311 | return state(getFilterList(metered, measure), value);
312 | }
313 |
314 | /**
315 | * Return the state that should be reported for the given counter, based on
316 | * its value.
317 | *
318 | * @param counter Counter instance
319 | * @return the state associated to the current value of the counter in the
320 | * or "ok" if there is no state mapped to this Counter value.
321 | */
322 | public String state(Counter counter) {
323 | final double value = counter.getCount();
324 | return state(getFilterList(counter, "count"), value);
325 | }
326 |
327 | /**
328 | * Clear all state mappings associated with the given Metric.
329 | *
330 | * @param metric Metric instance to clear mappings for
331 | */
332 | public synchronized void clear(Metric metric) {
333 | final Map> filters = filterMapMap.get(metric);
334 | for (Entry> entry : filters.entrySet()) {
335 | entry.getValue().clear();
336 | }
337 | filters.clear();
338 | }
339 |
340 | /**
341 | * Clear all mappings for all Metric instances.
342 | */
343 | public synchronized void clear() {
344 | for (Metric metric : filterMapMap.keySet()) {
345 | clear(metric);
346 | }
347 | }
348 |
349 | /**
350 | * Test all ValueFilters in filters against the given value. If none apply,
351 | * return "ok"; otherwise return the state associated with the last filter
352 | * that applies.
353 | *
354 | * @param filters List of ValueFilters to test
355 | * @param value value to test
356 | * @return "ok" or the state associated with the last filter in the list
357 | * that applies
358 | */
359 | private String state(List filters, double value) {
360 | String ret = "ok";
361 | if (filters != null) {
362 | for (ValueFilter filter : filters) {
363 | if (filter.applies(value)) {
364 | ret = filter.getState();
365 | }
366 | }
367 | }
368 | return ret;
369 | }
370 | }
371 |
--------------------------------------------------------------------------------
/metrics3-riemann-reporter/src/test/java/com/codahale/metrics/riemann/RiemannTest.java:
--------------------------------------------------------------------------------
1 | package com.codahale.metrics.riemann;
2 |
3 | import io.riemann.riemann.client.IRiemannClient;
4 |
5 | import org.junit.Test;
6 |
7 | import static org.mockito.Mockito.*;
8 |
9 | public class RiemannTest {
10 | private final IRiemannClient client = mock(IRiemannClient.class);
11 | private final Riemann riemann = new Riemann(client);
12 |
13 | @Test
14 | public void connectsToRiemann() throws Exception {
15 | riemann.connect();
16 | verify(client).connect();
17 | }
18 |
19 | @Test
20 | public void disconnectsFromRiemann() throws Exception {
21 | riemann.connect();
22 | riemann.close();
23 | verify(client).close();
24 | }
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/metrics3-riemann-reporter/src/test/java/com/codahale/metrics/riemann/ValueFilterTest.java:
--------------------------------------------------------------------------------
1 | package com.codahale.metrics.riemann;
2 |
3 | import static org.junit.Assert.assertFalse;
4 | import static org.junit.Assert.assertTrue;
5 |
6 | import org.junit.Test;
7 |
8 | public class ValueFilterTest {
9 |
10 | @Test
11 | public void testApplies() {
12 | final ValueFilter warnFilter = new ValueFilter.Builder("warn")
13 | .withLower(900).withUpper(1000).build();
14 | final ValueFilter criticalFilter = new ValueFilter.Builder("critical")
15 | .withLowerExclusive(1000).build();
16 | final ValueFilter halfOpenFilter = new ValueFilter.Builder("warn")
17 | .withUpperExclusive(300).withLower(100).build();
18 | assertTrue(warnFilter.applies(920));
19 | assertFalse(warnFilter.applies(1001));
20 | assertTrue(criticalFilter.applies(1001));
21 | assertFalse(criticalFilter.applies(950));
22 | assertTrue(halfOpenFilter.applies(200));
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/metrics4-riemann-reporter/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | 4.0.0
7 | metrics4-riemann-reporter
8 |
9 | riemann-java-client-parent
10 | io.riemann
11 | 0.5.4-SNAPSHOT
12 |
13 |
14 |
15 |
16 | io.dropwizard.metrics
17 | metrics-core
18 | 4.2.0
19 |
20 |
21 | ${project.groupId}
22 | riemann-java-client
23 | ${project.version}
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/metrics4-riemann-reporter/src/main/java/com/codahale/metrics/riemann/Riemann.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Dominic LoBue
3 | * Copyright 2014 Brandon Seibel
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.codahale.metrics.riemann;
19 |
20 | import io.riemann.riemann.client.IRiemannClient;
21 | import io.riemann.riemann.client.RiemannClient;
22 | import io.riemann.riemann.client.RiemannBatchClient;
23 | import io.riemann.riemann.client.UnsupportedJVMException;
24 |
25 |
26 | import java.io.Closeable;
27 | import java.io.IOException;
28 |
29 |
30 | public class Riemann implements Closeable {
31 |
32 | IRiemannClient client;
33 |
34 | public Riemann(String host, Integer port) throws IOException {
35 | this(host, port, 10);
36 | }
37 |
38 | public Riemann(String host, Integer port, int batchSize) throws IOException {
39 | this(getClient(host, port, batchSize));
40 | }
41 |
42 | private static IRiemannClient getClient(String host, Integer port, int batchSize) throws IOException {
43 | IRiemannClient c = RiemannClient.tcp(host, port);
44 | try {
45 | return new RiemannBatchClient(c, batchSize);
46 | } catch (UnsupportedJVMException e) {
47 | return c;
48 | }
49 | }
50 |
51 | public Riemann(IRiemannClient client) {
52 | this.client = client;
53 | }
54 |
55 | public void connect() throws IOException {
56 | if (!client.isConnected()) {
57 | client.connect();
58 | }
59 | }
60 |
61 | @Override
62 | public void close() throws IOException {
63 | if (client != null) {
64 | client.close();
65 | }
66 |
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/metrics4-riemann-reporter/src/main/java/com/codahale/metrics/riemann/ValueFilter.java:
--------------------------------------------------------------------------------
1 | package com.codahale.metrics.riemann;
2 |
3 | /**
4 | * Filters values to determine if they fall into a given range via the
5 | * {@link #applies(double)} method. Ranges are intervals, with endpoint
6 | * inclusion configurable. The {@code state} property implicitly binds values in
7 | * the represented range to that state for reporting.
8 | *
9 | * Instances are created using {@link ValueFilter.Builder}. Endpoints not
10 | * specified default to positive or negative infinity, respectively. For
11 | * example, {@code new ValueFilter.Builder("critical").withLower(50).build())}
12 | * creates a ValueFilter instance that binds "critical" to all values greater
13 | * than or equal to 50 and {@code new ValueFilter.Builder("warn")
14 | * .withUpperExclusive(300)
15 | * .withLower(100).build())} creates an instance
16 | * binding "warn" to values greater than or equal to 100 and strictly less than
17 | * 300.
18 | */
19 | public class ValueFilter {
20 |
21 | /** Lower bound of the interval */
22 | private final double lower;
23 |
24 | /** Upper bound of the interval */
25 | private final double upper;
26 |
27 | /** Whether or not the upper bound is included */
28 | private final boolean upperIncluded;
29 |
30 | /** Whether or not the lower bound is included */
31 | private final boolean lowerIncluded;
32 |
33 | /** State bound to values in this interval */
34 | private final String state;
35 |
36 | /** Create a ValueFilter instance using the given builder */
37 | private ValueFilter(Builder builder) {
38 | this.lower = builder.lower;
39 | this.upper = builder.upper;
40 | this.upperIncluded = builder.upperIncluded;
41 | this.lowerIncluded = builder.lowerIncluded;
42 | this.state = builder.state;
43 | }
44 |
45 | /**
46 | * Determine whether or not the given value falls within the range
47 | * associated with this ValueFilter.
48 | *
49 | * @param value value to test
50 | * @return true iff value is within the target interval
51 | */
52 | public boolean applies(double value) {
53 | boolean ret = upperIncluded ? value <= upper : value < upper;
54 | return ret && (lowerIncluded ? lower <= value : lower < value);
55 | }
56 |
57 | /**
58 | * @return the state bound to values in this ValueFilter
59 | */
60 | public String getState() {
61 | return state;
62 | }
63 |
64 | /**
65 | * Builder for ValueFilter instances
66 | */
67 | public static class Builder {
68 |
69 | /** Lower bound for the interval */
70 | private double lower = Double.NEGATIVE_INFINITY;
71 |
72 | /** Upper bound for the interval */
73 | private double upper = Double.POSITIVE_INFINITY;
74 |
75 | /** Whether or not the upper bound is included in the interval */
76 | private boolean upperIncluded = true;
77 |
78 | /** Whether or not the lower bound is included in the interval */
79 | private boolean lowerIncluded = true;
80 |
81 | /** State bound to values in the interval */
82 | private final String state;
83 |
84 | /**
85 | * Create a new builder for ValueFilters bound to the given state
86 | *
87 | * @param state state bound to values in the interval
88 | */
89 | public Builder(String state) {
90 | this.state = state;
91 | }
92 |
93 | /**
94 | * @param lower inclusive lower bound for the interval
95 | * @return Builder instance with lower set inclusively (i.e. lower bound
96 | * is included in the interval)
97 | */
98 | public Builder withLower(double lower) {
99 | this.lower = lower;
100 | return this;
101 | }
102 |
103 | /**
104 | * @param upper inclusive upper bound for the interval
105 | * @return Builder instance with upper set inclusively (i.e. upper bound
106 | * is included in the interval)
107 | */
108 | public Builder withUpper(double upper) {
109 | this.upper = upper;
110 | return this;
111 | }
112 |
113 | /**
114 | * @param lower exclusive lower bound for the interval
115 | * @return Builder instance with lower set exclusively (i.e. lower bound
116 | * is not included in the interval)
117 | */
118 | public Builder withLowerExclusive(double lower) {
119 | this.lower = lower;
120 | this.lowerIncluded = false;
121 | return this;
122 | };
123 |
124 | /**
125 | * @param upper exclusive upper bound for the interval
126 | * @return Builder instance with upper set exclusively (i.e. upper bound
127 | * is not included in the interval)
128 | */
129 | public Builder withUpperExclusive(double upper) {
130 | this.upper = upper;
131 | this.upperIncluded = false;
132 | return this;
133 | }
134 |
135 | /**
136 | * Build a ValueFilter using this Builder instance
137 | *
138 | * @return ValueFilter with properties determined by those of this
139 | * Builder
140 | */
141 | public ValueFilter build() {
142 | return new ValueFilter(this);
143 | }
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/metrics4-riemann-reporter/src/main/java/com/codahale/metrics/riemann/ValueFilterMap.java:
--------------------------------------------------------------------------------
1 | package com.codahale.metrics.riemann;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashMap;
5 | import java.util.List;
6 | import java.util.Map;
7 | import java.util.Map.Entry;
8 | import java.util.concurrent.ConcurrentHashMap;
9 | import java.util.concurrent.TimeUnit;
10 |
11 | import com.codahale.metrics.Counter;
12 | import com.codahale.metrics.Histogram;
13 | import com.codahale.metrics.Metered;
14 | import com.codahale.metrics.Metric;
15 | import com.codahale.metrics.Snapshot;
16 | import com.codahale.metrics.Timer;
17 |
18 | /**
19 | * Enables {@link ValueFilter}s to be associated with measures reported by
20 | * Metrics so that state can be computed based on values at report-generation
21 | * time. The {@link #addFilter(Metric, String, ValueFilter)} method associates a
22 | * {@link ValueFilter} with a measure and the {@code state} methods compute what
23 | * state should be reported based on the value of a measure.
24 | *
25 | * For example, {@code
26 | * ValueFilterMap valueFilterMap = new ValueFilterMap();
27 | * valueFilterMap
28 | .addFilter(timer, ValueFilterMap.MAX,
29 | new ValueFilter.Builder("critical").withLower(50).build())
30 | .addFilter(timer, ValueFilterMap.MEAN, new ValueFilter.Builder("warn")
31 | .withUpperExclusive(200).withLower(100).build());
32 | * } Attaches filters to the mean and max values reported by timers so that
33 | * {@code state(timer, "max")} will return "critical" if the max value reported
34 | * by the timer is greater than or equal to 50 and {@code state(timer, "mean")}
35 | * will return "warn" if the mean value is between 100 (inclusive) and 200
36 | * (exclusive). Filters are applied in the order they are added and the last one
37 | * that applies wins. If no filter applies, state methods return "ok".
38 | */
39 | public class ValueFilterMap {
40 |
41 | // Names for values reported by metrics - call these "measures"
42 | public static final String MAX = "max";
43 |
44 | public static final String MEAN = "mean";
45 |
46 | public static final String MIN = "min";
47 |
48 | public static final String STDDEV = "stddev";
49 |
50 | public static final String P50 = "p50";
51 |
52 | public static final String P75 = "p75";
53 |
54 | public static final String P95 = "p95";
55 |
56 | public static final String P98 = "p98";
57 |
58 | public static final String P99 = "p99";
59 |
60 | public static final String P999 = "p999";
61 |
62 | public static final String COUNT = "count";
63 |
64 | public static final String M1_RATE = "m1_rate";
65 |
66 | public static final String M5_RATE = "m5_rate";
67 |
68 | public static final String M15_RATE = "m15_rate";
69 |
70 | public static final String MEAN_RATE = "mean_rate";
71 |
72 | // TODO: when Java 8 is available, replace this bloated setup with lambdas
73 | //
74 | /** Implementations extract specific values from snapshots */
75 | interface SnapValue {
76 |
77 | /** @return the snapshot value that this lamba-like thing is keyed on */
78 | double value(Snapshot snapshot);
79 | }
80 |
81 | /** Implementations extract specific statistics from metered impls */
82 | interface MeteredValue {
83 |
84 | /** @return the metered value that this lamba-like thing is keyed on */
85 | double value(Metered metered);
86 | }
87 |
88 | /**
89 | * Map with keys equal to snapshot statistics names and values lambda-like
90 | * objects that extract the named statistic - statically initialized
91 | */
92 | private static final Map snapValueMap = new HashMap();
93 |
94 | /**
95 | * Map with keys equal to metered value names and values lambda-like objects
96 | * that extract the named metrics - statically initialized
97 | */
98 | private static final Map meteredValueMap = new HashMap();
99 |
100 | /**
101 | * Loads lambda-like functions into maps used to extract values from metrics
102 | */
103 | static {
104 | snapValueMap.put(MAX, new SnapValue() {
105 |
106 | public double value(Snapshot snapshot) {
107 | return snapshot.getMax();
108 | }
109 | });
110 | snapValueMap.put(MEAN, new SnapValue() {
111 |
112 | public double value(Snapshot snapshot) {
113 | return snapshot.getMean();
114 | }
115 | });
116 | snapValueMap.put(MIN, new SnapValue() {
117 |
118 | public double value(Snapshot snapshot) {
119 | return snapshot.getMin();
120 | }
121 | });
122 | snapValueMap.put(STDDEV, new SnapValue() {
123 |
124 | public double value(Snapshot snapshot) {
125 | return snapshot.getStdDev();
126 | }
127 | });
128 | snapValueMap.put(P50, new SnapValue() {
129 |
130 | public double value(Snapshot snapshot) {
131 | return snapshot.getMedian();
132 | }
133 | });
134 | snapValueMap.put(P75, new SnapValue() {
135 |
136 | public double value(Snapshot snapshot) {
137 | return snapshot.get75thPercentile();
138 | }
139 | });
140 | snapValueMap.put(P95, new SnapValue() {
141 |
142 | public double value(Snapshot snapshot) {
143 | return snapshot.get95thPercentile();
144 | }
145 | });
146 | snapValueMap.put(P98, new SnapValue() {
147 |
148 | public double value(Snapshot snapshot) {
149 | return snapshot.get98thPercentile();
150 | }
151 | });
152 | snapValueMap.put(P99, new SnapValue() {
153 |
154 | public double value(Snapshot snapshot) {
155 | return snapshot.get99thPercentile();
156 | }
157 | });
158 | snapValueMap.put(P999, new SnapValue() {
159 |
160 | public double value(Snapshot snapshot) {
161 | return snapshot.get999thPercentile();
162 | }
163 | });
164 |
165 | meteredValueMap.put(COUNT, new MeteredValue() {
166 |
167 | public double value(Metered metered) {
168 | return metered.getCount();
169 | }
170 | });
171 | meteredValueMap.put(M1_RATE, new MeteredValue() {
172 |
173 | public double value(Metered metered) {
174 | return metered.getOneMinuteRate();
175 | }
176 | });
177 | meteredValueMap.put(M5_RATE, new MeteredValue() {
178 |
179 | public double value(Metered metered) {
180 | return metered.getFiveMinuteRate();
181 | }
182 | });
183 | meteredValueMap.put(M15_RATE, new MeteredValue() {
184 |
185 | public double value(Metered metered) {
186 | return metered.getFifteenMinuteRate();
187 | }
188 | });
189 | meteredValueMap.put(MEAN_RATE, new MeteredValue() {
190 |
191 | public double value(Metered metered) {
192 | return metered.getMeanRate();
193 | }
194 | });
195 | }
196 |
197 | /**
198 | * If m is a Metric, then filterMapMap.get(m) is a map with keys equal to
199 | * measures (constants above) and values equal to ValueFilter instances.
200 | * ValueFilters mapped to measures are applied to determine the state
201 | * associated with the given measure reported to Riemann in m's report. For
202 | * example, if t is a timer, filterMapMap(t) is a map of lists of filters.
203 | * The keys to that map are "max", "mean", "min", etc. - all of the measures
204 | * that t has getters for. The associated value is a list of filters. The
205 | * last filter that applies determines what the reported state is for the
206 | * measure.
207 | */
208 | private final Map>> filterMapMap = new ConcurrentHashMap>>();
209 |
210 | public ValueFilterMap() {
211 | super();
212 | }
213 |
214 | /**
215 | * Add a filter associated with the given measure for the given metric.
216 | *
217 | * @param metric metric instance reporting the measure that the filter
218 | * applies to
219 | * @param measure name of the value reported by the metric that the filter
220 | * applies to
221 | * @param filter ValueFilter instance that, if it applies, may determine the
222 | * state reported for the measure
223 | * @return a reference to *this (for fluent activation)
224 | */
225 | public ValueFilterMap addFilter(Metric metric, String measure,
226 | ValueFilter filter) {
227 | Map> filterMap = filterMapMap.get(metric);
228 | List filterList;
229 | if (filterMap == null) {
230 | filterMap = new HashMap>();
231 | filterMapMap.put(metric, filterMap);
232 | }
233 | filterList = filterMap.get(measure);
234 | if (filterList == null) {
235 | filterList = new ArrayList();
236 | filterMap.put(measure, filterList);
237 | }
238 | filterList.add(filter);
239 | return this;
240 | }
241 |
242 | /**
243 | * Returns the list of filters that have been added to the given metric,
244 | * measure pair.
245 | *
246 | * @param metric metric the measure belongs to
247 | * @param measure name of one of the values reported by the metric
248 | * @return list of filters that should be applied when determining the state
249 | * reported with the given measure
250 | */
251 | public List getFilterList(Metric metric, String measure) {
252 | final Map> filterMap = filterMapMap
253 | .get(metric);
254 | if (filterMap == null) {
255 | return null;
256 | }
257 | return filterMap.get(measure);
258 | }
259 |
260 | /**
261 | * Determines the state that should be reported with the given measure for
262 | * the given timer instance.
263 | *
264 | * @param timer timer instance
265 | * @param measure name of the value reported by the timer whose reported
266 | * state is sought
267 | * @param durationUnit TimeUnit to convert timer's native (nanosecond)
268 | * measurements to
269 | * @return state corresponding to the given measure
270 | */
271 | public String state(Timer timer, String measure, TimeUnit durationUnit) {
272 | final Snapshot snap = timer.getSnapshot();
273 | final double value = snapValueMap.get(measure).value(snap) /
274 | durationUnit.toNanos(1);
275 | return state(getFilterList(timer, measure), value);
276 | }
277 |
278 | /**
279 | * Return the state that should be reported for the given measure, histogram
280 | * pair.
281 | *
282 | * @param histogram histogram instance
283 | * @param measure name of a measure included in the histogram report
284 | * @return the state associated to the current value of the measure in the
285 | * histogram, or "ok" if there is no state mapped to this measure,
286 | * histogram pair.
287 | */
288 | public String state(Histogram histogram, String measure) {
289 | double value;
290 | if (measure.equals(COUNT)) {
291 | value = histogram.getCount();
292 | } else {
293 | final Snapshot snap = histogram.getSnapshot();
294 | value = snapValueMap.get(measure).value(snap);
295 | }
296 | return state(getFilterList(histogram, measure), value);
297 | }
298 |
299 | /**
300 | * Return the state that should be reported for the given measure, metered
301 | * instance pair.
302 | *
303 | * @param metered metered instance
304 | * @param measure name of a measure included in the metered instance report
305 | * @return the state associated to the current value of the measure in the
306 | * metered instance, or "ok" if there is no state mapped to this
307 | * measure, metered instance pair.
308 | */
309 | public String state(Metered metered, String measure) {
310 | final double value = meteredValueMap.get(measure).value(metered);
311 | return state(getFilterList(metered, measure), value);
312 | }
313 |
314 | /**
315 | * Return the state that should be reported for the given counter, based on
316 | * its value.
317 | *
318 | * @param counter Counter instance
319 | * @return the state associated to the current value of the counter in the
320 | * or "ok" if there is no state mapped to this Counter value.
321 | */
322 | public String state(Counter counter) {
323 | final double value = counter.getCount();
324 | return state(getFilterList(counter, "count"), value);
325 | }
326 |
327 | /**
328 | * Clear all state mappings associated with the given Metric.
329 | *
330 | * @param metric Metric instance to clear mappings for
331 | */
332 | public synchronized void clear(Metric metric) {
333 | final Map> filters = filterMapMap.get(metric);
334 | for (Entry> entry : filters.entrySet()) {
335 | entry.getValue().clear();
336 | }
337 | filters.clear();
338 | }
339 |
340 | /**
341 | * Clear all mappings for all Metric instances.
342 | */
343 | public synchronized void clear() {
344 | for (Metric metric : filterMapMap.keySet()) {
345 | clear(metric);
346 | }
347 | }
348 |
349 | /**
350 | * Test all ValueFilters in filters against the given value. If none apply,
351 | * return "ok"; otherwise return the state associated with the last filter
352 | * that applies.
353 | *
354 | * @param filters List of ValueFilters to test
355 | * @param value value to test
356 | * @return "ok" or the state associated with the last filter in the list
357 | * that applies
358 | */
359 | private String state(List filters, double value) {
360 | String ret = "ok";
361 | if (filters != null) {
362 | for (ValueFilter filter : filters) {
363 | if (filter.applies(value)) {
364 | ret = filter.getState();
365 | }
366 | }
367 | }
368 | return ret;
369 | }
370 | }
371 |
--------------------------------------------------------------------------------
/metrics4-riemann-reporter/src/test/java/com/codahale/metrics/riemann/RiemannTest.java:
--------------------------------------------------------------------------------
1 | package com.codahale.metrics.riemann;
2 |
3 | import io.riemann.riemann.client.IRiemannClient;
4 |
5 | import org.junit.Test;
6 |
7 | import static org.mockito.Mockito.*;
8 |
9 | public class RiemannTest {
10 | private final IRiemannClient client = mock(IRiemannClient.class);
11 | private final Riemann riemann = new Riemann(client);
12 |
13 | @Test
14 | public void connectsToRiemann() throws Exception {
15 | riemann.connect();
16 | verify(client).connect();
17 | }
18 |
19 | @Test
20 | public void disconnectsFromRiemann() throws Exception {
21 | riemann.connect();
22 | riemann.close();
23 | verify(client).close();
24 | }
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/metrics4-riemann-reporter/src/test/java/com/codahale/metrics/riemann/ValueFilterTest.java:
--------------------------------------------------------------------------------
1 | package com.codahale.metrics.riemann;
2 |
3 | import static org.junit.Assert.assertFalse;
4 | import static org.junit.Assert.assertTrue;
5 |
6 | import org.junit.Test;
7 |
8 | public class ValueFilterTest {
9 |
10 | @Test
11 | public void testApplies() {
12 | final ValueFilter warnFilter = new ValueFilter.Builder("warn")
13 | .withLower(900).withUpper(1000).build();
14 | final ValueFilter criticalFilter = new ValueFilter.Builder("critical")
15 | .withLowerExclusive(1000).build();
16 | final ValueFilter halfOpenFilter = new ValueFilter.Builder("warn")
17 | .withUpperExclusive(300).withLower(100).build();
18 | assertTrue(warnFilter.applies(920));
19 | assertFalse(warnFilter.applies(1001));
20 | assertTrue(criticalFilter.applies(1001));
21 | assertFalse(criticalFilter.applies(950));
22 | assertTrue(halfOpenFilter.applies(200));
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | io.riemann
6 | riemann-java-client-parent
7 | 0.5.4-SNAPSHOT
8 |
9 | riemann-java-client
10 | metrics2-riemann-reporter
11 | metrics3-riemann-reporter
12 | metrics4-riemann-reporter
13 |
14 | pom
15 | Riemann Java Client
16 | Java client for http://riemann.io/
17 | /
18 |
19 |
20 | UTF-8
21 | UTF-8
22 |
23 |
24 |
25 |
26 | Apache-2.0
27 | https://choosealicense.com/licenses/apache-2.0/
28 |
29 |
30 |
31 |
32 |
33 |
34 | io.netty
35 | netty-bom
36 | 4.1.79.Final
37 | pom
38 | import
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | junit
47 | junit
48 | 4.13.1
49 | test
50 |
51 |
52 |
53 | org.mockito
54 | mockito-core
55 | 3.12.4
56 | test
57 |
58 |
59 |
60 |
61 |
62 |
63 | org.apache.maven.plugins
64 | maven-compiler-plugin
65 | 3.8.0
66 |
67 | 1.8
68 | 1.8
69 |
70 |
71 |
72 |
73 | org.codehaus.mojo
74 | build-helper-maven-plugin
75 | 3.0.0
76 |
77 |
78 | add-source
79 | generate-sources
80 |
81 | add-source
82 |
83 |
84 |
85 | target/generated-sources
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | org.apache.maven.plugins
94 | maven-source-plugin
95 | 3.0.1
96 |
97 |
98 | attach-sources
99 |
100 | jar-no-fork
101 |
102 |
103 |
104 |
105 |
106 |
107 | org.apache.maven.plugins
108 | maven-javadoc-plugin
109 |
110 | 8
111 | false
112 |
113 | 3.4.0
114 |
115 |
116 | attach-javadocs
117 |
118 | jar
119 |
120 |
121 |
122 |
123 |
124 |
125 | org.apache.felix
126 | maven-bundle-plugin
127 | 3.0.1
128 | true
129 |
130 |
131 |
132 | io.riemann.riemann;-noimport:=true,
133 | io.riemann.riemann.client;-noimport:=true,
134 | com.codahale.metrics.riemann;-noimport:=true,
135 | com.yammer.metrics.reporting;-noimport:=true
136 |
137 |
138 | io.dropwizard.metrics;version="[3.6.1)";resolution:="optional",
139 | com.yammer.metrics;resolution:="optional",
140 | com.yammer.metrics.core;resolution:="optional",
141 | com.yammer.metrics.stats;resolution:="optional",
142 | *
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 | clojars
153 | Clojars
154 | https://clojars.org/repo
155 |
156 |
157 |
158 |
--------------------------------------------------------------------------------
/riemann-java-client/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | riemann-java-client
6 | 4.0.0
7 |
8 |
9 | riemann-java-client-parent
10 | io.riemann
11 | 0.5.4-SNAPSHOT
12 |
13 |
14 |
15 |
16 | org.slf4j
17 | slf4j-api
18 | 1.7.25
19 |
20 |
21 | org.slf4j
22 | slf4j-simple
23 | 1.7.25
24 | test
25 |
26 |
27 |
28 | org.codehaus.jsr166-mirror
29 | jsr166y
30 | 1.7.0
31 | provided
32 |
33 |
34 |
35 | com.google.protobuf
36 | protobuf-java
37 | 3.16.3
38 | compile
39 |
40 |
41 |
42 | io.netty
43 | netty-codec
44 |
45 |
46 |
47 | io.netty
48 | netty-handler
49 |
50 |
51 |
52 | io.netty
53 | netty-transport
54 |
55 |
56 |
57 | javax.xml.bind
58 | jaxb-api
59 | 2.4.0-b180830.0359
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | maven-antrun-plugin
68 |
69 |
70 | generate-sources
71 | generate-sources
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | run
83 |
84 |
85 |
86 |
87 |
88 | org.apache.maven.plugins
89 | maven-compiler-plugin
90 | 3.8.0
91 |
92 | 1.8
93 | 1.8
94 |
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/clojure/lang/IBlockingDeref.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Rich Hickey. All rights reserved.
3 | * The use and distribution terms for this software are covered by the
4 | * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
5 | * which can be found in the file epl-v10.html at the root of this distribution.
6 | * By using this software in any fashion, you are agreeing to be bound by
7 | * the terms of this license.
8 | * You must not remove this notice, or any other, from this software.
9 | **/
10 |
11 | /* rich 3/18/11 */
12 |
13 | package clojure.lang;
14 |
15 | import java.lang.Exception;
16 |
17 | public interface IBlockingDeref {
18 | Object deref(long ms, Object timeoutValue) throws Exception;
19 | }
20 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/clojure/lang/IDeref.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Rich Hickey. All rights reserved.
3 | * The use and distribution terms for this software are covered by the
4 | * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
5 | * which can be found in the file epl-v10.html at the root of this distribution.
6 | * By using this software in any fashion, you are agreeing to be bound by
7 | * the terms of this license.
8 | * You must not remove this notice, or any other, from this software.
9 | **/
10 |
11 | /* rich Feb 9, 2009 */
12 |
13 | package clojure.lang;
14 |
15 | public interface IDeref{
16 | Object deref() throws Exception;
17 | }
18 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/AsynchronizeTransport.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | // Wraps an asynchronous transport with a concurrent synchronous layer.
4 |
5 | import io.riemann.riemann.Proto.Msg;
6 | import java.io.IOException;
7 |
8 | public class AsynchronizeTransport implements AsynchronousTransport {
9 | public final SynchronousTransport transport;
10 |
11 | public AsynchronizeTransport(final SynchronousTransport transport) {
12 | this.transport = transport;
13 | }
14 |
15 | // Asynchronous path
16 | public Promise sendMessage(final Msg msg) {
17 | final Promise p = new Promise();
18 |
19 | try {
20 | final Msg response = transport.sendMessage(msg);
21 | if (response == null) {
22 | p.deliver(new UnsupportedOperationException(
23 | transport.toString() + " doesn't support receiving messages."));
24 | } else {
25 | p.deliver(response);
26 | }
27 | } catch (IOException e) {
28 | p.deliver(e);
29 | } catch (RuntimeException e) {
30 | p.deliver(e);
31 | }
32 | return p;
33 | }
34 |
35 | @Override
36 | public SynchronousTransport transport() {
37 | return transport;
38 | }
39 |
40 | // Lifecycle
41 | @Override
42 | public boolean isConnected() {
43 | return transport.isConnected();
44 | }
45 |
46 | @Override
47 | public void connect() throws IOException {
48 | transport.connect();
49 | }
50 |
51 | @Override
52 | public void close() {
53 | transport.close();
54 | }
55 |
56 | @Override
57 | public void reconnect() throws IOException {
58 | transport.reconnect();
59 | }
60 |
61 | @Override
62 | public void flush() throws IOException {
63 | transport.flush();
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/AsynchronousTransport.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | import io.riemann.riemann.Proto.Msg;
4 |
5 | public interface AsynchronousTransport extends Transport {
6 | // Schedules a message to be sent, returns a promise which fulfills the
7 | // response. There are *no* guarantees that an asynchronous message will be
8 | // delivered in order, or at all; you *must* dereference the returned promise.
9 | IPromise sendMessage(final Msg msg);
10 | }
11 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/ChainPromise.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | import java.util.concurrent.TimeUnit;
4 | import java.io.IOException;
5 |
6 | // Imagine you're calling a function send() which returns an
7 | // IPromise, and want to return that promise to the caller.
8 | //
9 | // IPromise cleanHouse() {
10 | // return send();
11 | // }
12 | //
13 | // Problem is, you're not going to call send(); yet.
14 | //
15 | // IPromise cleanHouse() {
16 | // meh(sureDadWhatever);
17 | //
18 | // return // um what goes here???
19 | // }
20 | //
21 | // You're gonna call it *later*, because you're doing some kind of batching
22 | // async magic. When you *do* call send() you'll be able to hand that result
23 | // off to the client, but not yet.
24 | //
25 | // public void eventually() {
26 | // final IPromise = send();
27 | // // OK now what?
28 | // }
29 | //
30 | // Solution: construct a ChainPromise *now*, and call .attach(send())
31 | // on that ChainPromise *later*, which hooks the ChainPromise up to the
32 | // response of send(). Derefs on the ChainPromise wait until .attach() happens,
33 | // then dereference *that*.
34 | //
35 | // JUST ADD MORE LATCHES AND ATOMICREFERENCES, EVERYTHING IS FUCKING FINE
36 | public class ChainPromise implements IPromise {
37 | public final IPromise> inner = new Promise>();
38 |
39 | public void attach(final IPromise innerValue) {
40 | inner.deliver(innerValue);
41 | }
42 |
43 | @Override
44 | public void deliver(final Object value) {
45 | throw new UnsupportedOperationException("Can't deliver to a chained promise.; deliver to the underlying promise instead?");
46 | }
47 |
48 | @Override
49 | public T deref() throws IOException {
50 | return inner.deref().deref();
51 | }
52 |
53 | @Override
54 | public T deref(final long time, final TimeUnit unit) throws IOException {
55 | return deref(time, unit, null);
56 | }
57 |
58 | @Override
59 | public T deref(final long time, final TimeUnit unit, final T timeoutValue)
60 | throws IOException {
61 | return (T) unsafeDeref(time, unit, timeoutValue);
62 | }
63 |
64 | @Override
65 | public Object deref(final long millis, final Object timeoutValue)
66 | throws IOException {
67 | return unsafeDeref(millis, TimeUnit.MILLISECONDS, timeoutValue);
68 | }
69 |
70 | @Override
71 | public Object unsafeDeref(final long time,
72 | final TimeUnit unit,
73 | final Object timeoutValue)
74 | throws IOException {
75 | final long t1 = System.nanoTime();
76 |
77 | // Extract our attached promise
78 | final Object attached = inner.unsafeDeref(time, unit, timeoutValue);
79 | if (timeoutValue == attached) {
80 | return timeoutValue;
81 | }
82 |
83 | // How much time left?
84 | final long remainingNanos = t1 + unit.toNanos(time) - System.nanoTime();
85 |
86 | // Since inner.unsafeDeref did *not* return the timeoutValue, it gave us an
87 | // IPromise.
88 | return ((IPromise) attached)
89 | .unsafeDeref(remainingNanos, TimeUnit.NANOSECONDS, timeoutValue);
90 | }
91 |
92 | @Override
93 | public IPromise map(Fn2 f) {
94 | return new MapPromise(this, f);
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/EventBuilder.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | import io.riemann.riemann.Proto;
4 | import io.riemann.riemann.Proto.Attribute;
5 |
6 | import java.util.Arrays;
7 | import java.util.HashMap;
8 | import java.util.List;
9 | import java.util.Map;
10 |
11 | public class EventBuilder {
12 | private final Proto.Event.Builder builder;
13 | private final Map attributes = new HashMap();
14 |
15 | public EventBuilder() {
16 | this.builder = Proto.Event.newBuilder();
17 | }
18 |
19 | public EventBuilder host(String host) {
20 | if (null == host) {
21 | builder.clearHost();
22 | } else {
23 | builder.setHost(host);
24 | }
25 | return this;
26 | }
27 |
28 | public EventBuilder service(String service) {
29 | if (null == service) {
30 | builder.clearService();
31 | } else {
32 | builder.setService(service);
33 | }
34 | return this;
35 | }
36 |
37 | public EventBuilder state(String state) {
38 | if (null == state) {
39 | builder.clearState();
40 | } else {
41 | builder.setState(state);
42 | }
43 | return this;
44 | }
45 |
46 | public EventBuilder description(String description) {
47 | if (null == description) {
48 | builder.clearDescription();
49 | } else {
50 | builder.setDescription(description);
51 | }
52 | return this;
53 | }
54 |
55 | public EventBuilder time() {
56 | builder.clearTime();
57 | builder.clearTimeMicros();
58 | return this;
59 | }
60 |
61 | public EventBuilder time(float time) {
62 | builder.setTime((long) time);
63 | builder.setTimeMicros((long) (time * 1000000));
64 | return this;
65 | }
66 |
67 | public EventBuilder time(double time) {
68 | builder.setTime((long) time);
69 | builder.setTimeMicros((long) (time * 1000000));
70 | return this;
71 | }
72 |
73 | public EventBuilder time(long time) {
74 | builder.setTime(time);
75 | return this;
76 | }
77 |
78 | public EventBuilder metric() {
79 | builder.clearMetricF();
80 | builder.clearMetricD();
81 | builder.clearMetricSint64();
82 | return this;
83 | }
84 |
85 | public EventBuilder metric(byte metric) {
86 | builder.setMetricSint64((long) metric);
87 | builder.setMetricF((float) metric);
88 | return this;
89 | }
90 |
91 | public EventBuilder metric(short metric) {
92 | builder.setMetricSint64((long) metric);
93 | builder.setMetricF((float) metric);
94 | return this;
95 | }
96 |
97 | public EventBuilder metric(int metric) {
98 | builder.setMetricSint64((long) metric);
99 | builder.setMetricF((float) metric);
100 | return this;
101 | }
102 |
103 | public EventBuilder metric(long metric) {
104 | builder.setMetricSint64(metric);
105 | builder.setMetricF((float) metric);
106 | return this;
107 | }
108 |
109 | public EventBuilder metric(float metric) {
110 | builder.setMetricF(metric);
111 | return this;
112 | }
113 |
114 | public EventBuilder metric(double metric) {
115 | builder.setMetricD(metric);
116 | builder.setMetricF((float) metric);
117 | return this;
118 | }
119 |
120 | public EventBuilder tag(String tag) {
121 | builder.addTags(tag);
122 | return this;
123 | }
124 |
125 | public EventBuilder tags(List tags) {
126 | builder.addAllTags(tags);
127 | return this;
128 | }
129 |
130 | public EventBuilder tags(String... tags) {
131 | builder.addAllTags(Arrays.asList(tags));
132 | return this;
133 | }
134 |
135 | public EventBuilder ttl() {
136 | builder.clearTtl();
137 | return this;
138 | }
139 |
140 | public EventBuilder ttl(float ttl) {
141 | builder.setTtl(ttl);
142 | return this;
143 | }
144 |
145 | public EventBuilder attribute(String name, String value) {
146 | attributes.put(name, value);
147 | return this;
148 | }
149 |
150 | public EventBuilder attributes(Map attributes) {
151 | this.attributes.putAll(attributes);
152 | return this;
153 | }
154 |
155 | // Returns the compiled Protobuf event for this DSL. Merges in the custom
156 | // attributes map. Can only be called safely once.
157 | public Proto.Event build() {
158 | for (Map.Entry entry : attributes.entrySet()) {
159 | Attribute.Builder attribBuilder = Attribute.newBuilder();
160 | attribBuilder.setKey(entry.getKey());
161 | attribBuilder.setValue(entry.getValue());
162 | builder.addAttributes(attribBuilder);
163 | }
164 | return builder.build();
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/EventDSL.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | import io.riemann.riemann.Proto.Attribute;
4 | import io.riemann.riemann.Proto.Event;
5 | import io.riemann.riemann.Proto.Msg;
6 |
7 | import java.util.Arrays;
8 | import java.util.List;
9 | import java.util.Map;
10 | import java.util.HashMap;
11 |
12 | public class EventDSL {
13 | public final IRiemannClient client;
14 | public final EventBuilder event;
15 |
16 | public EventDSL(IRiemannClient client) {
17 | this.client = client;
18 | this.event = new EventBuilder();
19 | try {
20 | this.event.host(java.net.InetAddress.getLocalHost().getHostName());
21 | } catch (java.net.UnknownHostException e) {
22 | // If we can't get the local host, a null host is perfectly
23 | // acceptable. Caller will know soon enough. :)
24 | }
25 | }
26 |
27 | public EventDSL host(String host) {
28 | this.event.host(host);
29 | return this;
30 | }
31 |
32 | public EventDSL service(String service) {
33 | this.event.service(service);
34 | return this;
35 | }
36 |
37 | public EventDSL state(String state) {
38 | this.event.state(state);
39 | return this;
40 | }
41 |
42 | public EventDSL description(String description) {
43 | this.event.description(description);
44 | return this;
45 | }
46 |
47 | public EventDSL time(Null n) {
48 | this.event.time();
49 | return this;
50 | }
51 |
52 | public EventDSL time(float time) {
53 | this.event.time(time);
54 | return this;
55 | }
56 |
57 | public EventDSL time(double time) {
58 | this.event.time(time);
59 | return this;
60 | }
61 | public EventDSL time(long time) {
62 | this.event.time(time);
63 | return this;
64 | }
65 |
66 | public EventDSL metric(Null n) {
67 | this.event.metric();
68 | return this;
69 | }
70 |
71 | public EventDSL metric(byte metric) {
72 | this.event.metric(metric);
73 | return this;
74 | }
75 |
76 | public EventDSL metric(short metric) {
77 | this.event.metric(metric);
78 | return this;
79 | }
80 |
81 | public EventDSL metric(int metric) {
82 | this.event.metric(metric);
83 | return this;
84 | }
85 |
86 | public EventDSL metric(long metric) {
87 | this.event.metric(metric);
88 | return this;
89 | }
90 |
91 | public EventDSL metric(float metric) {
92 | this.event.metric(metric);
93 | return this;
94 | }
95 |
96 | public EventDSL metric(double metric) {
97 | this.event.metric(metric);
98 | return this;
99 | }
100 |
101 | public EventDSL tag(String tag) {
102 | this.event.tag(tag);
103 | return this;
104 | }
105 |
106 | public EventDSL tags(List tags) {
107 | this.event.tags(tags);
108 | return this;
109 | }
110 |
111 | public EventDSL tags(String... tags) {
112 | this.event.tags(tags);
113 | return this;
114 | }
115 |
116 | public EventDSL ttl(Null n) {
117 | this.event.ttl();
118 | return this;
119 | }
120 |
121 | public EventDSL ttl(float ttl) {
122 | this.event.ttl(ttl);
123 | return this;
124 | }
125 |
126 | public EventDSL attribute(String name, String value) {
127 | this.event.attribute(name, value);
128 | return this;
129 | }
130 |
131 | public EventDSL attributes(Map attributes) {
132 | this.event.attributes(attributes);
133 | return this;
134 | }
135 |
136 | // Returns the compiled Protobuf event for this DSL. Merges in the custom
137 | // attributes map. Can only be called safely once.
138 | public Event build() {
139 | return this.event.build();
140 | }
141 |
142 | public IPromise send() {
143 | return client.sendEvent(build());
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/ExceptionReporter.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | /**
4 | * Exception-reporting callback.
5 | *
6 | */
7 | public interface ExceptionReporter {
8 | public void reportException(final Throwable t);
9 | }
10 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/Fn2.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | // A function from T1 to T2.
4 | public interface Fn2 {
5 | public T2 call(final T1 x);
6 | }
7 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/IPromise.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | import java.util.concurrent.TimeUnit;
4 | import java.io.IOException;
5 | import clojure.lang.IDeref;
6 | import clojure.lang.IBlockingDeref;
7 |
8 | public interface IPromise extends IDeref, IBlockingDeref {
9 | // Fulfill this promise with a single value. Only the first call to
10 | // deliver() will succeed; successive deliveries are noops.
11 | public void deliver(final Object value);
12 |
13 | // Dereference this promise, blocking indefinitely.
14 | public T deref() throws IOException;
15 |
16 | // Dereference this promise, returning null if it times out.
17 | public T deref(final long time, final TimeUnit unit) throws IOException;
18 |
19 | // Dereference this promise, returning a T.
20 | public T deref(final long time,
21 | final TimeUnit unit,
22 | final T timeoutValue) throws IOException;
23 |
24 | // A timed deref that allows any object for the timeout value. You and I
25 | // both know this will return either a T or just timeoutValue, but
26 | // unfortunately, as you probably already know, the Java type system.
27 | public Object unsafeDeref(final long time,
28 | final TimeUnit unit,
29 | final Object timeoutValue) throws IOException;
30 |
31 | // Return a new promise, based on this one, which converts values from T to
32 | // T2 using a function.
33 | public IPromise map(Fn2 f);
34 | }
35 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/IRiemannClient.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | import io.riemann.riemann.Proto.Event;
4 | import io.riemann.riemann.Proto.Query;
5 | import io.riemann.riemann.Proto.Msg;
6 | import java.util.List;
7 |
8 | // The core functionality of any client.
9 |
10 | public interface IRiemannClient extends AsynchronousTransport {
11 | // Send any number of events asynchronously. Returns a promise of a response
12 | // Msg.
13 | IPromise sendEvent(final Event event);
14 | IPromise sendEvents(final Event... events);
15 | IPromise sendEvents(final List events);
16 |
17 | // Send an exception as an event.
18 | IPromise sendException(final String service, final Throwable t);
19 |
20 | // Query the server for all events matching query. Returns a promise of a
21 | // list of events.
22 | IPromise> query(final String q);
23 |
24 | // Create an EventDSL bound to this client
25 | EventDSL event();
26 | }
27 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/MapPromise.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | import java.util.concurrent.TimeUnit;
4 | import java.io.IOException;
5 |
6 | // Maps a Promise of type T1 to a promise of type T2 by applying a function
7 | // T1->T2 on a call to deref(). Caches the value so f is only invoked once (or
8 | // zero times if a timeout/error occurs).
9 | public class MapPromise implements IPromise {
10 | public static final Object sentinel = new Object();
11 |
12 | public volatile Object value = sentinel;
13 | public final IPromise p;
14 | public final Fn2 f;
15 |
16 | // Wrap a promise of type T1 with a function from T1->T2.
17 | public MapPromise(final IPromise p, final Fn2 f) {
18 | this.p = p;
19 | this.f = f;
20 | }
21 |
22 | // Call map and trap RuntimeExceptions as values
23 | // Fuck me, I miss macros
24 | public Object mapCapturingExceptions(final T1 x) {
25 | try {
26 | return f.call(x);
27 | } catch (RuntimeException e) {
28 | return e;
29 | }
30 | }
31 |
32 | // Delivering to a map promise delivers to the underlying promise.
33 | public void deliver(final Object value) {
34 | p.deliver(value);
35 | }
36 |
37 | // Deref, applying map to the value returned by the wrapped promise.
38 | public T2 deref() throws IOException {
39 | if (sentinel == value) {
40 | synchronized(this) {
41 | if (sentinel == value) {
42 | value = mapCapturingExceptions(p.deref());
43 | }
44 | }
45 | }
46 | return (T2) Promise.rehydrate(value);
47 | }
48 |
49 | public T2 deref(final long time, final TimeUnit unit) throws IOException {
50 | return deref(time, unit, null);
51 | }
52 |
53 | public T2 deref(final long time,
54 | final TimeUnit unit,
55 | final T2 timeoutValue) throws IOException {
56 | return (T2) unsafeDeref(time, unit, timeoutValue);
57 | }
58 |
59 | @Override
60 | public Object deref(final long millis, final Object timeoutValue)
61 | throws IOException {
62 | return unsafeDeref(millis, TimeUnit.MILLISECONDS, timeoutValue);
63 | }
64 |
65 | public Object unsafeDeref(final long time,
66 | final TimeUnit unit,
67 | final Object timeoutValue) throws IOException {
68 | if (sentinel == value) {
69 | synchronized(this) {
70 | if (sentinel == value) {
71 | final Object response = p.unsafeDeref(time, unit, sentinel);
72 | if (sentinel == response) {
73 | return timeoutValue;
74 | } else {
75 | // If we *didn't* get our timeout value, unsafeDeref is guaranteed
76 | // to give us a T1.
77 | value = mapCapturingExceptions((T1) response);
78 | }
79 | }
80 | }
81 | }
82 | return (T2) Promise.rehydrate(value);
83 | }
84 |
85 | @Override
86 | public IPromise map(Fn2 f) {
87 | return new MapPromise(this, f);
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/MsgTooLargeException.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | // Thrown when a message is too large to send over the specified transport.
4 | public class MsgTooLargeException extends java.io.IOException {
5 | }
6 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/MsgValidator.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | // Validates the integrity of messages, throwing ServerError if one is not OK.
4 | // Returns the message otherwise.
5 |
6 | import io.riemann.riemann.Proto.Msg;
7 |
8 | public class MsgValidator implements Fn2 {
9 | public Msg call(final Msg message) throws ServerError {
10 | if (message.hasOk() && !message.getOk()) {
11 | throw new ServerError(message.getError());
12 | }
13 | return message;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/Null.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | // null has a type, but you can't use it! Since the JVM doesn't have Maybe[], we'll just make one up instead.
4 | // The *only* acceptable argument for f(Null n) is f(null).
5 | public class Null {
6 | private Null() {}
7 | }
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/OverloadedException.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | // Thrown when a client is unable to handle additional requests; for example,
4 | // because of high network latencies
5 | public class OverloadedException extends java.io.IOException {
6 | public OverloadedException(final String msg) {
7 | super(msg);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/Promise.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | import java.util.concurrent.TimeUnit;
4 | import java.util.concurrent.CountDownLatch;
5 | import java.util.concurrent.atomic.AtomicReference;
6 | import java.io.IOException;
7 |
8 | public class Promise implements IPromise {
9 | // We store exceptions and actual values in the same reference; on deref(),
10 | // we have to *throw*, not return, the exceptions. This fun takes an Object
11 | // which is either an IOException, a RuntimeException, or a T. Throws the
12 | // exceptions, returns a T.
13 | public static Object rehydrate(final Object value) throws IOException {
14 | if (value instanceof IOException) {
15 | throw (IOException) value;
16 | } else if (value instanceof RuntimeException) {
17 | throw (RuntimeException) value;
18 | } else {
19 | return value;
20 | }
21 | }
22 |
23 | public final CountDownLatch latch = new CountDownLatch(1);
24 | public final AtomicReference ref = new AtomicReference(latch);
25 |
26 | public Promise() { }
27 |
28 | public void deliver(Object value) {
29 | if (0 < latch.getCount() && ref.compareAndSet(latch, value)) {
30 | latch.countDown();
31 | }
32 | }
33 |
34 | public T deref() throws IOException {
35 | try {
36 | latch.await();
37 | return (T) rehydrate(ref.get());
38 | } catch (InterruptedException e) {
39 | return null;
40 | }
41 | }
42 |
43 | public T deref(final long time, final TimeUnit unit) throws IOException {
44 | return deref(time, unit, null);
45 | }
46 |
47 | // Type-safe deref
48 | public T deref(final long time,
49 | final TimeUnit unit,
50 | final T timeoutValue) throws IOException {
51 | // My kingdom for a union type
52 | return (T) unsafeDeref(time, unit, timeoutValue);
53 | }
54 |
55 | @Override
56 | public Object deref(final long millis, final Object timeoutValue)
57 | throws IOException {
58 | return unsafeDeref(millis, TimeUnit.MILLISECONDS, timeoutValue);
59 | }
60 |
61 | // A timed deref that allows any object for the timeout value. You and I
62 | // both know this will return either a T1 or just timeoutValue, but
63 | // unfortunately, as you probably already know, the Java type system.
64 | public Object unsafeDeref(final long time,
65 | final TimeUnit unit,
66 | final Object timeoutValue) throws IOException {
67 | try {
68 | if (latch.await(time, unit)) {
69 | return (T) rehydrate(ref.get());
70 | } else {
71 | return timeoutValue;
72 | }
73 | } catch (InterruptedException e) {
74 | return timeoutValue;
75 | }
76 | }
77 |
78 | // Return a new promise, based on this one, which converts values from T to
79 | // T2 using a function.
80 | @Override
81 | public IPromise map(Fn2 f) {
82 | return new MapPromise(this, f);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/ReconnectHandler.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | import io.netty.bootstrap.Bootstrap;
4 | import io.netty.channel.ChannelFuture;
5 | import io.netty.channel.ChannelHandlerContext;
6 | import io.netty.channel.ChannelInboundHandlerAdapter;
7 | import io.netty.channel.group.ChannelGroup;
8 | import io.netty.handler.timeout.ReadTimeoutException;
9 | import java.net.ConnectException;
10 | import java.util.concurrent.TimeUnit;
11 | import java.util.concurrent.atomic.AtomicLong;
12 |
13 | public class ReconnectHandler extends ChannelInboundHandlerAdapter {
14 | private final Bootstrap bootstrap;
15 | private final ChannelGroup channels;
16 | public long startTime = -1;
17 | public final AtomicLong delay;
18 | public final TimeUnit unit;
19 |
20 | public ReconnectHandler(Bootstrap bootstrap, ChannelGroup channels, AtomicLong delay, TimeUnit unit) {
21 | this.bootstrap = bootstrap;
22 | this.channels = channels;
23 | this.delay = delay;
24 | this.unit = unit;
25 | }
26 |
27 | @Override
28 | public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
29 | try {
30 | ctx.executor().schedule(new Runnable() {
31 | @Override
32 | public void run() {
33 | ChannelFuture channelFuture = bootstrap.connect();
34 | channels.add(channelFuture.channel());
35 | }
36 | }, delay.get(), unit);
37 | } catch (java.lang.IllegalStateException ex) {
38 | // The executor must have been stopped.
39 | }
40 | super.channelInactive(ctx);
41 | }
42 |
43 | @Override
44 | public void channelActive(ChannelHandlerContext ctx) throws Exception {
45 | if (startTime < 0) {
46 | startTime = System.currentTimeMillis();
47 | }
48 | super.channelActive(ctx);
49 | }
50 |
51 | @Override
52 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) {
53 | final Throwable cause = e.getCause();
54 |
55 | if (cause instanceof ConnectException) {
56 | startTime = -1;
57 | } else if (cause instanceof ReadTimeoutException) {
58 | // The connection was OK but there was no traffic for the last period.
59 | } else {
60 | ctx.write(e);
61 | }
62 | ctx.channel().close();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/RiemannBatchClient.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | // Wraps any Riemann client for use in high-throughput loads. Behaves
4 | // exactly like the underlying client, except that calls to sendEvents and
5 | // sendEventsWithAck (including client.event()...send()), are
6 | // batched together into single messages for more efficient transfer.
7 | //
8 | // Can improve bulk throughput by at least an order of magnitude, without
9 | // requiring coordination from writer threads--at the cost of significantly
10 | // higher latencies.
11 | //
12 | // Note that Messages do not explain *which* events fail. Every event in a
13 | // message may raise an exception because of a problem with a single event.
14 | // There is no guarantee as to *which* events were successfully received. Since
15 | // Riemann considers almost every event valid, and will only return failures in
16 | // the event of resource problems, this should be acceptable for most
17 | // scenarios.
18 | //
19 | // To maximize throughput, BatchingRiemannClient is purely reactive, lockfree,
20 | // and makes no guarantees about event latency. Latency of sendEvents() may vary
21 | // widely, depending on whether or not that call flushed the buffer. You may
22 | // call flush() to force buffered events to be written. Flush is automatically
23 | // called at disconnect(). Calls to flush() may not clear the full buffer if
24 | // other threads are actively writing events.
25 | //
26 | // If you need finer-grained control over events, access the underlying client
27 | // directly. Arbitrarily many BatchingRiemannClients may operate over a single
28 | // underlying client.
29 |
30 | import java.util.concurrent.atomic.AtomicInteger;
31 | import java.util.concurrent.atomic.AtomicLong;
32 | import java.util.concurrent.LinkedTransferQueue;
33 | import java.util.ArrayList;
34 | import java.util.Arrays;
35 | import java.util.List;
36 | import io.riemann.riemann.Proto.Msg;
37 | import io.riemann.riemann.Proto.Event;
38 | import java.io.IOException;
39 |
40 | public class RiemannBatchClient implements IRiemannClient {
41 | public final int batchSize;
42 | public final AtomicInteger bufferSize = new AtomicInteger();
43 | public final LinkedTransferQueue buffer;
44 | public final IRiemannClient client;
45 |
46 | // Maximum time, in ms, we can wait for an event to be processed.
47 | public final AtomicLong readPromiseTimeout = new AtomicLong(5000);
48 |
49 | public RiemannBatchClient(final IRiemannClient client)
50 | throws UnsupportedJVMException {
51 | this(client, 10);
52 | }
53 |
54 | public RiemannBatchClient(final IRiemannClient client,
55 | final int batchSize)
56 | throws UnsupportedJVMException {
57 | this.client = client;
58 | this.batchSize = batchSize;
59 | this.buffer = new java.util.concurrent.LinkedTransferQueue();
60 | }
61 |
62 | @Override
63 | public IPromise sendMessage(final Msg message) {
64 | return client.sendMessage(message);
65 | }
66 |
67 | @Override
68 | public IPromise sendEvents(final List events) {
69 | final ChainPromise p = new ChainPromise();
70 |
71 | // Queue up all events with this IPromise.
72 | for (Event event : events) {
73 | queue(new Write(event, p));
74 | }
75 |
76 | return p;
77 | }
78 |
79 | @Override
80 | public IPromise sendEvents(final Event... events) {
81 | return sendEvents(Arrays.asList(events));
82 | }
83 |
84 | @Override
85 | public IPromise sendEvent(final Event event) {
86 | final ChainPromise p = new ChainPromise();
87 | queue(new Write(event, p));
88 | return p;
89 | }
90 |
91 | @Override
92 | public IPromise sendException(final String service, final Throwable t) {
93 | return RiemannClient.sendException(this, service, t);
94 | }
95 |
96 | @Override
97 | public IPromise> query(final String q) {
98 | return client.query(q);
99 | }
100 |
101 | @Override
102 | public EventDSL event() {
103 | return new EventDSL(this);
104 | }
105 |
106 | // Hey, I just called you
107 | // And this is crazy
108 | // But take this event
109 | // And flush queues maybe
110 | public void queue(final Write write) {
111 | buffer.put(write);
112 | if (batchSize <= bufferSize.addAndGet(1)) {
113 | flush();
114 | }
115 | }
116 |
117 | // Flushes up to batchSize writes from the queue, and fulfills their
118 | // promises. Should never throw; any exceptions go to the corresponding
119 | // promises.
120 | public int flush2() {
121 | final int maxWrites = Math.min(batchSize, bufferSize.get());
122 |
123 | // Allocate space for writes
124 | final ArrayList writes = new ArrayList(maxWrites);
125 |
126 | // Suck down elements from queue
127 | buffer.drainTo(writes, maxWrites);
128 |
129 | // Update count
130 | bufferSize.addAndGet(-1 * writes.size());
131 |
132 | // Build message
133 | final Msg.Builder message = Msg.newBuilder();
134 | for (Write write : writes) {
135 | message.addEvents(write.event);
136 | }
137 |
138 | // Send message
139 | final IPromise clientPromise = client.sendMessage(message.build());
140 |
141 | // And hook up all the response promises
142 | for (Write write : writes) {
143 | write.promise.attach(clientPromise);
144 | }
145 |
146 | try {
147 | client.flush();
148 | } catch (IOException e) {
149 | // not actually thrown by any implementation
150 | }
151 | return writes.size();
152 | }
153 |
154 | @Override
155 | public void flush() {
156 | flush2();
157 | }
158 |
159 | @Override
160 | public boolean isConnected() {
161 | return client.isConnected();
162 | }
163 |
164 | @Override
165 | public void connect() throws IOException {
166 | client.connect();
167 | }
168 |
169 | @Override
170 | public void close() {
171 | try {
172 | flush();
173 | } finally {
174 | client.close();
175 | }
176 | }
177 |
178 | @Override
179 | public void reconnect() throws IOException {
180 | client.reconnect();
181 | }
182 |
183 | // Returns the underlying client
184 | @Override
185 | public Transport transport() {
186 | return client;
187 | }
188 |
189 | // Combines an Event with a promise to fulfill when received.
190 | public class Write {
191 | public final Event event;
192 | public final ChainPromise promise;
193 |
194 | public Write(final Event event, final ChainPromise promise) {
195 | this.event = event;
196 | this.promise = promise;
197 | }
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/RiemannClient.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | import java.io.IOException;
4 | import java.net.InetSocketAddress;
5 | import java.util.Arrays;
6 | import java.util.Collections;
7 | import java.util.List;
8 |
9 | import io.riemann.riemann.Proto.Event;
10 | import io.riemann.riemann.Proto.Query;
11 | import io.riemann.riemann.Proto.Msg;
12 |
13 | // The standard client.
14 | public class RiemannClient implements IRiemannClient {
15 | // Vars
16 | public static final MsgValidator validate = new MsgValidator();
17 |
18 | // Send an exception over a client.
19 | public static IPromise sendException(final IRiemannClient client,
20 | final String service,
21 | final Throwable t) {
22 | // Format message and stacktrace
23 | final StringBuilder desc = new StringBuilder();
24 | desc.append(t.toString());
25 | desc.append("\n\n");
26 | for (StackTraceElement e : t.getStackTrace()) {
27 | desc.append(e);
28 | desc.append("\n");
29 | }
30 |
31 | // Build event and send
32 | return client.event()
33 | .service(service)
34 | .state("error")
35 | .tag("exception")
36 | .tag(t.getClass().getSimpleName())
37 | .description(desc.toString())
38 | .send();
39 | }
40 |
41 | // Wrap any transport
42 | public static RiemannClient wrap(final SynchronousTransport t) {
43 | return new RiemannClient(t);
44 | }
45 |
46 | public static RiemannClient wrap(final AsynchronousTransport t) {
47 | return new RiemannClient(t);
48 | }
49 |
50 | // TCP constructors
51 | public static RiemannClient tcp(final InetSocketAddress remoteAddress) throws IOException {
52 | return wrap(new TcpTransport(remoteAddress));
53 | }
54 |
55 | public static RiemannClient tcp(final InetSocketAddress remoteAddress, final InetSocketAddress localAddress) throws IOException {
56 | return wrap(new TcpTransport(remoteAddress,localAddress));
57 | }
58 |
59 | public static RiemannClient tcp(final String remoteHost, final int remotePort) throws IOException{
60 | return wrap(new TcpTransport(remoteHost, remotePort));
61 | }
62 |
63 | public static RiemannClient tcp(final String remoteHost, final int remotePort, final String localHost, final int localPort) throws IOException{
64 | return wrap(new TcpTransport(remoteHost, remotePort, localHost, localPort));
65 | }
66 |
67 | public static RiemannClient tcp(final String remoteHost) throws IOException {
68 | return wrap(new TcpTransport(remoteHost));
69 | }
70 |
71 | public static RiemannClient tcp(final String remoteHost, final String localHost) throws IOException {
72 | return wrap(new TcpTransport(remoteHost, localHost));
73 | }
74 |
75 | public static RiemannClient tcp(final int remotePort) throws IOException {
76 | return wrap(new TcpTransport(remotePort));
77 | }
78 |
79 | // UDP constructors
80 | // STOP REPEATING YOURSELF KYLE! STOP REPEATING YOURSELF KYLE!
81 | public static RiemannClient udp(final InetSocketAddress remoteAddress) throws IOException {
82 | return wrap(new UdpTransport(remoteAddress));
83 | }
84 |
85 | public static RiemannClient udp(final InetSocketAddress remoteAddress, final InetSocketAddress localAddress) throws IOException {
86 | return wrap(new UdpTransport(remoteAddress,localAddress));
87 | }
88 |
89 | public static RiemannClient udp(final String remoteHost, final int remotePort) throws IOException {
90 | return wrap(new UdpTransport(remoteHost, remotePort));
91 | }
92 |
93 | public static RiemannClient udp(final String remoteHost, final int remotePort, final String localHost, final int localPort) throws IOException{
94 | return wrap(new UdpTransport(remoteHost, remotePort, localHost, localPort));
95 | }
96 |
97 | public static RiemannClient udp(final String remoteHost) throws IOException {
98 | return wrap(new UdpTransport(remoteHost));
99 | }
100 |
101 | public static RiemannClient udp(final String remoteHost, final String localHost) throws IOException {
102 | return wrap(new UdpTransport(remoteHost, localHost));
103 | }
104 |
105 | public static RiemannClient udp(final int remotePort) throws IOException {
106 | return wrap(new UdpTransport(remotePort));
107 | }
108 |
109 |
110 | // Vars
111 | public volatile RiemannScheduler scheduler = null;
112 | public final AsynchronousTransport transport;
113 |
114 |
115 | // Transport constructors
116 | public RiemannClient(final SynchronousTransport t) {
117 | this(new AsynchronizeTransport(t));
118 | }
119 |
120 | public RiemannClient(final AsynchronousTransport t) {
121 | this.transport = t;
122 | }
123 |
124 |
125 | // Create a new event to send over this client
126 | @Override
127 | public EventDSL event() {
128 | return new EventDSL(this);
129 | }
130 |
131 | // Send and receive messages
132 | @Override
133 | public IPromise sendMessage(final Msg m) {
134 | return transport.sendMessage(m).map(validate);
135 | }
136 |
137 | @Override
138 | public IPromise sendEvent(final Event event) {
139 | return sendMessage(Msg.newBuilder().addEvents(event).build());
140 | }
141 |
142 | @Override
143 | public IPromise sendEvents(final Event... events) {
144 | return sendEvents(Arrays.asList(events));
145 | }
146 |
147 | @Override
148 | public IPromise sendEvents(final List events) {
149 | return sendMessage(Msg.newBuilder().addAllEvents(events).build());
150 | }
151 |
152 | @Override
153 | public IPromise sendException(final String service, final Throwable t) {
154 | return RiemannClient.sendException(this, service, t);
155 | }
156 |
157 | @Override
158 | public IPromise> query(final String q) {
159 | return sendMessage(
160 | Msg.newBuilder()
161 | .setQuery(Query.newBuilder().setString(q).build())
162 | .build())
163 | .map(new Fn2>() {
164 | public List call(final Msg m) {
165 | return Collections.unmodifiableList(m.getEventsList());
166 | }
167 | });
168 | }
169 |
170 |
171 | // Transport lifecycle
172 | @Override
173 | public Transport transport() {
174 | return transport;
175 | }
176 |
177 | @Override
178 | public void connect() throws IOException {
179 | transport.connect();
180 | }
181 |
182 | @Override
183 | public boolean isConnected() {
184 | return transport.isConnected();
185 | }
186 |
187 | @Override
188 | public void close() {
189 | transport.close();
190 | }
191 |
192 | @Override
193 | public void reconnect() throws IOException {
194 | transport.reconnect();
195 | }
196 |
197 | @Override
198 | public void flush() throws IOException {
199 | transport.flush();
200 | }
201 |
202 |
203 | // Returns the scheduler for this client. Creates the scheduler on first use.
204 | public synchronized RiemannScheduler scheduler() {
205 | if (scheduler == null) {
206 | scheduler = new RiemannScheduler(this);
207 | }
208 | return scheduler;
209 | }
210 | }
211 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/RiemannScheduler.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | import java.util.concurrent.ScheduledFuture;
4 | import java.util.concurrent.ScheduledThreadPoolExecutor;
5 | import java.util.concurrent.TimeUnit;
6 |
7 | // Supports periodic reporting of events.
8 | public class RiemannScheduler {
9 | public static abstract class Task {
10 | public abstract void run(IRiemannClient r);
11 | }
12 |
13 | public final ScheduledThreadPoolExecutor pool;
14 | public final IRiemannClient client;
15 |
16 | public RiemannScheduler(final IRiemannClient client) {
17 | this(client, 1);
18 | }
19 |
20 | // Finer control over threadpool
21 | public RiemannScheduler(final IRiemannClient client, int poolSize) {
22 | this.client = client;
23 | pool = new ScheduledThreadPoolExecutor(poolSize);
24 | pool.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
25 | pool.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
26 | }
27 |
28 | public void shutdown() {
29 | pool.shutdown();
30 | }
31 |
32 | // Converts a callback to a runnable by closing over this pool's client
33 | protected Runnable runnableCallback(final Task c) {
34 | return new Runnable() {
35 | @Override
36 | public void run() {
37 | c.run(client);
38 | }
39 | };
40 | }
41 |
42 | // Schedule an arbitrary runnable to be run periodically; useful when
43 | // you already have a client handy. Interval is in ms.
44 | public ScheduledFuture every(long interval, Runnable f) {
45 | return every(interval, 0, f);
46 | }
47 |
48 | public ScheduledFuture every(long interval, Task c) {
49 | return every(interval, runnableCallback(c));
50 | }
51 |
52 | // Schedule an arbitrary runnable to be run periodically. Adjustable
53 | // units.
54 | public ScheduledFuture every(long interval, TimeUnit unit, Runnable f) {
55 | return every(interval, 0, unit, f);
56 | }
57 |
58 | public ScheduledFuture every(long interval, TimeUnit unit, Task c) {
59 | return every(interval, unit, runnableCallback(c));
60 | }
61 |
62 | // Schedule an arbitrary runnable to be run periodically, with initial
63 | // delay. Times in ms.
64 | public ScheduledFuture every(long interval, long delay, Runnable f) {
65 | return every(interval, delay, TimeUnit.MILLISECONDS, f);
66 | }
67 |
68 | public ScheduledFuture every(long interval, long delay, Task c) {
69 | return every(interval, delay, runnableCallback(c));
70 | }
71 |
72 | // Schedule an arbitrary runnable to be run periodically, with initial
73 | // delay and adjustable units.
74 | public ScheduledFuture every(long interval, long delay, TimeUnit unit, Runnable f) {
75 | return pool.scheduleAtFixedRate(f, delay, interval, unit);
76 | }
77 |
78 | public ScheduledFuture every(long interval, long delay, TimeUnit unit, Task c) {
79 | return every(interval, delay, unit, runnableCallback(c));
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/SSL.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | import java.io.*;
4 | import java.net.InetSocketAddress;
5 | import java.security.cert.*;
6 | import java.security.Key;
7 | import java.security.KeyFactory;
8 | import java.security.KeyManagementException;
9 | import java.security.KeyPair;
10 | import java.security.KeyStore;
11 | import java.security.KeyStoreException;
12 | import java.security.NoSuchAlgorithmException;
13 | import java.security.NoSuchProviderException;
14 | import java.security.PrivateKey;
15 | import java.security.PublicKey;
16 | import java.security.spec.*;
17 | import java.security.UnrecoverableKeyException;
18 | import java.util.regex.Matcher;
19 | import java.util.regex.Pattern;
20 | import java.util.Scanner;
21 | import javax.net.ssl.*;
22 | import javax.xml.bind.DatatypeConverter;
23 |
24 | public class SSL {
25 | // You know, a mandatory password stored in memory so we can... encrypt...
26 | // data stored in memory.
27 | public static char[] keyStorePassword =
28 | "GheesBetDyPhuvwotNolofamLydMues9".toCharArray();
29 |
30 | // The X.509 certificate factory
31 | public static CertificateFactory X509CertFactory() throws
32 | CertificateException {
33 | return CertificateFactory.getInstance("X.509");
34 | }
35 |
36 | // An RSA key factory
37 | public static KeyFactory RSAKeyFactory() throws NoSuchAlgorithmException {
38 | return KeyFactory.getInstance("RSA");
39 | }
40 |
41 | // Parses a base64-encoded string to a byte array
42 | public static byte[] base64toBinary(final String string) {
43 | return DatatypeConverter.parseBase64Binary(string);
44 | }
45 |
46 | // Turns a filename into an inputstream
47 | public static FileInputStream inputStream(final String fileName) throws
48 | FileNotFoundException {
49 | return new FileInputStream(new File(fileName));
50 | }
51 |
52 | // Reads a filename into a string
53 | public static String slurp(final String file) throws FileNotFoundException {
54 | return new Scanner(new File(file)).useDelimiter("\\Z").next();
55 | }
56 |
57 | // Loads an X.509 certificate from a file.
58 | public static X509Certificate loadCertificate(final String file) throws
59 | IOException, CertificateException {
60 | FileInputStream stream = null;
61 | try {
62 | stream = inputStream(file);
63 | return (X509Certificate) X509CertFactory().generateCertificate(stream);
64 | } finally {
65 | if (stream != null) {
66 | stream.close();
67 | }
68 | }
69 | }
70 |
71 | // Loads a public key from a .crt file.
72 | public static PublicKey publicKey(final String file) throws IOException,
73 | CertificateException {
74 | return loadCertificate(file).getPublicKey();
75 | }
76 |
77 | // Loads a private key from a PKCS8 file.
78 | public static PrivateKey privateKey(final String file) throws
79 | FileNotFoundException, NoSuchAlgorithmException, InvalidKeySpecException {
80 | final Pattern p = Pattern.compile(
81 | "^-----BEGIN ?.*? PRIVATE KEY-----$(.+)^-----END ?.*? PRIVATE KEY-----$",
82 | Pattern.MULTILINE | Pattern.DOTALL);
83 |
84 | final Matcher matcher = p.matcher(slurp(file));
85 | matcher.find();
86 | return RSAKeyFactory().generatePrivate(
87 | new PKCS8EncodedKeySpec(
88 | base64toBinary(
89 | matcher.group(1))));
90 | }
91 |
92 | // Makes a KeyStore from a PKCS8 private key file, a public cert file, and the
93 | // signing CA certificate.
94 | public static KeyStore keyStore(final String keyFile,
95 | final String certFile,
96 | final String caCertFile) throws
97 | FileNotFoundException, IOException, KeyStoreException,
98 | NoSuchAlgorithmException, InvalidKeySpecException, CertificateException {
99 | final Key key = privateKey(keyFile);
100 | final Certificate cert = loadCertificate(certFile);
101 | final Certificate caCert = loadCertificate(caCertFile);
102 | final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
103 | keyStore.load(null, null);
104 | // alias, private key, password, certificate chain
105 | keyStore.setKeyEntry("key", key, keyStorePassword,
106 | new Certificate[] { cert });
107 | return keyStore;
108 | }
109 |
110 | // Makes a trust store, suitable for backing a TrustManager, out of a CA cert
111 | // file.
112 | public static KeyStore trustStore(final String caCertFile) throws
113 | KeyStoreException, IOException, NoSuchAlgorithmException,
114 | CertificateException {
115 | final KeyStore keyStore = KeyStore.getInstance("JKS");
116 | keyStore.load(null, null);
117 | keyStore.setCertificateEntry("cacert", loadCertificate(caCertFile));
118 | return keyStore;
119 | }
120 |
121 | // An X.509 trust manager for a KeyStore.
122 | public static TrustManager trustManager(final KeyStore keyStore) throws
123 | NoSuchAlgorithmException, KeyStoreException, NoSuchProviderException {
124 | final TrustManagerFactory factory =
125 | TrustManagerFactory.getInstance("SunX509", "SunJSSE");
126 | synchronized(factory) {
127 | factory.init(keyStore);
128 | for (TrustManager tm : factory.getTrustManagers()) {
129 | if (tm instanceof X509TrustManager) {
130 | return tm;
131 | }
132 | }
133 | return null;
134 | }
135 | }
136 |
137 | // An X.509 key manager for a KeyStore
138 | public static KeyManager keyManager(final KeyStore keyStore) throws
139 | NoSuchAlgorithmException, KeyStoreException, NoSuchProviderException,
140 | UnrecoverableKeyException {
141 | final KeyManagerFactory factory =
142 | KeyManagerFactory.getInstance("SunX509", "SunJSSE");
143 | synchronized(factory) {
144 | factory.init(keyStore, keyStorePassword);
145 | for (KeyManager km : factory.getKeyManagers()) {
146 | if (km instanceof X509KeyManager) {
147 | return km;
148 | }
149 | }
150 | return null;
151 | }
152 | }
153 |
154 | // Builds an SSL Context from a PKCS8 key file, a certificate file, and a
155 | // trusted CA certificate used to verify peers.
156 | public static SSLContext sslContext(final String keyFile,
157 | final String certFile,
158 | final String caCertFile) throws
159 | KeyManagementException, NoSuchAlgorithmException, FileNotFoundException,
160 | KeyStoreException, IOException, InvalidKeySpecException,
161 | CertificateException, NoSuchProviderException, UnrecoverableKeyException {
162 |
163 | final KeyManager keyManager = keyManager(
164 | keyStore(keyFile, certFile, caCertFile));
165 | final TrustManager trustManager = trustManager(trustStore(caCertFile));
166 | final SSLContext context = SSLContext.getInstance("TLS");
167 | context.init(new KeyManager[] { keyManager },
168 | new TrustManager[] { trustManager },
169 | null);
170 | return context;
171 | }
172 |
173 | public static SSLContext uncheckedSSLContext(final String keyFile, final String certFile, final String caCertFile) {
174 | // hack hack hack
175 | try {
176 | return sslContext(keyFile, certFile, caCertFile);
177 | } catch (KeyManagementException e) {
178 | throw new RuntimeException(e);
179 | } catch (NoSuchAlgorithmException e) {
180 | throw new RuntimeException(e);
181 | } catch (FileNotFoundException e) {
182 | throw new RuntimeException(e);
183 | } catch (KeyStoreException e) {
184 | throw new RuntimeException(e);
185 | } catch (IOException e) {
186 | throw new RuntimeException(e);
187 | } catch (InvalidKeySpecException e) {
188 | throw new RuntimeException(e);
189 | } catch (CertificateException e) {
190 | throw new RuntimeException(e);
191 | } catch (NoSuchProviderException e) {
192 | throw new RuntimeException(e);
193 | } catch (UnrecoverableKeyException e) {
194 | throw new RuntimeException(e);
195 | }
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/ServerError.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | public class ServerError extends RuntimeException {
4 | public final String message;
5 |
6 | public ServerError(String message) {
7 | this.message = message;
8 | }
9 |
10 | public String getMessage() {
11 | return message;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/SimpleUdpTransport.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | import java.io.IOException;
4 | import java.net.DatagramPacket;
5 | import java.net.DatagramSocket;
6 | import java.net.InetAddress;
7 | import java.net.InetSocketAddress;
8 | import java.net.UnknownHostException;
9 |
10 | import io.riemann.riemann.Proto.Msg;
11 |
12 | public class SimpleUdpTransport implements SynchronousTransport {
13 |
14 | public static final int DEFAULT_PORT = 5555;
15 |
16 | private volatile DatagramSocket socket;
17 | private volatile boolean connected = false;
18 |
19 | private final InetSocketAddress remoteAddress;
20 | private final InetSocketAddress localAddress;
21 |
22 | public SimpleUdpTransport(final InetSocketAddress remoteAddress) {
23 | this.remoteAddress = remoteAddress;
24 | this.localAddress = null;
25 | }
26 |
27 | public SimpleUdpTransport(final InetSocketAddress remoteAddress, final InetSocketAddress localAddress) {
28 | this.remoteAddress = remoteAddress;
29 | this.localAddress = localAddress;
30 | }
31 |
32 | public SimpleUdpTransport(final String host, final int port) throws IOException {
33 | this(InetSocketAddress.createUnresolved(host, port));
34 | }
35 |
36 | public SimpleUdpTransport(final String remoteHost, final int remotePort, final String localHost, final int localPort) throws IOException {
37 | this(InetSocketAddress.createUnresolved(remoteHost, remotePort),
38 | InetSocketAddress.createUnresolved(localHost, localPort));
39 | }
40 |
41 | public SimpleUdpTransport(final String host) throws IOException {
42 | this(host, DEFAULT_PORT);
43 | }
44 |
45 | public SimpleUdpTransport(final String remoteHost, final String localHost) throws IOException {
46 | this(remoteHost, DEFAULT_PORT, localHost, 0);
47 | }
48 |
49 | public SimpleUdpTransport(final int port) throws IOException {
50 | this(InetAddress.getLocalHost().getHostAddress(), port);
51 | }
52 |
53 | @Override
54 | public Msg sendMessage(final Msg msg) throws IOException {
55 | final byte[] body = msg.toByteArray();
56 | final DatagramPacket packet = new DatagramPacket(body, body.length, ensureResolved(remoteAddress));
57 | socket.send(packet);
58 |
59 | return null;
60 | }
61 |
62 | @Override
63 | public boolean isConnected() {
64 | return connected;
65 | }
66 |
67 | @Override
68 | public synchronized void connect() throws IOException {
69 | if (this.localAddress != null){
70 | socket = new DatagramSocket(ensureResolved(localAddress));
71 | }else{
72 | socket = new DatagramSocket();
73 | }
74 | connected = true;
75 | }
76 |
77 | @Override
78 | public synchronized void close() {
79 | try {
80 | socket.close();
81 | } finally {
82 | socket = null;
83 | connected = false;
84 | }
85 | }
86 |
87 | @Override
88 | public void reconnect() throws IOException {
89 | close();
90 | connect();
91 | }
92 |
93 | @Override
94 | public void flush() throws IOException {
95 | // Noop
96 | }
97 |
98 | @Override
99 | public Transport transport() {
100 | return null;
101 | }
102 |
103 | private static InetSocketAddress ensureResolved(InetSocketAddress maybeUnresolved)
104 | throws UnknownHostException {
105 | if (maybeUnresolved.isUnresolved()) {
106 | return new InetSocketAddress(
107 | InetAddress.getByName(maybeUnresolved.getHostName()), maybeUnresolved.getPort());
108 | } else {
109 | return maybeUnresolved;
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/SynchronousTransport.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | import io.riemann.riemann.Proto.Msg;
4 | import java.io.IOException;
5 |
6 | public interface SynchronousTransport extends Transport {
7 | // Sends a message and waits for its response, if possible. If the underlying
8 | // transport doesn't support receiving a response, returns null.
9 | public Msg sendMessage(final Msg msg) throws IOException;
10 | }
11 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/TcpHandler.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | import io.netty.channel.ChannelHandlerContext;
4 | import io.netty.channel.ChannelOutboundHandlerAdapter;
5 | import io.netty.channel.ChannelPromise;
6 | import io.netty.channel.CombinedChannelDuplexHandler;
7 | import io.netty.channel.SimpleChannelInboundHandler;
8 | import io.netty.util.concurrent.Future;
9 | import io.netty.util.concurrent.GenericFutureListener;
10 | import io.riemann.riemann.Proto.Msg;
11 | import java.io.IOException;
12 |
13 | public class TcpHandler
14 | extends CombinedChannelDuplexHandler {
15 |
16 | public final WriteQueue queue = new WriteQueue();
17 | public final ExceptionReporter exceptionReporter;
18 | // The last error used to fulfill outstanding promises.
19 | public volatile IOException lastError =
20 | new IOException("Channel closed.");
21 |
22 | public TcpHandler(final ExceptionReporter exceptionReporter) {
23 | this.exceptionReporter = exceptionReporter;
24 | init(new Inbound(), new Outbound());
25 | }
26 |
27 | public class Inbound extends SimpleChannelInboundHandler {
28 | @Override
29 | protected void channelRead0(ChannelHandlerContext ctx, Msg msg) {
30 | // When messages are received, deliver them to the next queued promise.
31 | queue.take().deliver(msg);
32 | }
33 |
34 | @Override
35 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
36 | // Log exceptions and close.
37 | try {
38 | exceptionReporter.reportException(cause);
39 | } catch (final Exception ee) {
40 | // Oh well
41 | }
42 |
43 | queue.close(cause);
44 | ctx.channel().close();
45 | super.exceptionCaught(ctx, cause);
46 | }
47 | }
48 |
49 | public class Outbound extends ChannelOutboundHandlerAdapter {
50 | @Override
51 | public void write(ChannelHandlerContext ctx, Object msg, final ChannelPromise channelPromise)
52 | throws Exception {
53 | // Destructure the write
54 | final Write write = (Write) msg;
55 | final Promise promise = write.promise;
56 |
57 | channelPromise.addListener(
58 | new GenericFutureListener>() {
59 | @Override
60 | public void operationComplete(Future super Void> future) throws Exception {
61 | if (future.isSuccess()) {
62 | queue.put(promise);
63 | } else if (future.cause() != null) {
64 | promise.deliver(new IOException("Write failed.", future.cause()));
65 | } else {
66 | promise.deliver(new IOException("Write failed."));
67 | }
68 | }
69 | });
70 | super.write(ctx, ((Write) msg).message, channelPromise);
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/TcpTransport.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | import io.netty.bootstrap.Bootstrap;
4 | import io.netty.channel.Channel;
5 | import io.netty.channel.ChannelFuture;
6 | import io.netty.channel.ChannelFutureListener;
7 | import io.netty.channel.ChannelInitializer;
8 | import io.netty.channel.ChannelOption;
9 | import io.netty.channel.ChannelPipeline;
10 | import io.netty.channel.EventLoopGroup;
11 | import io.netty.channel.group.ChannelGroup;
12 | import io.netty.channel.group.DefaultChannelGroup;
13 | import io.netty.channel.nio.NioEventLoopGroup;
14 | import io.netty.channel.socket.SocketChannel;
15 | import io.netty.channel.socket.nio.NioSocketChannel;
16 | import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
17 | import io.netty.handler.codec.LengthFieldPrepender;
18 | import io.netty.handler.codec.protobuf.ProtobufDecoder;
19 | import io.netty.handler.codec.protobuf.ProtobufEncoder;
20 | import io.netty.handler.ssl.SslHandler;
21 | import io.netty.util.concurrent.GlobalEventExecutor;
22 | import io.riemann.riemann.Proto.Msg;
23 | import java.io.IOException;
24 | import java.net.InetAddress;
25 | import java.net.InetSocketAddress;
26 | import java.util.concurrent.Semaphore;
27 | import java.util.concurrent.TimeUnit;
28 | import java.util.concurrent.atomic.AtomicBoolean;
29 | import java.util.concurrent.atomic.AtomicInteger;
30 | import java.util.concurrent.atomic.AtomicLong;
31 | import java.util.concurrent.atomic.AtomicReference;
32 | import javax.net.ssl.SSLContext;
33 | import javax.net.ssl.SSLEngine;
34 | import org.slf4j.Logger;
35 | import org.slf4j.LoggerFactory;
36 |
37 | public class TcpTransport implements AsynchronousTransport {
38 | // Logger
39 | public final Logger logger = LoggerFactory.getLogger(TcpTransport.class);
40 |
41 | // Shared pipeline handlers
42 | public static final ProtobufDecoder pbDecoder =
43 | new ProtobufDecoder(Msg.getDefaultInstance());
44 | public static final ProtobufEncoder pbEncoder =
45 | new ProtobufEncoder();
46 | public static final LengthFieldPrepender frameEncoder =
47 | new LengthFieldPrepender(4);
48 |
49 | public static final int DEFAULT_PORT = 5555;
50 |
51 | // I AM A STATE MUSHEEN
52 | public enum State {
53 | DISCONNECTED,
54 | CONNECTING,
55 | CONNECTED,
56 | DISCONNECTING
57 | }
58 |
59 | // STATE STATE STATE
60 | public volatile State state = State.DISCONNECTED;
61 | public final EventLoopGroup eventLoopGroup = new NioEventLoopGroup(1);
62 | public final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
63 | public volatile Bootstrap bootstrap;
64 | public volatile Semaphore writeLimiter = new Semaphore(8192);
65 |
66 | // Configuration
67 | public final AtomicBoolean autoFlush = new AtomicBoolean(true);
68 | public final AtomicInteger writeLimit = new AtomicInteger(8192);
69 | public final AtomicLong reconnectDelay = new AtomicLong(5000);
70 | public final AtomicInteger connectTimeout = new AtomicInteger(5000);
71 | public final AtomicInteger writeTimeout = new AtomicInteger(5000);
72 | public final AtomicInteger writeBufferHigh = new AtomicInteger(1024 * 64);
73 | public final AtomicInteger writeBufferLow = new AtomicInteger(1024 * 8);
74 |
75 | public final InetSocketAddress remoteAddress;
76 | public final InetSocketAddress localAddress;
77 | public final AtomicReference sslContext =
78 | new AtomicReference();
79 |
80 | public volatile ExceptionReporter exceptionReporter = new ExceptionReporter() {
81 | public void reportException(final Throwable t) {
82 | // By default, don't spam the logs.
83 | }
84 | };
85 |
86 | public void setExceptionReporter(final ExceptionReporter exceptionReporter) {
87 | this.exceptionReporter = exceptionReporter;
88 | }
89 |
90 | public TcpTransport(final InetSocketAddress remoteAddress) {
91 | this.remoteAddress = remoteAddress;
92 | this.localAddress = null;
93 | }
94 |
95 | public TcpTransport(final InetSocketAddress remoteAddress, final InetSocketAddress localAddress) {
96 | this.remoteAddress = remoteAddress;
97 | this.localAddress = localAddress;
98 | }
99 |
100 | public TcpTransport(final String remoteHost, final int remotePort) throws IOException {
101 | this(InetSocketAddress.createUnresolved(remoteHost, remotePort));
102 | }
103 |
104 | public TcpTransport(
105 | final String remoteHost, final int remotePort, final String localHost, final int localPort)
106 | throws IOException {
107 | this(
108 | InetSocketAddress.createUnresolved(remoteHost, remotePort), InetSocketAddress.createUnresolved(localHost, localPort));
109 | }
110 |
111 | public TcpTransport(final String remoteHost) throws IOException {
112 | this(remoteHost, DEFAULT_PORT);
113 | }
114 |
115 | public TcpTransport(final String remoteHost, final String localHost) throws IOException {
116 | this(remoteHost, DEFAULT_PORT, localHost, 0);
117 | }
118 |
119 | public TcpTransport(final int remotePort) throws IOException {
120 | this(InetAddress.getLocalHost().getHostAddress(), remotePort);
121 | }
122 |
123 | // Set the number of outstanding writes allowed at any time.
124 | public synchronized TcpTransport setWriteBufferLimit(final int limit) {
125 | if (isConnected()) {
126 | throw new IllegalStateException("can't modify the write buffer limit of a connected transport; please set the limit before connecting");
127 | }
128 |
129 | writeLimit.set(limit);
130 | writeLimiter = new Semaphore(limit);
131 | return this;
132 | }
133 |
134 | @Override
135 | public boolean isConnected() {
136 | // Are we in state connected?
137 | if (state != State.CONNECTED) {
138 | return false;
139 | }
140 |
141 | // Is at least one channel connected?
142 | for (Channel ch : channels) {
143 | if (ch.isOpen()) {
144 | return true;
145 | }
146 | }
147 |
148 | return false;
149 | }
150 |
151 | // Builds a new SSLHandler
152 | public SslHandler sslHandler() {
153 | final SSLContext context = sslContext.get();
154 | if (context == null) {
155 | return null;
156 | }
157 |
158 | final SSLEngine engine = context.createSSLEngine();
159 | engine.setUseClientMode(true);
160 |
161 | final SslHandler handler = new SslHandler(engine);
162 |
163 | // to disable tls renegotiation see:
164 | // https://stackoverflow.com/questions/31418644/is-it-possible-to-disable-tls-renegotiation-in-netty-4
165 |
166 | return handler;
167 | }
168 |
169 | @Override
170 | // Does nothing if not currently disconnected.
171 | public synchronized void connect() throws IOException {
172 | if (state != State.DISCONNECTED) {
173 | return;
174 | }
175 | state = State.CONNECTING;
176 |
177 | // Create bootstrap
178 | bootstrap = new Bootstrap().group(eventLoopGroup)
179 | .localAddress(localAddress)
180 | .remoteAddress(remoteAddress)
181 | .channel(NioSocketChannel.class)
182 | .handler(
183 | new ChannelInitializer() {
184 | @Override
185 | protected void initChannel(SocketChannel channel) {
186 | ChannelPipeline p = channel.pipeline();
187 | // Reconnections
188 | p.addLast(
189 | "reconnect",
190 | new ReconnectHandler(bootstrap, channels, reconnectDelay, TimeUnit.MILLISECONDS));
191 |
192 | // TLS
193 | final SslHandler sslHandler = sslHandler();
194 | if (sslHandler != null) {
195 | p.addLast("tls", sslHandler);
196 | }
197 |
198 | // Normal codec
199 | p.addLast("frame-decoder", new LengthFieldBasedFrameDecoder(
200 | Integer.MAX_VALUE, 0, 4, 0, 4));
201 | p.addLast("frame-encoder", frameEncoder);
202 | p.addLast("protobuf-decoder", pbDecoder);
203 | p.addLast("protobuf-encoder", pbEncoder);
204 | p.addLast("handler", new TcpHandler(exceptionReporter));
205 | }});
206 |
207 | // Set bootstrap options
208 | bootstrap.option(ChannelOption.TCP_NODELAY, true);
209 | bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
210 | bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeout.get());
211 | bootstrap.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, writeBufferLow.get());
212 | bootstrap.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, writeBufferHigh.get());
213 | bootstrap.localAddress(localAddress);
214 | bootstrap.remoteAddress(remoteAddress);
215 |
216 | // Connect and wait for connection ready
217 | final ChannelFuture result = bootstrap.connect();
218 | channels.add(result.channel());
219 | result.awaitUninterruptibly();
220 |
221 | // At this point we consider the client "connected"--even though the
222 | // connection may have failed. The channel will continue to initiate
223 | // reconnect attempts in the background.
224 | state = State.CONNECTED;
225 |
226 | // We'll throw an exception so users can pretend this call is synchronous
227 | // (and log errors as appropriate) but the client might succeed later.
228 | if (! result.isSuccess()) {
229 | throw new IOException("Connection failed", result.cause());
230 | }
231 | }
232 |
233 | @Override
234 | public void close() {
235 | close(false);
236 | }
237 |
238 | public synchronized void close(boolean force) {
239 | if (!(force || state == State.CONNECTED)) {
240 | return;
241 | }
242 |
243 | try {
244 | channels.close().awaitUninterruptibly();
245 | eventLoopGroup.shutdownGracefully().awaitUninterruptibly();
246 | } finally {
247 | bootstrap = null;
248 | state = State.DISCONNECTED;
249 | }
250 | }
251 |
252 | @Override
253 | public synchronized void reconnect() throws IOException {
254 | close();
255 | connect();
256 | }
257 |
258 | @Override
259 | public void flush() throws IOException {
260 | channels.flush();
261 | }
262 |
263 | // Write a message to any handler and return a promise to be fulfilled by
264 | // the corresponding response Msg.
265 | @Override
266 | public IPromise sendMessage(final Msg msg) {
267 | return sendMessage(msg, new Promise());
268 | }
269 |
270 | // Write a message to any available handler, fulfilling a specific promise.
271 | public Promise sendMessage(final Msg msg, final Promise promise) {
272 | if (state != State.CONNECTED) {
273 | promise.deliver(new IOException("client not connected"));
274 | return promise;
275 | }
276 |
277 | final Write write = new Write(msg, promise);
278 | final Semaphore limiter = writeLimiter;
279 |
280 | // Reserve a slot in the queue
281 | if (limiter.tryAcquire()) {
282 | for (Channel channel : channels) {
283 | // When the write is flushed from our local buffer, release our
284 | // limiter permit.
285 | ChannelFuture f;
286 | if (autoFlush.get()) {
287 | f = channel.writeAndFlush(write);
288 | } else {
289 | f = channel.write(write);
290 | }
291 | f.addListener(
292 | new ChannelFutureListener() {
293 | @Override
294 | public void operationComplete(ChannelFuture f) {
295 | limiter.release();
296 | }
297 | });
298 | return promise;
299 | }
300 |
301 | // No channels available, release the slot.
302 | limiter.release();
303 | promise.deliver(new IOException("no channels available"));
304 | return promise;
305 | }
306 |
307 | // Buffer's full.
308 | promise.deliver(
309 | new OverloadedException(
310 | "client write buffer is full: "
311 | + writeLimiter.availablePermits()
312 | + " / "
313 | + writeLimit.get()
314 | + " messages."));
315 | return promise;
316 | }
317 |
318 | @Override
319 | public Transport transport() {
320 | return null;
321 | }
322 | }
323 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/Transport.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | import java.io.IOException;
4 | import java.lang.AutoCloseable;
5 |
6 | // A common transport interface.
7 | public interface Transport extends AutoCloseable {
8 | boolean isConnected();
9 | void connect() throws IOException;
10 |
11 | void reconnect() throws IOException;
12 |
13 | void flush() throws IOException;
14 |
15 | // Our close should never throw.
16 | void close();
17 |
18 | Transport transport();
19 | }
20 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/UdpTransport.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | import io.netty.bootstrap.Bootstrap;
4 | import io.netty.channel.Channel;
5 | import io.netty.channel.ChannelFuture;
6 | import io.netty.channel.ChannelHandlerContext;
7 | import io.netty.channel.ChannelInboundHandlerAdapter;
8 | import io.netty.channel.ChannelInitializer;
9 | import io.netty.channel.ChannelOption;
10 | import io.netty.channel.ChannelPipeline;
11 | import io.netty.channel.DefaultEventLoopGroup;
12 | import io.netty.channel.EventLoopGroup;
13 | import io.netty.channel.group.ChannelGroup;
14 | import io.netty.channel.group.DefaultChannelGroup;
15 | import io.netty.channel.nio.NioEventLoopGroup;
16 | import io.netty.channel.socket.nio.NioDatagramChannel;
17 | import io.netty.handler.codec.protobuf.ProtobufEncoder;
18 | import io.netty.util.concurrent.GlobalEventExecutor;
19 | import io.riemann.riemann.Proto.Msg;
20 | import java.io.IOException;
21 | import java.net.InetAddress;
22 | import java.net.InetSocketAddress;
23 | import java.util.concurrent.TimeUnit;
24 | import java.util.concurrent.atomic.AtomicBoolean;
25 | import java.util.concurrent.atomic.AtomicInteger;
26 | import java.util.concurrent.atomic.AtomicLong;
27 |
28 | public class UdpTransport implements SynchronousTransport {
29 | // For writes we don't care about
30 | public static final Promise blackhole =
31 | new Promise();
32 |
33 | // Shared pipeline handlers
34 | public static final ProtobufEncoder pbEncoder = new ProtobufEncoder();
35 | public final DiscardHandler discardHandler = new DiscardHandler();
36 |
37 | public static final int DEFAULT_PORT = 5555;
38 |
39 | // I AM A STATE MUSHEEN
40 | public enum State {
41 | DISCONNECTED,
42 | CONNECTING,
43 | CONNECTED,
44 | DISCONNECTING
45 | }
46 |
47 | // STATE STATE STATE
48 | public volatile State state = State.DISCONNECTED;
49 | public volatile Bootstrap bootstrap;
50 | public final EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
51 | public final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
52 |
53 | // Configuration
54 | public final AtomicLong reconnectDelay = new AtomicLong(5000);
55 | public final AtomicLong connectTimeout = new AtomicLong(5000);
56 | // Changes to this value are applied only on reconnect.
57 | public final AtomicInteger sendBufferSize = new AtomicInteger(16384);
58 | public final AtomicBoolean autoFlush = new AtomicBoolean(true);
59 |
60 | public final InetSocketAddress remoteAddress;
61 | public final InetSocketAddress localAddress;
62 |
63 | public volatile ExceptionReporter exceptionReporter = new ExceptionReporter() {
64 | @Override
65 | public void reportException(final Throwable t) {
66 | t.printStackTrace();
67 | }
68 | };
69 |
70 | public void setExceptionReporter(final ExceptionReporter exceptionReporter) {
71 | this.exceptionReporter = exceptionReporter;
72 | }
73 |
74 | public UdpTransport(final InetSocketAddress remoteAddress) {
75 | this.remoteAddress = remoteAddress;
76 | this.localAddress = null;
77 | }
78 |
79 | public UdpTransport(final InetSocketAddress remoteAddress,final InetSocketAddress localAddress) {
80 | this.remoteAddress = remoteAddress;
81 | this.localAddress = localAddress;
82 | }
83 |
84 | public UdpTransport(final String host, final int port) throws IOException {
85 | this(new InetSocketAddress(host, port));
86 | }
87 |
88 | public UdpTransport(final String remoteHost, final int remotePort, final String localHost, final int localPort) throws IOException {
89 | this(new InetSocketAddress(remoteHost, remotePort),new InetSocketAddress(localHost, localPort) );
90 | }
91 |
92 | public UdpTransport(final String remoteHost) throws IOException {
93 | this(remoteHost, DEFAULT_PORT);
94 | }
95 |
96 | public UdpTransport(final String remoteHost, final String localHost) throws IOException {
97 | this(remoteHost, DEFAULT_PORT, localHost, 0);
98 | }
99 |
100 | public UdpTransport(final int port) throws IOException {
101 | this(InetAddress.getLocalHost().getHostAddress(), port);
102 | }
103 |
104 | @Override
105 | public boolean isConnected() {
106 | // Are we in state connected?
107 | return state == State.CONNECTED;
108 | }
109 |
110 | @Override
111 | // Does nothing if not currently disconnected.
112 | public synchronized void connect() throws IOException {
113 | if (state != State.DISCONNECTED) {
114 | return;
115 | };
116 | state = State.CONNECTING;
117 |
118 | // Create bootstrap
119 | bootstrap = new Bootstrap().group(eventLoopGroup)
120 | .channel(NioDatagramChannel.class);
121 |
122 | // Set up pipeline factory.
123 | bootstrap.handler(
124 | new ChannelInitializer() {
125 | @Override
126 | protected void initChannel(Channel ch) throws Exception {
127 | ChannelPipeline p = ch.pipeline();
128 | p.addLast("reconnect", new ReconnectHandler(
129 | bootstrap,
130 | channels,
131 | reconnectDelay,
132 | TimeUnit.MILLISECONDS));
133 | p.addLast("protobuf-encoder", pbEncoder);
134 | p.addLast("discard", discardHandler);
135 | }
136 | }
137 | );
138 |
139 | // Set bootstrap options
140 | bootstrap.remoteAddress(remoteAddress);
141 | bootstrap.localAddress(localAddress);
142 | bootstrap.option(ChannelOption.SO_SNDBUF, sendBufferSize.get());
143 |
144 | // Connect
145 | final ChannelFuture result = bootstrap.connect();
146 | channels.add(result.channel());
147 | result.awaitUninterruptibly();
148 |
149 | // Check for errors.
150 | if (! result.isSuccess()) {
151 | close(true);
152 | throw new IOException("Connection failed", result.cause());
153 | }
154 |
155 | // Done
156 | state = State.CONNECTED;
157 | }
158 |
159 | @Override
160 | public void close() {
161 | close(false);
162 | }
163 |
164 | public synchronized void close(boolean force) {
165 | if (!(force || state == State.CONNECTED)) {
166 | return;
167 | }
168 | // Close channel
169 | try {
170 | channels.close().awaitUninterruptibly();
171 | } finally {
172 |
173 | // Stop bootstrap
174 | try {
175 | eventLoopGroup.shutdownGracefully();
176 | } finally {
177 | bootstrap = null;
178 | state = State.DISCONNECTED;
179 | }
180 | }
181 | }
182 |
183 | @Override
184 | public void reconnect() throws IOException {
185 | close();
186 | connect();
187 | }
188 |
189 | // An Noop
190 | @Override
191 | public void flush() throws IOException {
192 | channels.flush();
193 | }
194 |
195 | @Override
196 | public Msg sendMessage(final Msg msg) {
197 | if (autoFlush.get()) {
198 | channels.writeAndFlush(msg);
199 | } else {
200 | channels.write(msg);
201 | }
202 |
203 | return null;
204 | }
205 |
206 | @Override
207 | public Transport transport() {
208 | return null;
209 | }
210 |
211 | public class DiscardHandler extends ChannelInboundHandlerAdapter {
212 |
213 | @Override
214 | public void channelActive(ChannelHandlerContext ctx) {
215 | ctx.channel().config().setAutoRead(false);
216 | }
217 |
218 | @Override
219 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
220 | try {
221 | exceptionReporter.reportException(cause);
222 | } catch (final Exception ee) {
223 | // Oh well
224 | } finally {
225 | try {
226 | ctx.channel().close();
227 | } catch (final Exception ee) {
228 | exceptionReporter.reportException(ee);
229 | }
230 | }
231 | }
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/UnsupportedJVMException.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | // Throw when we can't find an implementation of a particular class
4 | public class UnsupportedJVMException extends Exception {
5 | }
6 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/Write.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | import io.riemann.riemann.Proto.Msg;
4 |
5 | public class Write {
6 | public final Msg message;
7 | public final Promise promise;
8 |
9 | public Write(final Msg message, final Promise promise) {
10 | this.message = message;
11 | this.promise = promise;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/java/io/riemann/riemann/client/WriteQueue.java:
--------------------------------------------------------------------------------
1 | package io.riemann.riemann.client;
2 |
3 | import io.riemann.riemann.Proto.Msg;
4 | import java.io.*;
5 | import java.util.concurrent.LinkedBlockingQueue;
6 |
7 | // A synchronized FIFO queue intended to track the outstanding writes
8 | // associated with a TCP connection. Calling close() flushes the queue
9 | // atomically and prevents future writes. Open() allows writes to be enqueued
10 | // again.
11 | public class WriteQueue {
12 | public boolean isOpen = true;
13 | public volatile int size = 0;
14 | public final LinkedBlockingQueue> queue =
15 | new LinkedBlockingQueue>();
16 |
17 | public synchronized void open() {
18 | isOpen = true;
19 | size = 0;
20 | }
21 |
22 | // Returns the number of promises cleared.
23 | public synchronized void close(Throwable t) {
24 | isOpen = false;
25 |
26 | // Deliver exceptions to all outstanding promises.
27 | final IOException ex = new IOException("channel closed", t);
28 | Promise promise;
29 | while ((promise = queue.poll()) != null) {
30 | promise.deliver(ex);
31 | }
32 |
33 | size = 0;
34 | }
35 |
36 | public int size() {
37 | return this.size;
38 | }
39 |
40 | public synchronized void put(final Promise p) throws InterruptedException {
41 | if (isOpen) {
42 | try {
43 | queue.put(p);
44 | size++;
45 | } catch (RuntimeException e) {
46 | size = queue.size();
47 | throw e;
48 | } catch (InterruptedException e) {
49 | size = queue.size();
50 | throw e;
51 | }
52 | } else {
53 | p.deliver(new IOException("Channel closed."));
54 | }
55 | }
56 |
57 | public synchronized Promise take() {
58 | try {
59 | final Promise p = queue.take();
60 | size--;
61 | return p;
62 | } catch (RuntimeException e) {
63 | size = queue.size();
64 | throw e;
65 | } catch (InterruptedException e) {
66 | size = queue.size();
67 | throw new RuntimeException(e);
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/riemann-java-client/src/main/proto/riemann/proto.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto2";
2 |
3 | option java_package = "io.riemann.riemann";
4 | option java_outer_classname = "Proto";
5 |
6 | // Deprecated; state was used by early versions of the protocol, but not any
7 | // more.
8 | message State {
9 | optional int64 time = 1;
10 | optional string state = 2;
11 | optional string service = 3;
12 | optional string host = 4;
13 | optional string description = 5;
14 | optional bool once = 6;
15 | repeated string tags = 7;
16 | optional float ttl = 8;
17 | }
18 |
19 | message Event {
20 | optional int64 time = 1;
21 | optional string state = 2;
22 | optional string service = 3;
23 | optional string host = 4;
24 | optional string description = 5;
25 | repeated string tags = 7;
26 | optional float ttl = 8;
27 | repeated Attribute attributes = 9;
28 |
29 | optional int64 time_micros = 10;
30 | optional sint64 metric_sint64 = 13;
31 | optional double metric_d = 14;
32 | optional float metric_f = 15;
33 | }
34 |
35 | message Query {
36 | optional string string = 1;
37 | }
38 |
39 | message Msg {
40 | optional bool ok = 2;
41 | optional string error = 3;
42 | repeated State states = 4;
43 | optional Query query = 5;
44 | repeated Event events = 6;
45 | }
46 |
47 | message Attribute {
48 | required string key = 1;
49 | optional string value = 2;
50 | }
51 |
--------------------------------------------------------------------------------
/riemann-java-client/src/test/java/riemann/java/client/tests/BatchClientPromiseTest.java:
--------------------------------------------------------------------------------
1 | package riemann.java.client.tests;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.Assert.assertTrue;
5 | import org.junit.Test;
6 |
7 | import java.io.IOException;
8 | import java.util.*;
9 | import java.util.concurrent.TimeUnit;
10 |
11 | import io.riemann.riemann.client.IPromise;
12 | import io.riemann.riemann.client.IRiemannClient;
13 | import io.riemann.riemann.client.RiemannBatchClient;
14 | import io.riemann.riemann.client.RiemannClient;
15 | import io.riemann.riemann.client.ServerError;
16 | import io.riemann.riemann.client.UnsupportedJVMException;
17 |
18 | import io.riemann.riemann.Proto.Event;
19 | import io.riemann.riemann.Proto.Msg;
20 |
21 | public class BatchClientPromiseTest {
22 | @Test
23 | public void sendEventsTest() throws Exception, IOException, InterruptedException, ServerError, UnsupportedJVMException {
24 | final ArrayList events = new ArrayList();
25 | final ArrayList> promises = new ArrayList>();
26 |
27 | final Server server = new OkServer();
28 | IRiemannClient client = null;
29 |
30 | final int BATCH_SIZE = 10;
31 | final int NUM_EVENTS = 15;
32 | try {
33 | client = new RiemannBatchClient(RiemannClient.tcp(server.start()),
34 | BATCH_SIZE);
35 | client.connect();
36 | {
37 | final Event e = Util.createEvent();
38 | events.add(e);
39 | IPromise promise = client.sendEvent(e);
40 | // First event should be sitting in the buffer, not sent yet.
41 | assertEquals(null, promise.deref(10, (Object) null));
42 | promises.add(promise);
43 | }
44 | for (int i = 1; i < NUM_EVENTS; i++) {
45 | final Event e = Util.createEvent();
46 | events.add(e);
47 | promises.add(client.sendEvent(e));
48 | }
49 | client.flush();
50 | for (int i = 0; i < events.size(); i++) {
51 | Msg rsp = promises.get(i).deref(100, TimeUnit.MILLISECONDS,
52 | Msg.newBuilder().setOk(false).build());
53 | assertTrue(!rsp.hasOk() || rsp.getOk());
54 | }
55 | for (int i = 0; i < events.size(); ) {
56 | final int expecting = Math.min(events.size() - i, BATCH_SIZE);
57 | Msg recv = server.received.poll();
58 | assertEquals(expecting, recv.getEventsCount());
59 | for (int j = 0; j < expecting; i++, j++) {
60 | assertEquals(events.get(i), recv.getEvents(j));
61 | }
62 | }
63 | } finally {
64 | if (client != null) {
65 | client.close();
66 | }
67 | server.stop();
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/riemann-java-client/src/test/java/riemann/java/client/tests/ChainPromiseTest.java:
--------------------------------------------------------------------------------
1 | package riemann.java.client.tests;
2 |
3 | import io.riemann.riemann.client.ChainPromise;
4 | import io.riemann.riemann.client.Promise;
5 | import java.lang.Runnable;
6 | import java.lang.Thread;
7 | import org.junit.Before;
8 | import org.junit.Test;
9 | import static org.junit.Assert.assertEquals;
10 | import java.io.IOException;
11 |
12 | public class ChainPromiseTest {
13 |
14 | private ChainPromise cp;
15 |
16 | @Before
17 | public void setUp() {
18 | cp = new ChainPromise();
19 | }
20 |
21 | @Test
22 | public void singleTest() throws IOException {
23 | // A previous version of ChainPromise didn't properly attach the
24 | // inner promise, this problem was visible in
25 | // BatchClientPromiseTest
26 | Promise p = new Promise();
27 | cp.attach(p);
28 | p.deliver("foo");
29 | assertEquals("foo", cp.deref(10, (Object) null));
30 | }
31 |
32 | @Test
33 | public void threadTest() throws IOException {
34 | final Promise p = new Promise();
35 | new Thread(new Runnable() {
36 | public void run() {
37 | try {
38 | Thread.sleep(100);
39 | } catch (InterruptedException e) {
40 | System.out.println("interrupted");
41 | }
42 | p.deliver("bar");
43 | }
44 | }).start();
45 | new Thread(new Runnable() {
46 | public void run() {
47 | try {
48 | Thread.sleep(100);
49 | } catch (InterruptedException e) {
50 | System.out.println("interrupted");
51 | }
52 | cp.attach(p);
53 | }
54 | }).start();
55 | assertEquals("bar", p.deref(200, (Object) null));
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/riemann-java-client/src/test/java/riemann/java/client/tests/EchoServer.java:
--------------------------------------------------------------------------------
1 | package riemann.java.client.tests;
2 |
3 | import io.riemann.riemann.Proto.Msg;
4 |
5 | public class EchoServer extends Server {
6 | final long delay;
7 |
8 | public EchoServer() {
9 | this(0);
10 | }
11 |
12 | public EchoServer(final long delay) {
13 | this.delay = delay;
14 | }
15 |
16 | public Msg handle(final Msg m) {
17 | if (0 < delay) {
18 | try {
19 | Thread.sleep(delay);
20 | } catch (InterruptedException e) {
21 | }
22 | }
23 | //if (0 < m.getEventsCount()) {
24 | // System.out.println("Server: " + m.getEvents(0).getMetricSint64());
25 | //}
26 | return m;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/riemann-java-client/src/test/java/riemann/java/client/tests/EventBuilderTest.java:
--------------------------------------------------------------------------------
1 | package riemann.java.client.tests;
2 |
3 | import com.google.protobuf.ProtocolStringList;
4 | import io.riemann.riemann.Proto;
5 | import io.riemann.riemann.client.EventBuilder;
6 | import org.junit.Test;
7 |
8 | import java.util.*;
9 |
10 | import static org.junit.Assert.assertEquals;
11 |
12 | public class EventBuilderTest {
13 |
14 | @Test
15 | public void should_create_event() {
16 | Map attributes = new HashMap();
17 | attributes.put("network_zone", "dmz");
18 | List tags = new ArrayList();
19 | tags.add("fast");
20 | Proto.Event event = new EventBuilder()
21 | .host("riemann.localhost")
22 | .metric(42.2)
23 | .service("cpu_percent_usage")
24 | .state("fine")
25 | .tags("high", "backend")
26 | .tag("storage")
27 | .tags(tags)
28 | .description("Processor usage")
29 | .attribute("health", "good")
30 | .attribute("environment", "production")
31 | .attribute("datacenter", "eu_west")
32 | .attributes(attributes)
33 | .ttl(60)
34 | .build();
35 |
36 | assertEquals("cpu_percent_usage", event.getService());
37 | assertEquals("riemann.localhost", event.getHost());
38 | assertEquals("fine", event.getState());
39 | assertEquals(42.2, event.getMetricD(), 0D);
40 | ProtocolStringList eventTags = event.getTagsList();
41 | assertEquals(4, eventTags.size());
42 | assertEquals("high", eventTags.get(0));
43 | assertEquals("backend", eventTags.get(1));
44 | assertEquals("storage", eventTags.get(2));
45 | assertEquals("fast", eventTags.get(3));
46 | assertEquals("Processor usage", event.getDescription());
47 | List eventAttributes = getAttributesSortedByKey(event);
48 | assertEquals(4, eventAttributes.size());
49 | assertEqualsAttribute("datacenter", "eu_west", eventAttributes.get(0));
50 | assertEqualsAttribute("environment", "production", eventAttributes.get(1));
51 | assertEqualsAttribute("health", "good", eventAttributes.get(2));
52 | assertEqualsAttribute("network_zone", "dmz", eventAttributes.get(3));
53 | assertEquals(60F, event.getTtl(), 0F);
54 | }
55 |
56 | @Test
57 | public void should_create_event_with_metric_float() {
58 | Proto.Event event = new EventBuilder()
59 | .metric(123.4F)
60 | .build();
61 |
62 | assertEquals(123.4F, event.getMetricF(), 0F);
63 | }
64 |
65 | @Test
66 | public void should_create_event_with_metric_int() {
67 | Proto.Event event = new EventBuilder()
68 | .metric(123)
69 | .build();
70 |
71 | assertEquals(123, event.getMetricSint64());
72 | }
73 |
74 | @Test
75 | public void should_create_event_with_metric_short() {
76 | Proto.Event event = new EventBuilder()
77 | .metric((short) 1)
78 | .build();
79 |
80 | assertEquals(1, event.getMetricSint64());
81 | }
82 |
83 | @Test
84 | public void should_create_event_with_metric_long() {
85 | Proto.Event event = new EventBuilder()
86 | .metric(1234567891011L)
87 | .build();
88 |
89 | assertEquals(1234567891011L, event.getMetricSint64());
90 | }
91 |
92 | @Test
93 | public void should_create_event_with_metric_byte() {
94 | Proto.Event event = new EventBuilder()
95 | .metric((byte) 1)
96 | .build();
97 |
98 | assertEquals(1, event.getMetricSint64());
99 | }
100 |
101 | @Test
102 | public void should_clear_metric() {
103 | EventBuilder eventBuilder = new EventBuilder();
104 | Proto.Event event = eventBuilder
105 | .host("riemann.localhost")
106 | .metric(123)
107 | .metric()
108 | .build();
109 |
110 | assertEquals(false, event.hasMetricD());
111 | assertEquals(false, event.hasMetricF());
112 | assertEquals(false, event.hasMetricSint64());
113 | assertEquals(0D, event.getMetricD(), 0D);
114 | assertEquals(0F, event.getMetricF(), 0F);
115 | assertEquals(0L, event.getMetricSint64(), 0L);
116 | }
117 |
118 | @Test
119 | public void should_clear_time() {
120 | EventBuilder eventBuilder = new EventBuilder();
121 | Proto.Event event = eventBuilder
122 | .host("riemann.localhost")
123 | .time(System.currentTimeMillis())
124 | .time()
125 | .build();
126 |
127 | assertEquals(false, event.hasTime());
128 | assertEquals(0L, event.getTime());
129 | assertEquals(0L, event.getTimeMicros());
130 | }
131 |
132 | @Test
133 | public void should_clear_ttl() {
134 | EventBuilder eventBuilder = new EventBuilder();
135 | Proto.Event event = eventBuilder
136 | .host("riemann.localhost")
137 | .ttl(20)
138 | .ttl()
139 | .build();
140 |
141 | assertEquals(false, event.hasTtl());
142 | assertEquals(0F, event.getTtl(), 0F);
143 | }
144 |
145 | private List getAttributesSortedByKey(Proto.Event event) {
146 | List attributes = new ArrayList(event.getAttributesList());
147 | Collections.sort(attributes, new Comparator() {
148 | @Override
149 | public int compare(Proto.Attribute o1, Proto.Attribute o2) {
150 | return o1.getKey().compareTo(o2.getKey());
151 | }
152 | });
153 | return attributes;
154 | }
155 |
156 | private void assertEqualsAttribute(String expectedKey, String expectedValue, Proto.Attribute actual) {
157 | assertEquals(expectedKey, actual.getKey());
158 | assertEquals(expectedValue, actual.getValue());
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/riemann-java-client/src/test/java/riemann/java/client/tests/OkServer.java:
--------------------------------------------------------------------------------
1 | package riemann.java.client.tests;
2 |
3 | import io.riemann.riemann.Proto.Msg;
4 |
5 | public class OkServer extends Server {
6 | public Msg handle(final Msg m) {
7 | return Msg.newBuilder().build();
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/riemann-java-client/src/test/java/riemann/java/client/tests/PromiseTest.java:
--------------------------------------------------------------------------------
1 | package riemann.java.client.tests;
2 |
3 | import io.riemann.riemann.client.Promise;
4 | import java.lang.Runnable;
5 | import java.lang.Thread;
6 | import java.util.concurrent.TimeUnit;
7 | import org.junit.Before;
8 | import org.junit.Test;
9 | import static org.junit.Assert.assertEquals;
10 | import java.io.IOException;
11 |
12 | public class PromiseTest {
13 |
14 | private Promise p;
15 |
16 | @Before
17 | public void setUp() {
18 | p = new Promise();
19 | }
20 |
21 | @Test
22 | public void singleTest() throws IOException {
23 | p.deliver("foo");
24 | assertEquals("foo", p.deref());
25 | }
26 |
27 | @Test
28 | public void threadTest() throws IOException {
29 | new Thread(new Runnable() {
30 | public void run() {
31 | try {
32 | Thread.sleep(100);
33 | } catch (InterruptedException e) {
34 | System.out.println("interrupted");
35 | }
36 | p.deliver("bar");
37 | }
38 | }).start();
39 | assertEquals("bar", p.deref());
40 | }
41 |
42 | @Test
43 | public void timeoutTest() throws IOException {
44 | assertEquals(null, p.deref(1, TimeUnit.MILLISECONDS));
45 | assertEquals("failed", p.deref(1, TimeUnit.MILLISECONDS, "failed"));
46 |
47 | new Thread(new Runnable() {
48 | public void run() {
49 | try {
50 | Thread.sleep(100);
51 | } catch (InterruptedException e) {
52 | System.out.println("interrupted");
53 | }
54 | p.deliver("done");
55 | }
56 | }).start();
57 | assertEquals("not yet", p.deref(50, TimeUnit.MILLISECONDS, "not yet"));
58 | assertEquals("done", p.deref(100, TimeUnit.SECONDS));
59 | }
60 |
61 | @Test
62 | public void runtimeExceptionTest() throws IOException {
63 | RuntimeException thrown = null;
64 | p.deliver(new RuntimeException("fail"));
65 | try {
66 | p.deref();
67 | } catch (RuntimeException e) {
68 | thrown = e;
69 | }
70 | assertEquals("fail", thrown.getMessage());
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/riemann-java-client/src/test/java/riemann/java/client/tests/Server.java:
--------------------------------------------------------------------------------
1 | package riemann.java.client.tests;
2 |
3 | import java.util.Random;
4 | import java.util.concurrent.LinkedBlockingQueue;
5 | import java.net.*;
6 | import java.io.*;
7 |
8 | import io.riemann.riemann.client.RiemannClient;
9 | import io.riemann.riemann.Proto.Attribute;
10 | import io.riemann.riemann.Proto.Event;
11 | import io.riemann.riemann.Proto.Msg;
12 |
13 | public abstract class Server {
14 | public int port;
15 | public Thread thread;
16 | public ServerSocket serverSocket;
17 | public LinkedBlockingQueue received = new LinkedBlockingQueue();
18 |
19 | public InetSocketAddress start() throws IOException {
20 | this.serverSocket = new ServerSocket(0);
21 | this.port = serverSocket.getLocalPort();
22 | this.serverSocket.setReceiveBufferSize(100);
23 | this.thread = mainThread(this.serverSocket);
24 | this.thread.setPriority(10);
25 | this.thread.start();
26 |
27 | return new InetSocketAddress(InetAddress.getLocalHost(), this.port);
28 | }
29 |
30 | public void stop() {
31 | if (this.thread != null) {
32 | this.thread.interrupt();
33 | this.thread = null;
34 | }
35 | this.port = -1;
36 | this.serverSocket = null;
37 | }
38 |
39 | public Thread mainThread(final ServerSocket serverSocket) {
40 | return new Thread() {
41 | @Override
42 | public void run() {
43 | try {
44 | Socket sock = null;
45 | DataOutputStream out = null;
46 | DataInputStream in = null;
47 |
48 | try {
49 | // Accept connection
50 | while((sock = serverSocket.accept()) != null) {
51 | // Set up streams
52 | out = new DataOutputStream(sock.getOutputStream());
53 | in = new DataInputStream(sock.getInputStream());
54 |
55 | // Over each message
56 | while (true) {
57 | // Read length
58 | final int len = in.readInt();
59 |
60 | // Read message
61 | final byte[] data = new byte[len];
62 | in.readFully(data);
63 | final Msg request = Msg.parseFrom(data);
64 |
65 | // Log request
66 | received.put(request);
67 |
68 | // Handle message
69 | final Msg response = handle(request);
70 |
71 | // Write response
72 | out.writeInt(response.getSerializedSize());
73 | response.writeTo(out);
74 | }
75 | }
76 | } catch (SocketException e) {
77 | // Socket closed.
78 | } catch (EOFException e) {
79 | // Socket closed.
80 | } finally {
81 | if (out != null) { out.close(); }
82 | if (in != null) { in.close(); }
83 | if (sock != null) { sock.close(); }
84 | }
85 | } catch (Exception e) {
86 | e.printStackTrace();
87 | }
88 | }
89 | };
90 | }
91 |
92 | public abstract Msg handle(final Msg m);
93 | }
94 |
--------------------------------------------------------------------------------
/riemann-java-client/src/test/java/riemann/java/client/tests/SimpleUdpClientTest.java:
--------------------------------------------------------------------------------
1 | package riemann.java.client.tests;
2 |
3 | import io.riemann.riemann.Proto;
4 | import io.riemann.riemann.client.RiemannClient;
5 | import io.riemann.riemann.client.ServerError;
6 | import io.riemann.riemann.client.SimpleUdpTransport;
7 | import org.junit.Test;
8 |
9 | import java.io.IOException;
10 | import java.net.DatagramPacket;
11 | import java.net.DatagramSocket;
12 | import java.net.InetSocketAddress;
13 |
14 | import static org.junit.Assert.assertEquals;
15 | import static org.junit.Assert.assertFalse;
16 | import static org.junit.Assert.assertTrue;
17 | import static org.junit.Assert.fail;
18 |
19 | public class SimpleUdpClientTest {
20 | @Test
21 | public void sendEventsTest() throws IOException, InterruptedException, ServerError {
22 | final DatagramSocket serverSocket = new DatagramSocket();
23 | try {
24 | final RiemannClient client = new RiemannClient(new SimpleUdpTransport(serverSocket.getLocalPort()));
25 | try {
26 | client.connect();
27 | sendTestMessages(serverSocket, client);
28 | } finally {
29 | client.close();
30 | }
31 | } finally {
32 | serverSocket.close();
33 | }
34 | }
35 |
36 | @Test
37 | public void sendEventsOverReconnectionTest() throws IOException, InterruptedException, ServerError {
38 | DatagramSocket serverSocket = new DatagramSocket();
39 | final int port = serverSocket.getLocalPort();
40 | try {
41 | final RiemannClient client = new RiemannClient(new SimpleUdpTransport(serverSocket.getLocalPort()));
42 | try {
43 | client.connect();
44 | assertTrue(client.isConnected());
45 | sendTestMessages(serverSocket, client);
46 |
47 | // Close listening socket
48 | serverSocket.close();
49 |
50 | // Expect send to drop messages silently
51 | final Proto.Event e = Util.createEvent();
52 | client.sendEvent(e);
53 |
54 | // Reopen listening socket
55 | serverSocket = new DatagramSocket(new InetSocketAddress(port));
56 |
57 | // Expect sent messages to be received again
58 | sendTestMessages(serverSocket, client);
59 |
60 | } finally {
61 | client.close();
62 | assertFalse(client.isConnected());
63 | }
64 | } finally {
65 | serverSocket.close();
66 | }
67 | }
68 |
69 | private void sendTestMessages(final DatagramSocket serverSocket, final RiemannClient client) throws IOException {
70 | for (int i = 0; i < 10; i++) {
71 | final byte[] buffer = new byte[16384];
72 | final DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
73 | final Proto.Event e = Util.createEvent();
74 | client.sendEvent(e);
75 | serverSocket.receive(packet);
76 | final Proto.Msg msg = Proto.Msg.getDefaultInstance().newBuilderForType().mergeFrom(packet.getData(), 0, packet.getLength()).build();
77 | assertEquals(e, Util.soleEvent(msg));
78 | }
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/riemann-java-client/src/test/java/riemann/java/client/tests/TcpClientTest.java:
--------------------------------------------------------------------------------
1 |
2 | package riemann.java.client.tests;
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.Assert.assertTrue;
5 | import static org.junit.Assert.fail;
6 |
7 | import io.riemann.riemann.Proto.Event;
8 | import io.riemann.riemann.Proto.Msg;
9 | import io.riemann.riemann.client.IPromise;
10 | import io.riemann.riemann.client.IRiemannClient;
11 | import io.riemann.riemann.client.OverloadedException;
12 | import io.riemann.riemann.client.RiemannClient;
13 | import io.riemann.riemann.client.ServerError;
14 | import io.riemann.riemann.client.TcpTransport;
15 | import java.io.IOException;
16 | import java.util.ArrayList;
17 | import java.util.List;
18 | import java.util.concurrent.Semaphore;
19 | import java.util.concurrent.TimeUnit;
20 | import org.junit.Test;
21 |
22 | public class TcpClientTest {
23 | @Test
24 | public void sendEventsTest() throws IOException, InterruptedException, ServerError {
25 | final Server server = new OkServer();
26 | RiemannClient client = null;
27 | try {
28 | client = RiemannClient.tcp(server.start());
29 | client.connect();
30 | for (int i = 0; i < 10; i++) {
31 | final Event e = Util.createEvent();
32 | final Msg rsp = client.sendEvent(e).deref();
33 | assertEquals(true, !rsp.hasOk() || rsp.getOk());
34 | assertEquals(e, Util.soleEvent(server.received.poll()));
35 | }
36 | } finally {
37 | if (client != null) {
38 | client.close();
39 | }
40 | server.stop();
41 | }
42 | }
43 |
44 | @Test
45 | public void queryTest() throws IOException, InterruptedException, ServerError {
46 | final Server server = new EchoServer();
47 | RiemannClient client = null;
48 | try {
49 | client = RiemannClient.tcp(server.start());
50 | client.connect();
51 | for (int i = 0; i < 10; i++) {
52 | final List events = client.query("hi").deref();
53 | assertEquals(0, events.size());
54 | final Msg m = server.received.poll();
55 | assertEquals("hi", m.getQuery().getString());
56 | }
57 | } finally {
58 | if (client != null) {
59 | client.close();
60 | }
61 | server.stop();
62 | }
63 | }
64 |
65 |
66 | @Test
67 | public void limiterLeakTest() throws IOException {
68 | final long delay = 0; // Server time to process a message
69 | final Server server = new EchoServer(delay);
70 | IRiemannClient client = null;
71 |
72 | try {
73 | client = RiemannClient.tcp(server.start());
74 | TcpTransport transport = (TcpTransport) client.transport();
75 | int limit = 2;
76 | transport.writeLimiter = new Semaphore(limit);
77 | // Simulate the case where the server gets down after the client
78 | // connected and thus no channel is available
79 | transport.state = TcpTransport.State.CONNECTED;
80 |
81 | for (int i = 0; i < limit; i++) {
82 | try {
83 | client.event().service("foo").metric(1).send().deref();
84 | fail("Should throw IOException when there is no channel available");
85 | } catch (IOException e) {
86 | assertEquals("no channels available", e.getMessage());
87 | }
88 | }
89 |
90 | transport.state = TcpTransport.State.DISCONNECTED;
91 | client.connect();
92 |
93 | // Should not throw OverloadedException.
94 | client.event().service("foo").metric(1).send().deref();
95 | } finally {
96 | if (client != null) {
97 | client.close();
98 | }
99 | server.stop();
100 | }
101 | }
102 |
103 | @Test
104 | public void overloadTest() throws IOException, InterruptedException {
105 | boolean debug = true;
106 | // Milliseconds
107 | final long delay = 5; // Server time to process a message
108 | final long fast = 1; // Async latencies
109 | final double slow = ((double) delay) * 0.95; // Backpressure latencies
110 |
111 | final Server server = new EchoServer(delay);
112 | IRiemannClient client = null;
113 |
114 | try {
115 | client = RiemannClient.tcp(server.start());
116 | ((TcpTransport) client.transport()).setWriteBufferLimit(5);
117 | client.connect();
118 |
119 | final int n = 5000;
120 | final List> responses = new ArrayList>(n);
121 | long latency;
122 | long t0;
123 |
124 | // Warm up the server
125 | for (int j = 0; j < 100; j++) {
126 | responses.add(client.event().service("slow").metric(j).send());
127 | }
128 | for (IPromise response : responses) {
129 | try {
130 | response.deref();
131 | } catch (Exception e) {
132 | // NO-OP
133 | }
134 | }
135 | responses.clear();
136 |
137 | // Queue up a bunch of writes
138 | for (int i = 0; i < n; i++) {
139 | // Measure the time it takes to call .send()
140 | t0 = System.nanoTime();
141 | responses.add(client.event().service("slow").metric(i).send());
142 | latency = System.nanoTime() - t0;
143 | assertTrue(latency <= 100000000);
144 |
145 | if (i % 2 == 0) {
146 | Thread.sleep(1);
147 | }
148 | if (debug && i % 100 == 0) {
149 | System.out.println(i + " sent out of " + n);
150 | }
151 | }
152 |
153 | // Deref all and spew out success/failure pairs
154 | // 0: success
155 | // 1: timeout
156 | // 2: overload
157 | // 3: other
158 | final ArrayList results = new ArrayList();
159 | int state = -1;
160 | int count = 0;
161 | long deadline = System.currentTimeMillis() + 2000;
162 | for (IPromise response : responses) {
163 | try {
164 | if (null == response.deref(deadline - System.currentTimeMillis(),
165 | TimeUnit.MILLISECONDS)) {
166 | // Timeout
167 | if (state == -1) {
168 | state = 1;
169 | } else if (state != 1) {
170 | results.add(new int[]{state, count});
171 | state = 1;
172 | }
173 | } else {
174 | // OK
175 | if (state == -1) {
176 | state = 0;
177 | } else if (state != 0) {
178 | results.add(new int[]{state, count});
179 | state = 0;
180 | count = 0;
181 | }
182 | }
183 | } catch (OverloadedException e) {
184 | // Not OK
185 | if (state == -1) {
186 | state = 2;
187 | } else if (state != 2) {
188 | results.add(new int[]{state, count});
189 | state = 2;
190 | count = 0;
191 | }
192 | } catch (Exception e) {
193 | // Huh?
194 | if (state == -1) {
195 | state = 3;
196 | } else if (state != 3) {
197 | results.add(new int[]{state, count});
198 | state = 3;
199 | count = 0;
200 | }
201 | }
202 | count++;
203 | }
204 |
205 | // Print outcomes
206 | if (debug) {
207 | for (int[] res : results) {
208 | if (res[0] == 0) {
209 | System.out.println("ok\t\t" + res[1]);
210 | } else if (res[0] == 1) {
211 | System.out.println("timeout\t" + res[1]);
212 | } else if (res[0] == 2) {
213 | System.out.println("overload\t" + res[1]);
214 | } else {
215 | System.out.println("other\t\t" + res[1]);
216 | }
217 | }
218 | }
219 |
220 | // OKs should come first
221 | assertTrue(0 == results.get(0)[0]);
222 | // Should be a lot of OKs
223 | int initialOkCount = results.get(0)[1];
224 | System.out.println("Count of initial oks: " + initialOkCount);
225 | assertTrue(10 < results.get(0)[1]);
226 |
227 | // Tally up totals
228 | int[] counts = new int[4];
229 | for (int[] res : results) {
230 | counts[res[0]] += res[1];
231 | }
232 |
233 | // Should see both overloads and timeouts
234 | assertTrue("No Overloads", 0 < counts[1]);
235 | assertTrue("No timeouts", 0 < counts[2]);
236 |
237 | // No others
238 | assertTrue(counts[3] == 0);
239 | } finally {
240 | if (client != null) {
241 | client.close();
242 | }
243 | server.stop();
244 | }
245 | }
246 | }
247 |
--------------------------------------------------------------------------------
/riemann-java-client/src/test/java/riemann/java/client/tests/Util.java:
--------------------------------------------------------------------------------
1 | package riemann.java.client.tests;
2 |
3 | import io.riemann.riemann.Proto.Attribute;
4 | import io.riemann.riemann.Proto.Event;
5 | import io.riemann.riemann.Proto.Msg;
6 | import java.util.*;
7 | import org.junit.Test;
8 | import static org.junit.Assert.assertEquals;
9 | import static org.junit.Assert.fail;
10 |
11 | public class Util {
12 | public static Event createEvent() {
13 | final Random random = new Random();
14 | return Event.newBuilder()
15 | .setDescription("desc")
16 | .setHost("127.0.0.1")
17 | .setService("service")
18 | .setTime(random.nextInt(1000))
19 | .setTtl(random.nextInt(1000))
20 | .setMetricF(random.nextInt(1000))
21 | .addAllTags(Arrays.asList("tag1", "tag2"))
22 | .addAttributes(Attribute.newBuilder().setKey("key1").setValue("value1"))
23 | .build();
24 | }
25 |
26 | // Returns a single event from a message, asserting that it has only one
27 | // event.
28 | public static Event soleEvent(final Msg m) {
29 | assertEquals(1, m.getEventsCount());
30 | return m.getEvents(0);
31 | }
32 |
33 | public static void compareEvents(final Event e1, final Event e2) {
34 | assertEquals(e1.getHost(), e2.getHost());
35 | assertEquals(e1.getService(), e2.getService());
36 | assertEquals(e1.getState(), e2.getState());
37 | assertEquals(e1.getDescription(), e2.getDescription());
38 | assertEquals(e1.getMetricF(), e2.getMetricF(), 0);
39 | assertEquals(e1.getTime(), e2.getTime(), 0);
40 | assertEquals(e1.getTtl(), e2.getTtl(), 0);
41 | assertEquals(e1.getTagsList(), e2.getTagsList());
42 | assertEquals(e1.getAttributesList(), e2.getAttributesList());
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/scripts/install-protobuf.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -ex
3 | if [ ! -f "$HOME/bin/protoc" ]; then
4 | cd $HOME
5 | wget https://github.com/google/protobuf/releases/download/v3.16.1/protoc-3.16.1-linux-x86_64.zip
6 | unzip protoc-3.16.1-linux-x86_64.zip
7 | else
8 | echo "Using cached directory."
9 | fi
10 |
--------------------------------------------------------------------------------