├── .gitignore
├── README.md
├── pom.xml
└── src
├── main
└── java
│ └── org
│ └── opentsdb
│ └── client
│ ├── Client.java
│ ├── ExpectResponse.java
│ ├── HttpClient.java
│ ├── HttpClientImpl.java
│ ├── PoolingHttpClient.java
│ ├── builder
│ ├── DataFormatException.java
│ ├── Metric.java
│ └── MetricBuilder.java
│ ├── response
│ ├── ErrorDetail.java
│ ├── Response.java
│ └── SimpleHttpResponse.java
│ └── util
│ └── Preconditions.java
└── test
├── java
└── org
│ └── opentsdb
│ └── client
│ ├── HttpClientTest.java
│ ├── PoolingHttpClientTest.java
│ └── builder
│ ├── MetricBuilderTest.java
│ └── MetricTest.java
└── resources
└── multiple_metrics.json
/.gitignore:
--------------------------------------------------------------------------------
1 | *.ipr
2 | *.iws
3 | *.iml
4 | var/*
5 | target/*
6 | build/*
7 | */target/*
8 | .gradle/*
9 | out/*
10 | dependency.txt
11 | .tablesawcache
12 | .idea
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | OpenTSDB Java Client
2 | ====================
3 |
4 | The OpenTSDB client is a Java library that makes sending metrics and querying the OpenTSDB server simple.
5 | The HttpClientImpl class is used to push metrics or query the OpenTSDB server. The library uses the builder pattern to
6 | simplify the task of creating the JSON that is used by the client. Also it use the PoolingHttpClientConnectionManager to
7 | manager the http connection.
8 |
9 | **NOTE** I only implements the send metrics interface. Please feel free to fork and implements for your needs.
10 |
11 | ## Sending Metrics
12 |
13 | Sending metrics is done by using the MetricBuilder. You simply add a metric, the tags associated with the metric, and
14 | the data points.
15 |
16 | HttpClient client = new HttpClientImpl("http://localhost:8242");
17 |
18 | MetricBuilder builder = MetricBuilder.getInstance();
19 |
20 | builder.addMetric("metric1").setDataPoint(2, 30L)
21 | .addTag("tag1", "tab1value").addTag("tag2", "tab2value");
22 |
23 | builder.addMetric("metric2").setDataPoint(2, 232.34)
24 | .addTag("tag3", "tab3value");
25 |
26 | try {
27 | Response response = client.pushMetrics(builder,
28 | ExpectResponse.SUMMARY);
29 | System.out.println(response);
30 | } catch (IOException e) {
31 | e.printStackTrace();
32 | }
33 |
34 |
35 | ## Copyright and License
36 |
37 | Copyright 2015 Proofpoint Inc.
38 |
39 | Licensed under the Apache License, Version 2.0 (the "License");
40 | you may not use this file except in compliance with the License.
41 | You may obtain a copy of the License at
42 |
43 | [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)
44 |
45 | Unless required by applicable law or agreed to in writing, software
46 | distributed under the License is distributed on an "AS IS" BASIS,
47 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
48 | See the License for the specific language governing permissions and
49 | limitations under the License.
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | org.opentsdb
6 | java-client
7 | 2.1.0
8 | jar
9 |
10 | opentsdbclient
11 | Java client for pushing and querying data to/from OpenTSDB
12 | http://opentsdb.org
13 |
14 |
15 |
16 | com.google.guava
17 | guava
18 | 14.0
19 |
20 |
21 | org.hamcrest
22 | hamcrest-all
23 | 1.1
24 | test
25 |
26 |
27 | org.apache.httpcomponents
28 | httpclient
29 | 4.3.3
30 |
31 |
32 | commons-lang
33 | commons-lang
34 | 2.6
35 |
36 |
37 | commons-io
38 | commons-io
39 | 2.2
40 |
41 |
42 | com.google.code.findbugs
43 | jsr305
44 | 2.0.0
45 |
46 |
47 |
48 | log4j
49 | log4j
50 | 1.2.16
51 |
52 |
53 |
54 | org.slf4j
55 | slf4j-log4j12
56 | 1.7.2
57 |
58 |
59 |
60 | org.slf4j
61 | slf4j-api
62 | 1.7.2
63 |
64 |
65 |
66 | junit
67 | junit
68 | 4.11
69 | test
70 |
71 |
72 | com.google.code.gson
73 | gson
74 | 2.2.4
75 |
76 |
77 | org.mockito
78 | mockito-core
79 | 1.9.5
80 | test
81 |
82 |
83 |
84 |
85 |
86 |
87 | org.apache.maven.plugins
88 | maven-compiler-plugin
89 | 2.5.1
90 |
91 | 1.6
92 | 1.6
93 |
94 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/src/main/java/org/opentsdb/client/Client.java:
--------------------------------------------------------------------------------
1 | package org.opentsdb.client;
2 |
3 | import java.io.IOException;
4 |
5 | import org.opentsdb.client.builder.MetricBuilder;
6 | import org.opentsdb.client.response.Response;
7 |
8 | public interface Client {
9 |
10 | public final static String POST_API = "/api/put";
11 |
12 | /**
13 | * Sends metrics from the builder to the KairosDB server.
14 | *
15 | * @param builder
16 | * metrics builder
17 | * @return response from the server
18 | * @throws IOException
19 | * problem occurred sending to the server
20 | */
21 | Response pushMetrics(MetricBuilder builder) throws IOException;
22 |
23 | }
--------------------------------------------------------------------------------
/src/main/java/org/opentsdb/client/ExpectResponse.java:
--------------------------------------------------------------------------------
1 | package org.opentsdb.client;
2 |
3 | /**
4 | *
5 | * expect response.
6 | *
7 | *
8 | *
9 | *
10 | * @author argan
11 | *
12 | */
13 | public enum ExpectResponse {
14 | STATUS_CODE, SUMMARY, DETAIL
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/org/opentsdb/client/HttpClient.java:
--------------------------------------------------------------------------------
1 | package org.opentsdb.client;
2 |
3 | import java.io.IOException;
4 |
5 | import org.opentsdb.client.builder.MetricBuilder;
6 | import org.opentsdb.client.response.Response;
7 |
8 | public interface HttpClient extends Client {
9 |
10 | public Response pushMetrics(MetricBuilder builder,
11 | ExpectResponse exceptResponse) throws IOException;
12 | }
--------------------------------------------------------------------------------
/src/main/java/org/opentsdb/client/HttpClientImpl.java:
--------------------------------------------------------------------------------
1 | package org.opentsdb.client;
2 |
3 | import static com.google.common.base.Preconditions.checkNotNull;
4 |
5 | import java.io.IOException;
6 |
7 | import org.apache.commons.lang.StringUtils;
8 | import org.apache.log4j.Logger;
9 | import org.opentsdb.client.builder.MetricBuilder;
10 | import org.opentsdb.client.response.ErrorDetail;
11 | import org.opentsdb.client.response.Response;
12 | import org.opentsdb.client.response.SimpleHttpResponse;
13 |
14 | import com.google.gson.Gson;
15 | import com.google.gson.GsonBuilder;
16 |
17 | /**
18 | * HTTP implementation of a client.
19 | */
20 | public class HttpClientImpl implements HttpClient {
21 |
22 | private static Logger logger = Logger.getLogger(HttpClientImpl.class);
23 |
24 | private String serviceUrl;
25 |
26 | private Gson mapper;
27 |
28 | PoolingHttpClient httpClient = new PoolingHttpClient();
29 |
30 | public HttpClientImpl(String serviceUrl) {
31 | this.serviceUrl = serviceUrl;
32 |
33 | GsonBuilder builder = new GsonBuilder();
34 | mapper = builder.create();
35 | }
36 |
37 | @Override
38 | public Response pushMetrics(MetricBuilder builder) throws IOException {
39 | return pushMetrics(builder, ExpectResponse.STATUS_CODE);
40 |
41 | }
42 |
43 | @Override
44 | public Response pushMetrics(MetricBuilder builder,
45 | ExpectResponse expectResponse) throws IOException {
46 | checkNotNull(builder);
47 |
48 | // TODO 错误处理,比如IOException或者failed>0,写到队列或者文件后续重试。
49 | SimpleHttpResponse response = httpClient
50 | .doPost(buildUrl(serviceUrl, POST_API, expectResponse),
51 | builder.build());
52 |
53 | return getResponse(response);
54 | }
55 |
56 | private String buildUrl(String serviceUrl, String postApiEndPoint,
57 | ExpectResponse expectResponse) {
58 | String url = serviceUrl + postApiEndPoint;
59 |
60 | switch (expectResponse) {
61 | case SUMMARY:
62 | url += "?summary";
63 | break;
64 | case DETAIL:
65 | url += "?details";
66 | break;
67 | default:
68 | break;
69 | }
70 |
71 | return url;
72 | }
73 |
74 | private Response getResponse(SimpleHttpResponse httpResponse) {
75 | Response response = new Response(httpResponse.getStatusCode());
76 | String content = httpResponse.getContent();
77 | if (StringUtils.isNotEmpty(content)) {
78 | if (response.isSuccess()) {
79 | ErrorDetail errorDetail = mapper.fromJson(content,
80 | ErrorDetail.class);
81 | response.setErrorDetail(errorDetail);
82 | } else {
83 | logger.error("request failed!" + httpResponse);
84 | }
85 | }
86 | return response;
87 | }
88 | }
--------------------------------------------------------------------------------
/src/main/java/org/opentsdb/client/PoolingHttpClient.java:
--------------------------------------------------------------------------------
1 | package org.opentsdb.client;
2 |
3 | import static com.google.common.base.Preconditions.checkArgument;
4 |
5 | import java.io.IOException;
6 | import java.util.concurrent.TimeUnit;
7 |
8 | import org.apache.commons.lang.StringUtils;
9 | import org.apache.http.HeaderElement;
10 | import org.apache.http.HeaderElementIterator;
11 | import org.apache.http.HttpEntity;
12 | import org.apache.http.HttpResponse;
13 | import org.apache.http.client.config.RequestConfig;
14 | import org.apache.http.client.methods.HttpPost;
15 | import org.apache.http.client.methods.HttpUriRequest;
16 | import org.apache.http.conn.ConnectionKeepAliveStrategy;
17 | import org.apache.http.conn.HttpClientConnectionManager;
18 | import org.apache.http.entity.StringEntity;
19 | import org.apache.http.impl.client.CloseableHttpClient;
20 | import org.apache.http.impl.client.HttpClients;
21 | import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
22 | import org.apache.http.message.BasicHeaderElementIterator;
23 | import org.apache.http.protocol.HTTP;
24 | import org.apache.http.protocol.HttpContext;
25 | import org.apache.http.util.EntityUtils;
26 | import org.opentsdb.client.response.SimpleHttpResponse;
27 |
28 | /**
29 | *
30 | * @author arganzheng
31 | *
32 | */
33 | public class PoolingHttpClient {
34 |
35 | private static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 200;
36 |
37 | private static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = DEFAULT_MAX_TOTAL_CONNECTIONS;
38 |
39 | private static final int DEFAULT_CONNECTION_TIMEOUT_MILLISECONDS = (10 * 1000);
40 | private static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = (10 * 1000);
41 | private static final int DEFAULT_WAIT_TIMEOUT_MILLISECONDS = (10 * 1000);
42 |
43 | private static final int DEFAULT_KEEP_ALIVE_MILLISECONDS = (5 * 60 * 1000);
44 |
45 | private static final String DEFAULT_CHARSET = "UTF-8";
46 |
47 | private static final int DEFAULT_RETRY_COUNT = 2;
48 |
49 | private int keepAlive = DEFAULT_KEEP_ALIVE_MILLISECONDS;
50 |
51 | private int maxTotalConnections = DEFAULT_MAX_TOTAL_CONNECTIONS;
52 | private int maxConnectionsPerRoute = DEFAULT_MAX_CONNECTIONS_PER_ROUTE;
53 |
54 | private int connectTimeout = DEFAULT_CONNECTION_TIMEOUT_MILLISECONDS;
55 | private int readTimeout = DEFAULT_READ_TIMEOUT_MILLISECONDS;
56 | private int waitTimeout = DEFAULT_WAIT_TIMEOUT_MILLISECONDS;
57 |
58 | private int retries = DEFAULT_RETRY_COUNT;
59 |
60 | private PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
61 |
62 | private CloseableHttpClient httpClient = null;
63 |
64 | private ConnectionKeepAliveStrategy keepAliveStrategy = new ConnectionKeepAliveStrategy() {
65 | @Override
66 | public long getKeepAliveDuration(HttpResponse response,
67 | HttpContext context) {
68 | HeaderElementIterator it = new BasicHeaderElementIterator(
69 | response.headerIterator(HTTP.CONN_KEEP_ALIVE));
70 | while (it.hasNext()) {
71 | HeaderElement he = it.nextElement();
72 | String param = he.getName();
73 | String value = he.getValue();
74 | if (value != null && param.equalsIgnoreCase("timeout")) {
75 | return Long.parseLong(value) * 1000;
76 | }
77 | }
78 | return keepAlive;
79 | }
80 | };
81 |
82 | public PoolingHttpClient() {
83 | // Increase max total connection
84 | connManager.setMaxTotal(maxTotalConnections);
85 | // Increase default max connection per route
86 | connManager.setDefaultMaxPerRoute(maxConnectionsPerRoute);
87 |
88 | // config timeout
89 | RequestConfig config = RequestConfig.custom()
90 | .setConnectTimeout(connectTimeout)
91 | .setConnectionRequestTimeout(waitTimeout)
92 | .setSocketTimeout(readTimeout).build();
93 |
94 | httpClient = HttpClients.custom()
95 | .setKeepAliveStrategy(keepAliveStrategy)
96 | .setConnectionManager(connManager)
97 | .setDefaultRequestConfig(config).build();
98 |
99 | // detect idle and expired connections and close them
100 | IdleConnectionMonitorThread staleMonitor = new IdleConnectionMonitorThread(
101 | connManager);
102 | staleMonitor.start();
103 | }
104 |
105 | public SimpleHttpResponse doPost(String url, String data)
106 | throws IOException {
107 | StringEntity requestEntity = new StringEntity(data);
108 | HttpPost postMethod = new HttpPost(url);
109 | postMethod.setEntity(requestEntity);
110 |
111 | HttpResponse response = execute(postMethod);
112 | int statusCode = response.getStatusLine().getStatusCode();
113 |
114 | SimpleHttpResponse simpleResponse = new SimpleHttpResponse();
115 | simpleResponse.setStatusCode(statusCode);
116 |
117 | HttpEntity entity = response.getEntity();
118 | if (entity != null) {
119 | // should return: application/json; charset=UTF-8
120 | String ctype = entity.getContentType().getValue();
121 | String charset = getResponseCharset(ctype);
122 | String content = EntityUtils.toString(entity, charset);
123 | simpleResponse.setContent(content);
124 | }
125 |
126 | return simpleResponse;
127 | }
128 |
129 | private static String getResponseCharset(String ctype) {
130 | String charset = DEFAULT_CHARSET;
131 |
132 | if (!StringUtils.isEmpty(ctype)) {
133 | String[] params = ctype.split(";");
134 | for (String param : params) {
135 | param = param.trim();
136 | if (param.startsWith("charset")) {
137 | String[] pair = param.split("=", 2);
138 | if (pair.length == 2) {
139 | if (!StringUtils.isEmpty(pair[1])) {
140 | charset = pair[1].trim();
141 | }
142 | }
143 | break;
144 | }
145 | }
146 | }
147 |
148 | return charset;
149 | }
150 |
151 | public HttpResponse execute(HttpUriRequest request) throws IOException {
152 | HttpResponse response;
153 |
154 | int tries = ++retries;
155 | while (true) {
156 | tries--;
157 | try {
158 | response = httpClient.execute(request);
159 | break;
160 | } catch (IOException e) {
161 | if (tries < 1)
162 | throw e;
163 | }
164 | }
165 |
166 | return response;
167 | }
168 |
169 | public void shutdown() throws IOException {
170 | httpClient.close();
171 | }
172 |
173 | public int getKeepAlive() {
174 | return keepAlive;
175 | }
176 |
177 | public void setKeepAlive(int keepAlive) {
178 | this.keepAlive = keepAlive;
179 | }
180 |
181 | public int getMaxTotalConnections() {
182 | return maxTotalConnections;
183 | }
184 |
185 | public void setMaxTotalConnections(int maxTotalConnections) {
186 | this.maxTotalConnections = maxTotalConnections;
187 | }
188 |
189 | public int getMaxConnectionsPerRoute() {
190 | return maxConnectionsPerRoute;
191 | }
192 |
193 | public void setMaxConnectionsPerRoute(int maxConnectionsPerRoute) {
194 | this.maxConnectionsPerRoute = maxConnectionsPerRoute;
195 | }
196 |
197 | public int getConnectTimeout() {
198 | return connectTimeout;
199 | }
200 |
201 | public void setConnectTimeout(int connectTimeout) {
202 | this.connectTimeout = connectTimeout;
203 | }
204 |
205 | public int getReadTimeout() {
206 | return readTimeout;
207 | }
208 |
209 | public void setReadTimeout(int readTimeout) {
210 | this.readTimeout = readTimeout;
211 | }
212 |
213 | public int getWaitTimeout() {
214 | return waitTimeout;
215 | }
216 |
217 | public void setWaitTimeout(int waitTimeout) {
218 | this.waitTimeout = waitTimeout;
219 | }
220 |
221 | public PoolingHttpClientConnectionManager getConnManager() {
222 | return connManager;
223 | }
224 |
225 | public void setConnManager(PoolingHttpClientConnectionManager connManager) {
226 | this.connManager = connManager;
227 | }
228 |
229 | public int getRetryCount() {
230 | return retries;
231 | }
232 |
233 | public void setRetryCount(int retries) {
234 | checkArgument(retries >= 0);
235 | this.retries = retries;
236 | }
237 |
238 | public static class IdleConnectionMonitorThread extends Thread {
239 |
240 | private final HttpClientConnectionManager connMgr;
241 | private volatile boolean shutdown;
242 |
243 | public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) {
244 | super();
245 | this.connMgr = connMgr;
246 | }
247 |
248 | @Override
249 | public void run() {
250 | try {
251 | while (!shutdown) {
252 | synchronized (this) {
253 | wait(5000);
254 | // Close expired connections
255 | connMgr.closeExpiredConnections();
256 | // Optionally, close connections
257 | // that have been idle longer than 60 sec
258 | connMgr.closeIdleConnections(60, TimeUnit.SECONDS);
259 | }
260 | }
261 | } catch (InterruptedException ex) {
262 | // terminate
263 | shutdown();
264 | }
265 | }
266 |
267 | public void shutdown() {
268 | shutdown = true;
269 | synchronized (this) {
270 | notifyAll();
271 | }
272 | }
273 |
274 | }
275 | }
--------------------------------------------------------------------------------
/src/main/java/org/opentsdb/client/builder/DataFormatException.java:
--------------------------------------------------------------------------------
1 | package org.opentsdb.client.builder;
2 |
3 | public class DataFormatException extends Exception
4 | {
5 | public DataFormatException()
6 | {
7 | super();
8 | }
9 |
10 | public DataFormatException(String s)
11 | {
12 | super(s);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/org/opentsdb/client/builder/Metric.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Proofpoint Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.opentsdb.client.builder;
17 |
18 | import static com.google.common.base.Preconditions.checkArgument;
19 | import static com.google.common.base.Preconditions.checkNotNull;
20 | import static org.opentsdb.client.util.Preconditions.checkNotNullOrEmpty;
21 |
22 | import java.util.Collections;
23 | import java.util.HashMap;
24 | import java.util.Map;
25 |
26 | import com.google.gson.annotations.SerializedName;
27 |
28 | /**
29 | * A metric contains measurements or data points. Each data point has a time
30 | * stamp of when the measurement occurred and a value that is either a long or
31 | * double and optionally contains tags. Tags are labels that can be added to
32 | * better identify the metric. For example, if the measurement was done on
33 | * server1 then you might add a tag named "host" with a value of "server1". Note
34 | * that a metric must have at least one tag.
35 | */
36 | public class Metric {
37 |
38 | @SerializedName("metric")
39 | private String name;
40 |
41 | private long timestamp;
42 |
43 | private Object value;
44 |
45 | private Map tags = new HashMap();
46 |
47 | protected Metric(String name) {
48 | this.name = checkNotNullOrEmpty(name);
49 | }
50 |
51 | /**
52 | * Adds a tag to the data point.
53 | *
54 | * @param name
55 | * tag identifier
56 | * @param value
57 | * tag value
58 | * @return the metric the tag was added to
59 | */
60 | public Metric addTag(String name, String value) {
61 | checkNotNullOrEmpty(name);
62 | checkNotNullOrEmpty(value);
63 | tags.put(name, value);
64 |
65 | return this;
66 | }
67 |
68 | /**
69 | * Adds tags to the data point.
70 | *
71 | * @param tags
72 | * map of tags
73 | * @return the metric the tags were added to
74 | */
75 | public Metric addTags(Map tags) {
76 | checkNotNull(tags);
77 | this.tags.putAll(tags);
78 |
79 | return this;
80 | }
81 |
82 | /**
83 | * set the data point for the metric.
84 | *
85 | * @param timestamp
86 | * when the measurement occurred
87 | * @param value
88 | * the measurement value
89 | * @return the metric
90 | */
91 | protected Metric innerAddDataPoint(long timestamp, Object value) {
92 | checkArgument(timestamp > 0);
93 | this.timestamp = timestamp;
94 | this.value = checkNotNull(value);
95 |
96 | return this;
97 | }
98 |
99 | /**
100 | * Adds the data point to the metric with a timestamp of now.
101 | *
102 | * @param value
103 | * the measurement value
104 | * @return the metric
105 | */
106 | public Metric setDataPoint(long value) {
107 | return innerAddDataPoint(System.currentTimeMillis(), value);
108 | }
109 |
110 | public Metric setDataPoint(long timestamp, long value) {
111 | return innerAddDataPoint(timestamp, value);
112 | }
113 |
114 | /**
115 | * Adds the data point to the metric.
116 | *
117 | * @param timestamp
118 | * when the measurement occurred
119 | * @param value
120 | * the measurement value
121 | * @return the metric
122 | */
123 | public Metric setDataPoint(long timestamp, double value) {
124 | return innerAddDataPoint(timestamp, value);
125 | }
126 |
127 | /**
128 | * Adds the data point to the metric with a timestamp of now.
129 | *
130 | * @param value
131 | * the measurement value
132 | * @return the metric
133 | */
134 | public Metric setDataPoint(double value) {
135 | return innerAddDataPoint(System.currentTimeMillis(), value);
136 | }
137 |
138 | /**
139 | * Time when the data point was measured.
140 | *
141 | * @return time when the data point was measured
142 | */
143 | public long getTimestamp() {
144 | return timestamp;
145 | }
146 |
147 | public Object getValue() {
148 | return value;
149 | }
150 |
151 | public String stringValue() throws DataFormatException {
152 | return value.toString();
153 | }
154 |
155 | public long longValue() throws DataFormatException {
156 | try {
157 | return ((Number) value).longValue();
158 | } catch (Exception e) {
159 | throw new DataFormatException("Value is not a long");
160 | }
161 | }
162 |
163 | public double doubleValue() throws DataFormatException {
164 | try {
165 | return ((Number) value).doubleValue();
166 | } catch (Exception e) {
167 | throw new DataFormatException("Value is not a double");
168 | }
169 | }
170 |
171 | public boolean isDoubleValue() {
172 | return !(((Number) value).doubleValue() == Math.floor(((Number) value)
173 | .doubleValue()));
174 | }
175 |
176 | public boolean isIntegerValue() {
177 | return ((Number) value).doubleValue() == Math.floor(((Number) value)
178 | .doubleValue());
179 | }
180 |
181 | /**
182 | * Returns the metric name.
183 | *
184 | * @return metric name
185 | */
186 | public String getName() {
187 | return name;
188 | }
189 |
190 | /**
191 | * Returns the tags associated with the data point.
192 | *
193 | * @return tag for the data point
194 | */
195 | public Map getTags() {
196 | return Collections.unmodifiableMap(tags);
197 | }
198 |
199 | }
200 |
--------------------------------------------------------------------------------
/src/main/java/org/opentsdb/client/builder/MetricBuilder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Proofpoint Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.opentsdb.client.builder;
17 |
18 | import static com.google.common.base.Preconditions.checkState;
19 |
20 | import java.io.IOException;
21 | import java.util.ArrayList;
22 | import java.util.List;
23 |
24 | import com.google.gson.Gson;
25 | import com.google.gson.GsonBuilder;
26 |
27 | /**
28 | * Builder used to create the JSON to push metrics to KairosDB.
29 | */
30 | public class MetricBuilder {
31 | private List metrics = new ArrayList();
32 | private transient Gson mapper;
33 |
34 | private MetricBuilder() {
35 | GsonBuilder builder = new GsonBuilder();
36 | mapper = builder.create();
37 | }
38 |
39 | /**
40 | * Returns a new metric builder.
41 | *
42 | * @return metric builder
43 | */
44 | public static MetricBuilder getInstance() {
45 | return new MetricBuilder();
46 | }
47 |
48 | /**
49 | * Adds a metric to the builder.
50 | *
51 | * @param metricName
52 | * metric name
53 | * @return the new metric
54 | */
55 | public Metric addMetric(String metricName) {
56 | Metric metric = new Metric(metricName);
57 | metrics.add(metric);
58 | return metric;
59 | }
60 |
61 | /**
62 | * Returns a list of metrics added to the builder.
63 | *
64 | * @return list of metrics
65 | */
66 | public List getMetrics() {
67 | return metrics;
68 | }
69 |
70 | /**
71 | * Returns the JSON string built by the builder. This is the JSON that can
72 | * be used by the client add metrics.
73 | *
74 | * @return JSON
75 | * @throws IOException
76 | * if metrics cannot be converted to JSON
77 | */
78 | public String build() throws IOException {
79 | for (Metric metric : metrics) {
80 | // verify that there is at least one tag for each metric
81 | checkState(metric.getTags().size() > 0, metric.getName()
82 | + " must contain at least one tag.");
83 | }
84 | return mapper.toJson(metrics);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/main/java/org/opentsdb/client/response/ErrorDetail.java:
--------------------------------------------------------------------------------
1 | package org.opentsdb.client.response;
2 |
3 | import java.util.Collections;
4 | import java.util.HashMap;
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | /**
9 | * List of errors returned by OpenTSDB.
10 | */
11 | public class ErrorDetail {
12 | private List errors;
13 |
14 | private Integer success;
15 | private Integer failed;
16 |
17 | public ErrorDetail(List errors) {
18 | this.errors = errors;
19 | }
20 |
21 | public ErrorDetail(Integer success, Integer failed) {
22 | this.success = success;
23 | this.failed = failed;
24 | }
25 |
26 | public ErrorDetail(Integer success, Integer failed,
27 | List errors) {
28 | this.success = success;
29 | this.failed = failed;
30 |
31 | this.errors = errors;
32 | }
33 |
34 | public ErrorDetail(ErrorDetailEntity error) {
35 | errors = Collections.singletonList(error);
36 | }
37 |
38 | public List getErrors() {
39 | return (errors);
40 | }
41 |
42 | public Integer getSuccess() {
43 | return success;
44 | }
45 |
46 | public void setSuccess(Integer success) {
47 | this.success = success;
48 | }
49 |
50 | public Integer getFailed() {
51 | return failed;
52 | }
53 |
54 | public void setFailed(Integer failed) {
55 | this.failed = failed;
56 | }
57 |
58 | public static class ErrorDetailEntity {
59 | private DataPoint datapoint;
60 | private String error;
61 |
62 | public DataPoint getDatapoint() {
63 | return datapoint;
64 | }
65 |
66 | public void setDatapoint(DataPoint datapoint) {
67 | this.datapoint = datapoint;
68 | }
69 |
70 | public String getError() {
71 | return error;
72 | }
73 |
74 | public void setError(String error) {
75 | this.error = error;
76 | }
77 |
78 | @Override
79 | public String toString() {
80 | return "ErrorDetailEntity [datapoint=" + datapoint + ", error="
81 | + error + "]";
82 | }
83 |
84 | }
85 |
86 | public static class DataPoint {
87 | private String metric;
88 | private long timestamp;
89 |
90 | private Object value;
91 |
92 | private Map tags = new HashMap();
93 |
94 | public String getMetric() {
95 | return metric;
96 | }
97 |
98 | public void setMetric(String metric) {
99 | this.metric = metric;
100 | }
101 |
102 | public long getTimestamp() {
103 | return timestamp;
104 | }
105 |
106 | public void setTimestamp(long timestamp) {
107 | this.timestamp = timestamp;
108 | }
109 |
110 | public Object getValue() {
111 | return value;
112 | }
113 |
114 | public void setValue(Object value) {
115 | this.value = value;
116 | }
117 |
118 | public Map getTags() {
119 | return tags;
120 | }
121 |
122 | public void setTags(Map tags) {
123 | this.tags = tags;
124 | }
125 |
126 | }
127 |
128 | @Override
129 | public String toString() {
130 | return "ErrorDetail [" + "success=" + success + ", failed=" + failed
131 | + ", errors=" + errors + "]";
132 | }
133 | }
--------------------------------------------------------------------------------
/src/main/java/org/opentsdb/client/response/Response.java:
--------------------------------------------------------------------------------
1 | package org.opentsdb.client.response;
2 |
3 | /**
4 | * Response returned by the OpenTSDB server.
5 | */
6 | public class Response {
7 | private int statusCode;
8 | private ErrorDetail errorDetail;
9 |
10 | public Response() {
11 | }
12 |
13 | public boolean isSuccess() {
14 | return statusCode == 200 || statusCode == 204;
15 | }
16 |
17 | public Response(int statusCode) {
18 | this.statusCode = statusCode;
19 | }
20 |
21 | public int getStatusCode() {
22 | return statusCode;
23 | }
24 |
25 | public void setStatusCode(int statusCode) {
26 | this.statusCode = statusCode;
27 | }
28 |
29 | public ErrorDetail getErrorDetail() {
30 | return errorDetail;
31 | }
32 |
33 | public void setErrorDetail(ErrorDetail errorDetail) {
34 | this.errorDetail = errorDetail;
35 | }
36 |
37 | @Override
38 | public String toString() {
39 | return "Response [statusCode=" + statusCode + ", errorDetail="
40 | + errorDetail + "]";
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/src/main/java/org/opentsdb/client/response/SimpleHttpResponse.java:
--------------------------------------------------------------------------------
1 | package org.opentsdb.client.response;
2 |
3 | /**
4 | * @author argan
5 | *
6 | */
7 | public class SimpleHttpResponse {
8 | private int statusCode;
9 | private String content;
10 |
11 | public boolean isSuccess() {
12 | return statusCode == 200 || statusCode == 204;
13 | }
14 |
15 | public int getStatusCode() {
16 | return statusCode;
17 | }
18 |
19 | public void setStatusCode(int statusCode) {
20 | this.statusCode = statusCode;
21 | }
22 |
23 | public String getContent() {
24 | return content;
25 | }
26 |
27 | public void setContent(String content) {
28 | this.content = content;
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/org/opentsdb/client/util/Preconditions.java:
--------------------------------------------------------------------------------
1 | package org.opentsdb.client.util;
2 |
3 | import com.google.common.annotations.VisibleForTesting;
4 | import javax.annotation.Nullable;
5 |
6 | public class Preconditions {
7 | public static String checkNotNullOrEmpty(String reference) {
8 | com.google.common.base.Preconditions.checkNotNull(reference);
9 | if (reference.isEmpty()) {
10 | throw new IllegalArgumentException();
11 | }
12 | return reference;
13 | }
14 |
15 | public static String checkNotNullOrEmpty(String reference,
16 | @Nullable String errorMessageTemplate,
17 | @Nullable Object... errorMessageArgs) {
18 | com.google.common.base.Preconditions.checkNotNull(reference,
19 | errorMessageTemplate, errorMessageArgs);
20 | if (reference.isEmpty()) {
21 | throw new IllegalArgumentException(format(errorMessageTemplate,
22 | errorMessageArgs));
23 |
24 | }
25 | return reference;
26 | }
27 |
28 | /**
29 | * Copied from Google's Precondition class because it is package protected.
30 | *
31 | * Substitutes each {@code %s} in {@code template} with an argument. These
32 | * are matched by position - the first {@code %s} gets {@code args[0]}, etc.
33 | * If there are more arguments than placeholders, the unmatched arguments
34 | * will be appended to the end of the formatted message in square braces.
35 | *
36 | * @param template
37 | * a non-null string containing 0 or more {@code %s}
38 | * placeholders.
39 | * @param args
40 | * the arguments to be substituted into the message template.
41 | * Arguments are converted to strings using
42 | * {@link String#valueOf(Object)}. Arguments can be null.
43 | */
44 | @VisibleForTesting
45 | static String format(String template, @Nullable Object... args) {
46 | template = String.valueOf(template); // null -> "null"
47 |
48 | // start substituting the arguments into the '%s' placeholders
49 | StringBuilder builder = new StringBuilder(template.length() + 16
50 | * args.length);
51 | int templateStart = 0;
52 | int i = 0;
53 | while (i < args.length) {
54 | int placeholderStart = template.indexOf("%s", templateStart);
55 | if (placeholderStart == -1) {
56 | break;
57 | }
58 | builder.append(template.substring(templateStart, placeholderStart));
59 | builder.append(args[i++]);
60 | templateStart = placeholderStart + 2;
61 | }
62 | builder.append(template.substring(templateStart));
63 |
64 | // if we run out of placeholders, append the extra args in square braces
65 | if (i < args.length) {
66 | builder.append(" [");
67 | builder.append(args[i++]);
68 | while (i < args.length) {
69 | builder.append(", ");
70 | builder.append(args[i++]);
71 | }
72 | builder.append(']');
73 | }
74 |
75 | return builder.toString();
76 | }
77 |
78 | }
--------------------------------------------------------------------------------
/src/test/java/org/opentsdb/client/HttpClientTest.java:
--------------------------------------------------------------------------------
1 | package org.opentsdb.client;
2 |
3 | import java.io.IOException;
4 |
5 | import org.junit.Test;
6 | import org.opentsdb.client.builder.MetricBuilder;
7 | import org.opentsdb.client.response.Response;
8 |
9 | public class HttpClientTest {
10 |
11 | @Test
12 | public void test_pushMetrics_DefaultRetries() {
13 | HttpClientImpl client = new HttpClientImpl("http://localhost:8242");
14 |
15 | MetricBuilder builder = MetricBuilder.getInstance();
16 |
17 | builder.addMetric("metric1").setDataPoint(2, 30L)
18 | .addTag("tag1", "tab1value").addTag("tag2", "tab2value");
19 |
20 | builder.addMetric("metric2").setDataPoint(2, 232.34)
21 | .addTag("tag3", "tab3value");
22 |
23 | try {
24 | Response response = client.pushMetrics(builder,
25 | ExpectResponse.SUMMARY);
26 | System.out.println(response);
27 | } catch (IOException e) {
28 | e.printStackTrace();
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/src/test/java/org/opentsdb/client/PoolingHttpClientTest.java:
--------------------------------------------------------------------------------
1 | package org.opentsdb.client;
2 |
3 | import java.io.IOException;
4 |
5 | import org.junit.Test;
6 | import org.opentsdb.client.builder.MetricBuilder;
7 | import org.opentsdb.client.response.SimpleHttpResponse;
8 |
9 | public class PoolingHttpClientTest {
10 |
11 | @Test
12 | public void test_postJson_DefaultRetries() throws InterruptedException {
13 | PoolingHttpClient client = new PoolingHttpClient();
14 |
15 | try {
16 | for (int i = 0; i < 10; i++) {
17 | MetricBuilder builder = MetricBuilder.getInstance();
18 |
19 | builder.addMetric("metric" + i).setDataPoint(2, 30L)
20 | .addTag("tag1", "tab1value")
21 | .addTag("tag2", "tab2value");
22 |
23 | SimpleHttpResponse response = client.doPost(
24 | "http://localhost:8242/api/put/?details",
25 | builder.build());
26 | System.out.println(response.getStatusCode());
27 | System.out.println(response.getContent());
28 | }
29 | } catch (IOException e) {
30 | e.printStackTrace();
31 | }
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/src/test/java/org/opentsdb/client/builder/MetricBuilderTest.java:
--------------------------------------------------------------------------------
1 | package org.opentsdb.client.builder;
2 |
3 | import com.google.common.base.Charsets;
4 | import com.google.common.io.Resources;
5 |
6 | import org.junit.Test;
7 | import org.opentsdb.client.builder.MetricBuilder;
8 |
9 | import java.io.IOException;
10 |
11 | import static org.hamcrest.CoreMatchers.equalTo;
12 | import static org.junit.Assert.assertThat;
13 |
14 | public class MetricBuilderTest {
15 | @Test
16 | public void test() throws IOException {
17 | String json = Resources.toString(
18 | Resources.getResource("multiple_metrics.json"), Charsets.UTF_8);
19 |
20 | MetricBuilder builder = MetricBuilder.getInstance();
21 |
22 | builder.addMetric("metric1").setDataPoint(2, 30L)
23 | .addTag("tag1", "tab1value").addTag("tag2", "tab2value");
24 |
25 | builder.addMetric("metric2").setDataPoint(2, 232.34)
26 | .addTag("tag3", "tab3value");
27 |
28 | assertThat(builder.build(), equalTo(json));
29 | }
30 |
31 | @Test(expected = IllegalStateException.class)
32 | public void test_metricContainsTags() throws IOException {
33 | MetricBuilder builder = MetricBuilder.getInstance();
34 | builder.addMetric("metric1");
35 | builder.addMetric("metric2").addTag("tag", "value");
36 |
37 | builder.build();
38 | }
39 |
40 | public static class MetricTest {
41 | @Test(expected = NullPointerException.class)
42 | public void test_nullMetricName_invalid() {
43 | MetricBuilder builder = MetricBuilder.getInstance();
44 |
45 | builder.addMetric(null);
46 | }
47 |
48 | @Test(expected = IllegalArgumentException.class)
49 | public void test_emptyMetricName_invalid() {
50 | MetricBuilder builder = MetricBuilder.getInstance();
51 |
52 | builder.addMetric("");
53 | }
54 |
55 | @Test(expected = NullPointerException.class)
56 | public void test_nullTagName_invalid() {
57 | MetricBuilder builder = MetricBuilder.getInstance();
58 |
59 | builder.addMetric("metric1").addTag(null, "value");
60 | }
61 |
62 | @Test(expected = IllegalArgumentException.class)
63 | public void test_emptyTagName_invalid() {
64 | MetricBuilder builder = MetricBuilder.getInstance();
65 |
66 | builder.addMetric("metric1").addTag("", "value");
67 | }
68 |
69 | @Test(expected = NullPointerException.class)
70 | public void test_nullTagValue_invalid() {
71 | MetricBuilder builder = MetricBuilder.getInstance();
72 |
73 | builder.addMetric("metric1").addTag("tag", null);
74 | }
75 |
76 | @Test(expected = IllegalArgumentException.class)
77 | public void test_emptyTagValue_invalid() {
78 | MetricBuilder builder = MetricBuilder.getInstance();
79 |
80 | builder.addMetric("metric1").addTag("tag", "");
81 | }
82 | }
83 | }
--------------------------------------------------------------------------------
/src/test/java/org/opentsdb/client/builder/MetricTest.java:
--------------------------------------------------------------------------------
1 | package org.opentsdb.client.builder;
2 |
3 | import org.junit.Test;
4 | import org.opentsdb.client.builder.MetricBuilder;
5 |
6 | public class MetricTest {
7 | @Test(expected = NullPointerException.class)
8 | public void test_nullMetricName_invalid() {
9 | MetricBuilder builder = MetricBuilder.getInstance();
10 |
11 | builder.addMetric(null);
12 | }
13 |
14 | @Test(expected = IllegalArgumentException.class)
15 | public void test_emptyMetricName_invalid() {
16 | MetricBuilder builder = MetricBuilder.getInstance();
17 |
18 | builder.addMetric("");
19 | }
20 |
21 | @Test(expected = NullPointerException.class)
22 | public void test_nullTagName_invalid() {
23 | MetricBuilder builder = MetricBuilder.getInstance();
24 |
25 | builder.addMetric("metric1").addTag(null, "value");
26 | }
27 |
28 | @Test(expected = IllegalArgumentException.class)
29 | public void test_emptyTagName_invalid() {
30 | MetricBuilder builder = MetricBuilder.getInstance();
31 |
32 | builder.addMetric("metric1").addTag("", "value");
33 | }
34 |
35 | @Test(expected = NullPointerException.class)
36 | public void test_nullTagValue_invalid() {
37 | MetricBuilder builder = MetricBuilder.getInstance();
38 |
39 | builder.addMetric("metric1").addTag("tag", null);
40 | }
41 |
42 | @Test(expected = IllegalArgumentException.class)
43 | public void test_emptyTagValue_invalid() {
44 | MetricBuilder builder = MetricBuilder.getInstance();
45 |
46 | builder.addMetric("metric1").addTag("tag", "");
47 | }
48 | }
--------------------------------------------------------------------------------
/src/test/resources/multiple_metrics.json:
--------------------------------------------------------------------------------
1 | [{"name":"metric1","tags":{"tag2":"tab2value","tag1":"tab1value"},"datapoints":[[1,10],[2,30]]},{"name":"metric2","tags":{"tag3":"tab3value"},"datapoints":[[2,30],[3,2.3]]}]
--------------------------------------------------------------------------------