diff --git a/.gitignore b/.gitignore
index 090692a..6258417 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,6 @@ out/*
dependency.txt
.tablesawcache
.idea
++.classpath
++.project
++.settings
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index ad68d25..c1f36b2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,14 +1,16 @@
-
- 4.0.0
+ 4.0.0
- org.kairosdb
- client
- 2.2.0-SNAPSHOT
- jar
+ org.kairosdb
+ client
+ 2.2.0-SNAPSHOT
+ jar
- kairosclient
- Java client for pushing and querying data to/from KairosDB
+ kairosclient
+ Java client for pushing and querying data to/from KairosDB
+
http://kairosdb.org
@@ -36,78 +38,78 @@
-
-
- com.google.guava
- guava
- 14.0
-
-
- org.hamcrest
- hamcrest-all
- 1.1
- test
-
-
- org.apache.httpcomponents
- httpclient
- 4.3.3
-
-
- commons-lang
- commons-lang
- 2.6
-
-
- commons-io
- commons-io
- 2.2
-
-
- com.google.code.findbugs
- jsr305
- 2.0.0
-
+
+
+ com.google.guava
+ guava
+ 14.0
+
+
+ org.hamcrest
+ hamcrest-all
+ 1.1
+ test
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.3.3
+
+
+ commons-lang
+ commons-lang
+ 2.6
+
+
+ commons-io
+ commons-io
+ 2.2
+
+
+ com.google.code.findbugs
+ jsr305
+ 2.0.0
+
-
- junit
- junit
- 4.11
- test
-
-
- com.google.code.gson
- gson
- 2.2.4
-
-
- org.kairosdb
- kairosdb
- 1.1.2-1
- test
-
+
+ junit
+ junit
+ 4.11
+ test
+
+
+ com.google.code.gson
+ gson
+ 2.2.4
+
+
+ org.kairosdb
+ kairosdb
+ 1.1.3-1
+ test
+
-
- org.mockito
- mockito-core
- 1.9.5
- test
-
-
+
+ org.mockito
+ mockito-core
+ 1.9.5
+ test
+
+
-
-
- sonatype-nexus-snapshots
- Sonatype Nexus Snapshots
- http://oss.sonatype.org/content/repositories/snapshots
-
- false
-
-
- true
-
-
-
+
+
+ sonatype-nexus-snapshots
+ Sonatype Nexus Snapshots
+ http://oss.sonatype.org/content/repositories/snapshots
+
+ false
+
+
+ true
+
+
+
@@ -116,96 +118,97 @@
ossrh
- https://oss.sonatype.org/service/local/staging/deploy/maven2/
+ https://oss.sonatype.org/service/local/staging/deploy/maven2/
+
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 2.5.1
-
-
- 1.6
-
-
-
- org.jacoco
- jacoco-maven-plugin
- 0.7.7.201606060606
-
-
- prepare-agent
-
- prepare-agent
-
-
-
- report
- prepare-package
-
- report
-
-
-
-
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 2.5.1
+
+
+ 1.6
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.7.7.201606060606
+
+
+ prepare-agent
+
+ prepare-agent
+
+
+
+ report
+ prepare-package
+
+ report
+
+
+
+
-
- org.apache.maven.plugins
- maven-source-plugin
- 2.2.1
-
-
- attach-sources
-
- jar-no-fork
-
-
-
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
- 2.9.1
-
-
- attach-javadocs
-
- jar
-
-
-
-
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 2.2.1
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.9.1
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
-
- org.apache.maven.plugins
- maven-gpg-plugin
- 1.6
-
-
- sign-artifacts
- verify
-
- sign
-
-
-
-
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 1.6
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
-
- org.sonatype.plugins
- nexus-staging-maven-plugin
- 1.6.2
- true
-
- ossrh
- https://oss.sonatype.org/
- false
-
-
-
-
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.6.2
+ true
+
+ ossrh
+ https://oss.sonatype.org/
+ false
+
+
+
+
diff --git a/src/main/java/org/kairosdb/client/AbstractClient.java b/src/main/java/org/kairosdb/client/AbstractClient.java
index 752201e..c23e5c4 100644
--- a/src/main/java/org/kairosdb/client/AbstractClient.java
+++ b/src/main/java/org/kairosdb/client/AbstractClient.java
@@ -1,27 +1,10 @@
-/*
- * Copyright 2013 Proofpoint Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
package org.kairosdb.client;
import com.google.gson.stream.JsonReader;
import org.kairosdb.client.builder.MetricBuilder;
import org.kairosdb.client.builder.QueryBuilder;
-import org.kairosdb.client.response.ErrorResponse;
-import org.kairosdb.client.response.GetResponse;
-import org.kairosdb.client.response.QueryResponse;
-import org.kairosdb.client.response.Response;
+import org.kairosdb.client.builder.QueryTagBuilder;
+import org.kairosdb.client.response.*;
import java.io.IOException;
import java.io.InputStream;
@@ -77,6 +60,16 @@ public GetResponse getTagValues() throws IOException
return get(url + "/api/v1/tagvalues");
}
+ @Override
+ public QueryTagResponse queryTag(QueryTagBuilder builder) throws URISyntaxException, IOException
+ {
+ ClientResponse clientResponse = postData(builder.build(), url + "/api/v1/datapoints/query/tags");
+ int responseCode = clientResponse.getStatusCode();
+
+ InputStream stream = clientResponse.getContentStream();
+ return new QueryTagResponse(mapper, responseCode, stream);
+ }
+
@Override
public QueryResponse query(QueryBuilder builder) throws URISyntaxException, IOException
{
diff --git a/src/main/java/org/kairosdb/client/Client.java b/src/main/java/org/kairosdb/client/Client.java
index 464f855..e070518 100644
--- a/src/main/java/org/kairosdb/client/Client.java
+++ b/src/main/java/org/kairosdb/client/Client.java
@@ -1,24 +1,11 @@
-/*
- * Copyright 2013 Proofpoint Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
package org.kairosdb.client;
import org.kairosdb.client.builder.MetricBuilder;
import org.kairosdb.client.builder.QueryBuilder;
+import org.kairosdb.client.builder.QueryTagBuilder;
import org.kairosdb.client.response.GetResponse;
import org.kairosdb.client.response.QueryResponse;
+import org.kairosdb.client.response.QueryTagResponse;
import org.kairosdb.client.response.Response;
import java.io.IOException;
@@ -60,6 +47,17 @@ public interface Client
*/
QueryResponse query(QueryBuilder builder) throws URISyntaxException, IOException;
+ /**
+ * Queries KairosDB tags using the query built by the builder.
+ *
+ * @param builder query tag builder
+ * @return response from the server
+ * @throws URISyntaxException if the host or post is invalid
+ * @throws IOException problem occurred querying the server
+ */
+ QueryTagResponse queryTag(QueryTagBuilder builder) throws URISyntaxException, IOException;
+
+
/**
* Sends metrics from the builder to the KairosDB server.
*
@@ -74,8 +72,8 @@ public interface Client
* Deletes a metric. This is the metric and all its data points.
*
* @param name the metric to delete
- * @throws IOException problem occurred sending to the server
* @return response from the server
+ * @throws IOException problem occurred sending to the server
*/
Response deleteMetric(String name) throws IOException;
@@ -99,6 +97,7 @@ public interface Client
/**
* Shuts down the client. Should be called when done using the client.
+ *
* @throws IOException if could not shutdown the client
*/
void shutdown() throws IOException;
diff --git a/src/main/java/org/kairosdb/client/JsonMapper.java b/src/main/java/org/kairosdb/client/JsonMapper.java
index f346d4a..b97ce5e 100644
--- a/src/main/java/org/kairosdb/client/JsonMapper.java
+++ b/src/main/java/org/kairosdb/client/JsonMapper.java
@@ -20,8 +20,6 @@ public JsonMapper(DataPointTypeRegistry typeRegistry)
builder.registerTypeAdapter(GroupResult.class, new GroupByDeserializer());
builder.registerTypeAdapter(Result.class, new ResultsDeserializer(typeRegistry));
mapper = builder.create();
-
-
}
public T fromJson(Reader json, Type typeOfT)
diff --git a/src/main/java/org/kairosdb/client/builder/AbstractQueryBuilder.java b/src/main/java/org/kairosdb/client/builder/AbstractQueryBuilder.java
new file mode 100644
index 0000000..39ca25f
--- /dev/null
+++ b/src/main/java/org/kairosdb/client/builder/AbstractQueryBuilder.java
@@ -0,0 +1,213 @@
+package org.kairosdb.client.builder;
+
+import com.google.gson.Gson;
+import com.google.gson.annotations.SerializedName;
+
+import java.io.IOException;
+import java.util.Date;
+
+import static com.google.common.base.Preconditions.*;
+
+/**
+ * Abstract class for querying KairosDB.
+ * @param the builder
+ */
+public abstract class AbstractQueryBuilder>
+{
+ @SerializedName("start_absolute")
+ protected Long startAbsolute;
+
+ @SerializedName("end_absolute")
+ protected Long endAbsolute;
+
+ @SerializedName("start_relative")
+ protected RelativeTime startRelative;
+
+ @SerializedName("end_relative")
+ protected RelativeTime endRelative;
+
+ protected transient Gson mapper;
+
+ protected AbstractQueryBuilder()
+ {
+ mapper = buildGson();
+ }
+
+ /**
+ * Builds Gson used by this implementation
+ */
+ protected abstract Gson buildGson();
+
+ /**
+ * Returns the absolute range start time.
+ *
+ * @return absolute range start time
+ */
+ public Date getStartAbsolute()
+ {
+ return new Date(startAbsolute);
+ }
+
+ /**
+ * Returns the absolute range end time.
+ *
+ * @return absolute range end time
+ */
+ public Date getEndAbsolute()
+ {
+ return new Date(endAbsolute);
+ }
+
+ /**
+ * Returns the relative range start time.
+ *
+ * @return relative range start time
+ */
+ public RelativeTime getStartRelative()
+ {
+ return startRelative;
+ }
+
+ /**
+ * Returns the relative range end time.
+ *
+ * @return relative range end time
+ */
+ public RelativeTime getEndRelative()
+ {
+ return endRelative;
+ }
+
+ /**
+ * The beginning time of the time range.
+ *
+ * @param start start time
+ * @return the builder
+ */
+ @SuppressWarnings({"unchecked", "ConstantConditions"})
+ public B setStart(Date start)
+ {
+ checkNotNull(start, "start cannot be null");
+ checkArgument(startRelative == null, "Both relative and absolute start times cannot be set.");
+
+ this.startAbsolute = start.getTime();
+ checkArgument(startAbsolute <= System.currentTimeMillis(), "Start time cannot be in the future.");
+ return (B) this;
+ }
+
+ /**
+ * The beginning time of the time range relative to now. For example, return all data points that starting 2 days
+ * ago.
+ *
+ * @param duration relative time value
+ * @param unit unit of time
+ * @return the builder
+ */
+ @SuppressWarnings({"unchecked", "ConstantConditions"})
+ public B setStart(int duration, TimeUnit unit)
+ {
+ checkArgument(duration > 0, "duration must be greater than 0");
+ checkNotNull(unit, "unit cannot be null");
+ checkArgument(startAbsolute == null, "Both relative and absolute start times cannot be set.");
+
+ startRelative = new RelativeTime(duration, unit);
+ checkArgument(startRelative.getTimeRelativeTo(System.currentTimeMillis()) <= System.currentTimeMillis(), "Start time cannot be in the future.");
+ return (B) this;
+ }
+
+ /**
+ * The ending value of the time range. Must be later in time than the start time. An end time is not required
+ * and default to now.
+ *
+ * @param end end time
+ * @return the builder
+ */
+ @SuppressWarnings("unchecked")
+ public B setEnd(Date end)
+ {
+ checkArgument(endRelative == null, "Both relative and absolute end times cannot be set.");
+ this.endAbsolute = end.getTime();
+ return (B) this;
+ }
+
+ /**
+ * The ending time of the time range relative to now.
+ *
+ * @param duration relative time value
+ * @param unit unit of time
+ * @return the builder
+ */
+ @SuppressWarnings({"unchecked", "ConstantConditions"})
+ public B setEnd(int duration, TimeUnit unit)
+ {
+ checkNotNull(unit, "unit cannot be null");
+ checkArgument(duration > 0, "duration must be greater than 0");
+ checkArgument(endAbsolute == null, "Both relative and absolute end times cannot be set.");
+ endRelative = new RelativeTime(duration, unit);
+ return (B) this;
+ }
+
+
+ /**
+ * Returns the JSON string built by the builder. This is the JSON that can be used by the client to query KairosDB
+ *
+ * @return JSON
+ * @throws IOException if the query is invalid and cannot be converted to JSON
+ */
+ public String build() throws IOException
+ {
+ validateTimes();
+
+ return mapper.toJson(this);
+ }
+
+ protected void validateTimes()
+ {
+ checkState(startAbsolute != null || startRelative != null, "Start time must be specified");
+
+ if (endAbsolute != null)
+ {
+ if (startAbsolute != null)
+ TimeValidator.validateEndTimeLaterThanStartTime(startAbsolute, endAbsolute);
+ else
+ TimeValidator.validateEndTimeLaterThanStartTime(startRelative, endAbsolute);
+ }
+ else if (endRelative != null)
+ {
+ if (startAbsolute != null)
+ TimeValidator.validateEndTimeLaterThanStartTime(startAbsolute, endRelative);
+ else
+ TimeValidator.validateEndTimeLaterThanStartTime(startRelative, endRelative);
+ }
+ }
+
+ @SuppressWarnings("SimplifiableIfStatement")
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ AbstractQueryBuilder> that = (AbstractQueryBuilder>) o;
+
+ if (startAbsolute != null ? !startAbsolute.equals(that.startAbsolute) : that.startAbsolute != null)
+ return false;
+ if (endAbsolute != null ? !endAbsolute.equals(that.endAbsolute) : that.endAbsolute != null)
+ return false;
+ if (startRelative != null ? !startRelative.equals(that.startRelative) : that.startRelative != null)
+ return false;
+ return endRelative != null ? endRelative.equals(that.endRelative) : that.endRelative == null;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = startAbsolute != null ? startAbsolute.hashCode() : 0;
+ result = 31 * result + (endAbsolute != null ? endAbsolute.hashCode() : 0);
+ result = 31 * result + (startRelative != null ? startRelative.hashCode() : 0);
+ result = 31 * result + (endRelative != null ? endRelative.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/src/main/java/org/kairosdb/client/builder/QueryBuilder.java b/src/main/java/org/kairosdb/client/builder/QueryBuilder.java
index 79ab06e..6cf9bf9 100644
--- a/src/main/java/org/kairosdb/client/builder/QueryBuilder.java
+++ b/src/main/java/org/kairosdb/client/builder/QueryBuilder.java
@@ -1,18 +1,3 @@
-/*
- * Copyright 2013 Proofpoint Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
package org.kairosdb.client.builder;
import com.google.common.collect.ListMultimap;
@@ -23,13 +8,12 @@
import org.kairosdb.client.builder.grouper.CustomGrouper;
import org.kairosdb.client.serializer.*;
-import java.io.IOException;
import java.util.ArrayList;
-import java.util.Date;
import java.util.List;
import java.util.TimeZone;
-import static com.google.common.base.Preconditions.*;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
import static org.kairosdb.client.util.Preconditions.checkNotNullOrEmpty;
/**
@@ -46,20 +30,8 @@
* all matching data points that occurred between the last 30 minutes up to and including the last 10 minutes are returned.
*/
@SuppressWarnings("UnusedDeclaration")
-public class QueryBuilder
+public class QueryBuilder extends AbstractQueryBuilder
{
- @SerializedName("start_absolute")
- private Long startAbsolute;
-
- @SerializedName("end_absolute")
- private Long endAbsolute;
-
- @SerializedName("start_relative")
- private RelativeTime startRelative;
-
- @SerializedName("end_relative")
- private RelativeTime endRelative;
-
@SerializedName("cache_time")
private int cacheTime;
@@ -67,9 +39,14 @@ public class QueryBuilder
private TimeZone timeZone;
private List metrics = new ArrayList();
- private transient Gson mapper;
private QueryBuilder()
+ {
+ super();
+ }
+
+ @Override
+ protected Gson buildGson()
{
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(CustomAggregator.class, new CustomAggregatorSerializer());
@@ -78,72 +55,7 @@ private QueryBuilder()
builder.registerTypeAdapter(QueryMetric.Order.class, new OrderSerializer());
builder.registerTypeAdapter(TimeZone.class, new TimeZoneSerializer());
- mapper = builder.create();
- }
-
- /**
- * The beginning time of the time range.
- *
- * @param start start time
- * @return the builder
- */
- public QueryBuilder setStart(Date start)
- {
- checkNotNull(start);
- checkArgument(startRelative == null, "Both relative and absolute start times cannot be set.");
-
- this.startAbsolute = start.getTime();
- checkArgument(startAbsolute <= System.currentTimeMillis(), "Start time cannot be in the future.");
- return this;
- }
-
- /**
- * The beginning time of the time range relative to now. For example, return all data points that starting 2 days
- * ago.
- *
- * @param duration relative time value
- * @param unit unit of time
- * @return the builder
- */
- public QueryBuilder setStart(int duration, TimeUnit unit)
- {
- checkArgument(duration > 0);
- checkNotNull(unit);
- checkArgument(startAbsolute == null, "Both relative and absolute start times cannot be set.");
-
- startRelative = new RelativeTime(duration, unit);
- checkArgument(startRelative.getTimeRelativeTo(System.currentTimeMillis()) <= System.currentTimeMillis(), "Start time cannot be in the future.");
- return this;
- }
-
- /**
- * The ending value of the time range. Must be later in time than the start time. An end time is not required
- * and default to now.
- *
- * @param end end time
- * @return the builder
- */
- public QueryBuilder setEnd(Date end)
- {
- checkArgument(endRelative == null, "Both relative and absolute end times cannot be set.");
- this.endAbsolute = end.getTime();
- return this;
- }
-
- /**
- * The ending time of the time range relative to now.
- *
- * @param duration relative time value
- * @param unit unit of time
- * @return the builder
- */
- public QueryBuilder setEnd(int duration, TimeUnit unit)
- {
- checkNotNull(unit);
- checkArgument(duration > 0);
- checkArgument(endAbsolute == null, "Both relative and absolute end times cannot be set.");
- endRelative = new RelativeTime(duration, unit);
- return this;
+ return builder.create();
}
/**
@@ -184,46 +96,6 @@ public QueryMetric addMetric(String name)
return metric;
}
- /**
- * Returns the absolute range start time.
- *
- * @return absolute range start time
- */
- public Date getStartAbsolute()
- {
- return new Date(startAbsolute);
- }
-
- /**
- * Returns the absolute range end time.
- *
- * @return absolute range end time
- */
- public Date getEndAbsolute()
- {
- return new Date(endAbsolute);
- }
-
- /**
- * Returns the relative range start time.
- *
- * @return relative range start time
- */
- public RelativeTime getStartRelative()
- {
- return startRelative;
- }
-
- /**
- * Returns the relative range end time.
- *
- * @return relative range end time
- */
- public RelativeTime getEndRelative()
- {
- return endRelative;
- }
-
/**
* Returns the cache time.
*
@@ -256,6 +128,7 @@ public TimeZone getTimeZone()
return timeZone;
}
+ @SuppressWarnings("ConstantConditions")
public QueryBuilder setTimeZone(TimeZone timeZone)
{
checkNotNull(timeZone, "timezone cannot be null");
@@ -264,75 +137,63 @@ public QueryBuilder setTimeZone(TimeZone timeZone)
return this;
}
- /**
- * Returns the JSON string built by the builder. This is the JSON that can be used by the client to query KairosDB
- *
- * @return JSON
- * @throws IOException if the query is invalid and cannot be converted to JSON
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
*/
- public String build() throws IOException
- {
- validateTimes();
-
- return mapper.toJson(this);
- }
-
- private void validateTimes()
+ @Override
+ public int hashCode()
{
- checkState(startAbsolute != null || startRelative != null, "Start time must be specified");
-
- if (endAbsolute != null)
- {
- if (startAbsolute != null)
- TimeValidator.validateEndTimeLaterThanStartTime(startAbsolute, endAbsolute);
- else
- TimeValidator.validateEndTimeLaterThanStartTime(startRelative, endAbsolute);
- }
- else if (endRelative != null)
- {
- if (startAbsolute != null)
- TimeValidator.validateEndTimeLaterThanStartTime(startAbsolute, endRelative);
- else
- TimeValidator.validateEndTimeLaterThanStartTime(startRelative, endRelative);
- }
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + cacheTime;
+ result = prime * result + ((metrics == null) ? 0 : metrics.hashCode());
+ result = prime * result + ((timeZone == null) ? 0 : timeZone.hashCode());
+ return result;
}
- @SuppressWarnings("SimplifiableIfStatement")
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
@Override
- public boolean equals(Object o)
+ public boolean equals(Object obj)
{
- if (this == o)
+ if (this == obj)
+ {
return true;
- if (o == null || getClass() != o.getClass())
- return false;
-
- QueryBuilder that = (QueryBuilder) o;
-
- if (cacheTime != that.cacheTime)
- return false;
- if (startAbsolute != null ? !startAbsolute.equals(that.startAbsolute) : that.startAbsolute != null)
+ }
+ if (!super.equals(obj))
+ {
return false;
- if (endAbsolute != null ? !endAbsolute.equals(that.endAbsolute) : that.endAbsolute != null)
+ }
+ if (getClass() != obj.getClass())
+ {
return false;
- if (startRelative != null ? !startRelative.equals(that.startRelative) : that.startRelative != null)
+ }
+ QueryBuilder other = (QueryBuilder) obj;
+ if (cacheTime != other.cacheTime)
+ {
return false;
- if (endRelative != null ? !endRelative.equals(that.endRelative) : that.endRelative != null)
+ }
+ if (metrics == null)
+ {
+ if (other.metrics != null)
+ {
+ return false;
+ }
+ } else if (!metrics.equals(other.metrics))
+ {
return false;
- if (timeZone != null ? !timeZone.equals(that.timeZone) : that.timeZone != null)
+ }
+ if (timeZone == null)
+ {
+ if (other.timeZone != null)
+ {
+ return false;
+ }
+ } else if (!timeZone.equals(other.timeZone))
+ {
return false;
- return !(metrics != null ? !metrics.equals(that.metrics) : that.metrics != null);
- }
-
- @Override
- public int hashCode()
- {
- int result = startAbsolute != null ? startAbsolute.hashCode() : 0;
- result = 31 * result + (endAbsolute != null ? endAbsolute.hashCode() : 0);
- result = 31 * result + (startRelative != null ? startRelative.hashCode() : 0);
- result = 31 * result + (endRelative != null ? endRelative.hashCode() : 0);
- result = 31 * result + cacheTime;
- result = 31 * result + (timeZone != null ? timeZone.hashCode() : 0);
- result = 31 * result + (metrics != null ? metrics.hashCode() : 0);
- return result;
+ }
+ return true;
}
}
\ No newline at end of file
diff --git a/src/main/java/org/kairosdb/client/builder/QueryTagBuilder.java b/src/main/java/org/kairosdb/client/builder/QueryTagBuilder.java
new file mode 100644
index 0000000..6ecea1b
--- /dev/null
+++ b/src/main/java/org/kairosdb/client/builder/QueryTagBuilder.java
@@ -0,0 +1,78 @@
+package org.kairosdb.client.builder;
+
+import com.google.common.collect.ListMultimap;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import org.kairosdb.client.serializer.ListMultiMapSerializer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.kairosdb.client.util.Preconditions.checkNotNullOrEmpty;
+
+/**
+ * Builder used to create the JSON to query tags from KairosDB.
+ *
+ *
+ * This is similar to a regular query but just returns the tags (no data points)
+ * for the range specified. The time range can be specified as absolute or relative.
+ * Absolute times are a given point in time. Relative times are relative to now.
+ * The end time is not required and defaults to now.
+ *
+ *
+ * For example, if you specify a relative start time of 30 minutes, all matching data points for the last 30 minutes
+ * will be returned. If you specify a relative start time of 30 minutes and a relative end time of 10 minutes, then
+ * all matching data points that occurred between the last 30 minutes up to and including the last 10 minutes are returned.
+ */
+public class QueryTagBuilder extends AbstractQueryBuilder
+{
+ private List metrics = new ArrayList();
+
+ private QueryTagBuilder()
+ {
+ super();
+ }
+
+ @Override
+ protected Gson buildGson()
+ {
+ GsonBuilder builder = new GsonBuilder();
+ builder.registerTypeAdapter(ListMultimap.class, new ListMultiMapSerializer());
+ return builder.create();
+ }
+
+ /**
+ * Returns a new query tag builder.
+ *
+ * @return new query tag builder
+ */
+ public static QueryTagBuilder getInstance()
+ {
+ return new QueryTagBuilder();
+ }
+
+ /**
+ * The metric to query tag for.
+ *
+ * @param name metric name
+ * @return the builder
+ */
+ public QueryTagMetric addMetric(String name)
+ {
+ checkNotNullOrEmpty(name, "Name cannot be null or empty.");
+
+ QueryTagMetric metric = new QueryTagMetric(name);
+ metrics.add(metric);
+ return metric;
+ }
+
+ /**
+ * Returns the list metrics to query for tags.
+ *
+ * @return metrics
+ */
+ public List getMetrics()
+ {
+ return metrics;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/kairosdb/client/builder/QueryTagMetric.java b/src/main/java/org/kairosdb/client/builder/QueryTagMetric.java
new file mode 100644
index 0000000..caee45e
--- /dev/null
+++ b/src/main/java/org/kairosdb/client/builder/QueryTagMetric.java
@@ -0,0 +1,79 @@
+package org.kairosdb.client.builder;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
+
+import java.util.Arrays;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.kairosdb.client.util.Preconditions.checkNotNullOrEmpty;
+
+/**
+ * Query request for tags. You can narrow down the query by adding tags.
+ * Only metrics that include the tag and matches one of the values are returned.
+ */
+public class QueryTagMetric
+{
+ @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
+ private final String name;
+
+ private final ListMultimap tags = ArrayListMultimap.create();
+
+ public QueryTagMetric(String name)
+ {
+ this.name = checkNotNullOrEmpty(name, "name cannot be null or empty");
+ }
+
+ /**
+ * Add a map of tags. This narrows the query to only show metadata associated with the tags' values.
+ *
+ * @param tags tags to add
+ * @return the metric
+ */
+ public QueryTagMetric addTags(Map tags)
+ {
+ checkNotNull(tags);
+
+ for (String key : tags.keySet())
+ {
+ this.tags.put(key, tags.get(key));
+ }
+
+ return this;
+ }
+
+ /**
+ * Adds a tag with multiple values. This narrows the query to only show metadata associated with the tag's values.
+ *
+ * @param name tag name
+ * @param values tag values
+ * @return the metric
+ */
+ public QueryTagMetric addTag(String name, String... values)
+ {
+ checkNotNullOrEmpty(name, "name cannot be null or empty");
+ checkArgument(values.length > 0, "value must be greater than 0");
+
+ for (String value : values)
+ {
+ checkNotNullOrEmpty(value, "value cannot be null or empty");
+ }
+
+ tags.putAll(name, Arrays.asList(values));
+
+ return (this);
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public ListMultimap getTags()
+ {
+ return tags;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/kairosdb/client/response/QueryResponse.java b/src/main/java/org/kairosdb/client/response/QueryResponse.java
index 530aa2a..e71ddef 100644
--- a/src/main/java/org/kairosdb/client/response/QueryResponse.java
+++ b/src/main/java/org/kairosdb/client/response/QueryResponse.java
@@ -24,7 +24,7 @@
import java.io.InputStreamReader;
import java.util.*;
-import static com.google.gson.internal.$Gson$Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkNotNull;
/**
* Response returned by KairosDB.
@@ -42,7 +42,7 @@ public class QueryResponse extends Response
public QueryResponse(JsonMapper mapper, int responseCode, InputStream stream) throws IOException
{
super(responseCode);
- this.mapper = checkNotNull(mapper);
+ this.mapper = checkNotNull(mapper, "mapper cannot be null");
this.responseCode = responseCode;
this.body = getBody(stream);
this.queries = getQueries();
diff --git a/src/main/java/org/kairosdb/client/response/QueryTagResponse.java b/src/main/java/org/kairosdb/client/response/QueryTagResponse.java
new file mode 100644
index 0000000..9e0528b
--- /dev/null
+++ b/src/main/java/org/kairosdb/client/response/QueryTagResponse.java
@@ -0,0 +1,114 @@
+package org.kairosdb.client.response;
+
+import com.google.gson.JsonSyntaxException;
+import org.kairosdb.client.JsonMapper;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Response returned by KairosDB.
+ */
+public class QueryTagResponse extends Response
+{
+ private final int responseCode;
+ private final JsonMapper mapper;
+
+ private List results;
+ private String body;
+
+ @SuppressWarnings("ConstantConditions")
+ public QueryTagResponse(JsonMapper mapper, int responseCode, InputStream stream) throws IOException
+ {
+ super(responseCode);
+ this.mapper = checkNotNull(mapper, "mapper cannot be null");
+ this.responseCode = responseCode;
+ this.body = getBody(stream);
+ this.results = getQueries();
+ }
+
+ /**
+ * Returns a list of query results returned by KairosDB. If status code is not
+ * successful, call getErrors to get errors returned.
+ *
+ * @return list of query results or empty list of no data or if an error is returned.
+ * @throws IOException if could not map response to Queries object
+ * @throws JsonSyntaxException if the response is not JSON or is invalid JSON
+ */
+ public List getQueries() throws IOException
+ {
+ if (results != null)
+ return results;
+
+ if (getBody() != null)
+ {
+ // We only get JSON if the response is a 200, 400 or 500 error
+ if (responseCode == 400 || responseCode == 500)
+ {
+ ErrorResponse errorResponse = mapper.fromJson(body, ErrorResponse.class);
+ addErrors(errorResponse.getErrors());
+ return Collections.emptyList();
+ }
+ else if (responseCode == 200)
+ {
+ KairosTagsResponse response = mapper.fromJson(body, KairosTagsResponse.class);
+ return response.getQueries();
+ }
+ }
+
+ return Collections.emptyList();
+ }
+
+ /**
+ * Returns the body response as a string.
+ *
+ * @return body as a string or empty string.
+ */
+ public String getBody()
+ {
+ return body;
+ }
+
+ public String getBody(InputStream stream) throws IOException
+ {
+ if (stream == null)
+ return "";
+
+ StringBuilder builder = new StringBuilder();
+ BufferedReader reader = null;
+ try
+ {
+ reader = new BufferedReader(new InputStreamReader(stream));
+ String line;
+ while ((line = reader.readLine()) != null)
+ {
+ builder.append(line);
+ }
+ }
+ finally
+ {
+ if (reader != null)
+ reader.close();
+ }
+
+ body = builder.toString();
+ return body;
+ }
+
+ private class KairosTagsResponse
+ {
+ private List queries = new ArrayList();
+
+ public List getQueries()
+ {
+ return queries;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/kairosdb/client/response/TagQuery.java b/src/main/java/org/kairosdb/client/response/TagQuery.java
new file mode 100644
index 0000000..e229677
--- /dev/null
+++ b/src/main/java/org/kairosdb/client/response/TagQuery.java
@@ -0,0 +1,22 @@
+package org.kairosdb.client.response;
+
+import java.util.List;
+
+/**
+ * Resulting object from a tag query.
+ */
+public class TagQuery
+{
+ private List results;
+
+ public TagQuery(List results)
+ {
+ this.results = results;
+ }
+
+ public List getResults()
+ {
+ return results;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/kairosdb/client/response/TagResult.java b/src/main/java/org/kairosdb/client/response/TagResult.java
new file mode 100644
index 0000000..dcd10d3
--- /dev/null
+++ b/src/main/java/org/kairosdb/client/response/TagResult.java
@@ -0,0 +1,29 @@
+package org.kairosdb.client.response;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Tag Query Results. This is the results of a single query.
+ */
+public class TagResult
+{
+ private String name;
+ private Map> tags;
+
+ public TagResult(String name, Map> tags)
+ {
+ this.name = name;
+ this.tags = tags;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public Map> getTags()
+ {
+ return tags;
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/kairosdb/client/builder/QueryTagBuilderTest.java b/src/test/java/org/kairosdb/client/builder/QueryTagBuilderTest.java
new file mode 100644
index 0000000..6443b7e
--- /dev/null
+++ b/src/test/java/org/kairosdb/client/builder/QueryTagBuilderTest.java
@@ -0,0 +1,133 @@
+package org.kairosdb.client.builder;
+
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.Date;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+public class QueryTagBuilderTest
+{
+ @Test(expected = NullPointerException.class)
+ public void test_MetricNameNull_Invalid()
+ {
+ QueryTagBuilder.getInstance().addMetric(null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void test_MetricNameEmpty_Invalid()
+ {
+ QueryTagBuilder.getInstance().addMetric("");
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void test_AbsoluteStartNull_Invalid()
+ {
+ QueryTagBuilder.getInstance().setStart(null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void test_AbsoluteStartAndRelativeStartSet_Invalid()
+ {
+ QueryTagBuilder.getInstance().setStart(new Date()).setStart(3, TimeUnit.DAYS);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void test_RelativeStartAndAbsoluteStartSet_Invalid()
+ {
+ QueryTagBuilder.getInstance().setStart(3, TimeUnit.DAYS).setStart(new Date());
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void test_RelativeStartUnitNull_Invalid() throws IOException
+ {
+ QueryTagBuilder.getInstance().setStart(3, null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void test_RelativeStartValueZero_Invalid() throws IOException
+ {
+ QueryTagBuilder.getInstance().setStart(0, TimeUnit.DAYS);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void test_RelativeStartValueNegative_Invalid() throws IOException
+ {
+ QueryTagBuilder.getInstance().setStart(-1, TimeUnit.DAYS);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void test_RelativeEndUnitNull_Invalid() throws IOException
+ {
+ QueryTagBuilder.getInstance().setEnd(3, null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void test_RelativeEndValueZero_Invalid() throws IOException
+ {
+ QueryTagBuilder.getInstance().setEnd(0, TimeUnit.DAYS);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void test_RelativeEndValueNegative_Invalid() throws IOException
+ {
+ QueryTagBuilder.getInstance().setEnd(-1, TimeUnit.DAYS);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void test_startTimeNotSpecified_Invalid() throws IOException
+ {
+ QueryTagBuilder.getInstance().build();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void test_endTimeAbsoluteBeforeStartTimeAbsolute_invalid() throws IOException
+ {
+ QueryTagBuilder.getInstance()
+ .setStart(new Date())
+ .setEnd(new Date(System.currentTimeMillis() - 10000))
+ .build();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void test_endTimeRelativeBeforeThanStartTimeRelative_invalid() throws IOException
+ {
+ QueryTagBuilder.getInstance()
+ .setStart(2, TimeUnit.DAYS)
+ .setEnd(2, TimeUnit.WEEKS)
+ .build();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void test_endTimeRelativeBeforeStartTimeAbsolute_invalid() throws IOException
+ {
+ QueryTagBuilder.getInstance()
+ .setStart(new Date())
+ .setEnd(2, TimeUnit.WEEKS)
+ .build();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void test_endTimeAbsoluteBeforeStartTimeRelative_invalid() throws IOException
+ {
+ QueryTagBuilder.getInstance()
+ .setStart(60, TimeUnit.SECONDS)
+ .setEnd(new Date(1000))
+ .build();
+ }
+
+ @Test
+ public void test() throws IOException
+ {
+ QueryTagBuilder builder = QueryTagBuilder.getInstance()
+ .setStart(1, TimeUnit.HOURS);
+ builder.addMetric("metricName")
+ .addTag("foo", "bar")
+ .addTag("fi", "fum");
+
+ assertThat(builder.build(), equalTo("{\"metrics\":[{\"name\":\"metricName\",\"tags\":{\"fi\":[\"fum\"],\"foo\":[\"bar\"]}}],\"start_relative\":{\"value\":1,\"unit\":\"HOURS\"}}"));
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/kairosdb/client/response/QueryResponseTest.java b/src/test/java/org/kairosdb/client/response/QueryResponseTest.java
index 901d7a9..d540d05 100644
--- a/src/test/java/org/kairosdb/client/response/QueryResponseTest.java
+++ b/src/test/java/org/kairosdb/client/response/QueryResponseTest.java
@@ -121,6 +121,7 @@ public void getQueriesWith200ErrorNoJson() throws IOException
/**
* Verify result if something other than 200, 400, and 500 response code.
*/
+ @Test
public void getQueriesWith300NoJson() throws IOException
{
String responseBody = "Not JSON";
diff --git a/src/test/java/org/kairosdb/client/response/QueryTagResponseTest.java b/src/test/java/org/kairosdb/client/response/QueryTagResponseTest.java
new file mode 100644
index 0000000..c58ff98
--- /dev/null
+++ b/src/test/java/org/kairosdb/client/response/QueryTagResponseTest.java
@@ -0,0 +1,134 @@
+package org.kairosdb.client.response;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Resources;
+import com.google.gson.JsonSyntaxException;
+import org.junit.Before;
+import org.junit.Test;
+import org.kairosdb.client.DataPointTypeRegistry;
+import org.kairosdb.client.JsonMapper;
+import org.kairosdb.client.builder.DataFormatException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.Matchers.hasItems;
+import static org.junit.Assert.assertThat;
+
+public class QueryTagResponseTest
+{
+ private JsonMapper mapper;
+
+ @Before
+ public void setup()
+ {
+ mapper = new JsonMapper(new DataPointTypeRegistry());
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testConstructorNullMapperInvalid() throws IOException
+ {
+ new QueryTagResponse(null, 200, new ByteArrayInputStream("bogus".getBytes()));
+ }
+
+ @Test
+ public void getJson() throws IOException
+ {
+ String json = Resources.toString(Resources.getResource("query_tag_response_valid.json"), Charsets.UTF_8);
+ json = json.replaceAll(System.getProperty("line.separator"), ""); // remove newlines so strings can compare
+
+ QueryTagResponse response = new QueryTagResponse(mapper, 200, new ByteArrayInputStream(json.getBytes()));
+
+ assertThat(response.getBody(), equalTo(json));
+ assertThat(response.getStatusCode(), equalTo(200));
+ assertThat(response.getErrors().size(), equalTo(0));
+ }
+
+ @Test
+ public void getJsonWithErrors() throws IOException
+ {
+ String json = "{\"errors\":[\"query.start_time relative or absolute time must be set\"]}";
+
+ QueryTagResponse response = new QueryTagResponse(mapper, 400, new ByteArrayInputStream(json.getBytes()));
+
+ assertThat(response.getBody(), equalTo(json));
+ assertThat(response.getStatusCode(), equalTo(400));
+ assertThat(response.getErrors().size(), equalTo(1));
+ assertThat(response.getErrors().get(0), equalTo("query.start_time relative or absolute time must be set"));
+ }
+
+ @Test
+ public void getQueriesWithErrors() throws IOException
+ {
+ String json = "{\"errors\":[\"query.start_time relative or absolute time must be set\"]}";
+
+ QueryTagResponse response = new QueryTagResponse(mapper, 400, new ByteArrayInputStream(json.getBytes()));
+
+ assertThat(response.getQueries(), equalTo(Collections.emptyList()));
+ assertThat(response.getBody(), equalTo(json));
+ assertThat(response.getStatusCode(), equalTo(400));
+ assertThat(response.getErrors().size(), equalTo(1));
+ assertThat(response.getErrors().get(0), equalTo("query.start_time relative or absolute time must be set"));
+ }
+
+ @Test
+ public void getQueries() throws IOException, DataFormatException
+ {
+ String json = Resources.toString(Resources.getResource("query_tag_response_valid.json"), Charsets.UTF_8);
+ json = json.replaceAll(System.getProperty("line.separator"), ""); // remove newlines so strings can compare
+
+ QueryTagResponse response = new QueryTagResponse(mapper, 200, new ByteArrayInputStream(json.getBytes()));
+
+ List queries = response.getQueries();
+ assertThat(response.getBody(), equalTo(json));
+ assertThat(response.getStatusCode(), equalTo(200));
+ assertThat(response.getErrors().size(), equalTo(0));
+ assertThat(queries.get(0).getResults().get(0).getTags().get("host"), hasItems("localhost", "host2"));
+ assertThat(queries.get(0).getResults().get(0).getTags().get("rollup"), hasItems("kairos.import_export_unit_test_rollup"));
+ assertThat(queries.get(0).getResults().get(0).getTags().get("status"), hasItems("success"));
+ }
+
+ @Test(expected = JsonSyntaxException.class)
+ public void getQueriesWith400ErrorNoJson() throws IOException
+ {
+ String responseBody = "Not JSON";
+
+ new QueryTagResponse(mapper, 400, new ByteArrayInputStream(responseBody.getBytes()));
+ }
+
+ @Test(expected = JsonSyntaxException.class)
+ public void getQueriesWith500ErrorNoJson() throws IOException
+ {
+ String responseBody = "Not JSON";
+
+ new QueryTagResponse(mapper, 500, new ByteArrayInputStream(responseBody.getBytes()));
+ }
+
+ @Test(expected = JsonSyntaxException.class)
+ public void getQueriesWith200ErrorNoJson() throws IOException
+ {
+ String responseBody = "Not JSON";
+
+ new QueryTagResponse(mapper, 200, new ByteArrayInputStream(responseBody.getBytes()));
+ }
+
+ /**
+ * Verify result if something other than 200, 400, and 500 response code.
+ */
+ @Test
+ public void getQueriesWith300NoJson() throws IOException
+ {
+ String responseBody = "Not JSON";
+
+ QueryTagResponse response = new QueryTagResponse(mapper, 300, new ByteArrayInputStream(responseBody.getBytes()));
+
+ assertThat(response.getQueries(), equalTo(Collections.emptyList()));
+ assertThat(response.getBody(), equalTo(responseBody));
+ assertThat(response.getStatusCode(), equalTo(300));
+ assertThat(response.getErrors().size(), equalTo(0));
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/resources/query_tag_response_valid.json b/src/test/resources/query_tag_response_valid.json
new file mode 100644
index 0000000..ca63de0
--- /dev/null
+++ b/src/test/resources/query_tag_response_valid.json
@@ -0,0 +1,23 @@
+{
+ "queries": [
+ {
+ "results": [
+ {
+ "name": "kairosdb.datastore.query_time",
+ "tags": {
+ "host": [
+ "localhost",
+ "host2"
+ ],
+ "rollup": [
+ "kairos.import_export_unit_test_rollup"
+ ],
+ "status": [
+ "success"
+ ]
+ }
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file