aboutsummaryrefslogtreecommitdiffstats
path: root/container-core/src/main/java/com/yahoo
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@verizonmedia.com>2021-02-02 16:04:50 +0100
committerBjørn Christian Seime <bjorncs@verizonmedia.com>2021-02-02 16:04:50 +0100
commit3ab3ce18ca6c6ef721cba5c11fde3d987816f56f (patch)
treec2a7ff592f3f665b87f4ee4c9182b2bdd555b3cb /container-core/src/main/java/com/yahoo
parent10a9d8ec4d23b0ec8d1fa58d1ba148258f5f2bcb (diff)
Remove usage of org.json
Diffstat (limited to 'container-core/src/main/java/com/yahoo')
-rw-r--r--container-core/src/main/java/com/yahoo/container/handler/metrics/HttpHandlerBase.java23
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/CoredumpGatherer.java25
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/HostLifeGatherer.java31
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/JSONObjectWithLegibleException.java87
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/JsonUtil.java15
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/MetricGatherer.java6
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/MetricsPacketsHandler.java68
-rw-r--r--container-core/src/main/java/com/yahoo/container/jdisc/state/StateHandler.java120
8 files changed, 161 insertions, 214 deletions
diff --git a/container-core/src/main/java/com/yahoo/container/handler/metrics/HttpHandlerBase.java b/container-core/src/main/java/com/yahoo/container/handler/metrics/HttpHandlerBase.java
index 92840cee48f..8c902f88e38 100644
--- a/container-core/src/main/java/com/yahoo/container/handler/metrics/HttpHandlerBase.java
+++ b/container-core/src/main/java/com/yahoo/container/handler/metrics/HttpHandlerBase.java
@@ -1,13 +1,14 @@
// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.handler.metrics;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.container.jdisc.ThreadedHttpRequestHandler;
import com.yahoo.restapi.Path;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
import java.net.URI;
import java.util.List;
@@ -26,6 +27,8 @@ import static java.util.logging.Level.WARNING;
*/
public abstract class HttpHandlerBase extends ThreadedHttpRequestHandler {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
protected HttpHandlerBase(Executor executor) {
super(executor);
}
@@ -49,15 +52,14 @@ public abstract class HttpHandlerBase extends ThreadedHttpRequestHandler {
protected JsonResponse resourceListResponse(URI requestUri, List<String> resources) {
try {
return new JsonResponse(OK, resourceList(requestUri, resources));
- } catch (JSONException e) {
+ } catch (JsonProcessingException e) {
log.log(WARNING, "Bad JSON construction in generated resource list for " + requestUri.getPath(), e);
return new ErrorResponse(INTERNAL_SERVER_ERROR,
"An error occurred when generating the list of api resources.");
}
}
- // TODO: Use jackson with a "Resources" class instead of JSONObject
- private static String resourceList(URI requestUri, List<String> resources) throws JSONException {
+ private static String resourceList(URI requestUri, List<String> resources) throws JsonProcessingException {
int port = requestUri.getPort();
String host = requestUri.getHost();
StringBuilder base = new StringBuilder("http://");
@@ -66,13 +68,14 @@ public abstract class HttpHandlerBase extends ThreadedHttpRequestHandler {
base.append(":").append(port);
}
String uriBase = base.toString();
- JSONArray linkList = new JSONArray();
+ ArrayNode linkList = jsonMapper.createArrayNode();
for (String api : resources) {
- JSONObject resource = new JSONObject();
+ ObjectNode resource = jsonMapper.createObjectNode();
resource.put("url", uriBase + api);
- linkList.put(resource);
+ linkList.add(resource);
}
- return new JSONObject().put("resources", linkList).toString(4);
+ return jsonMapper.writerWithDefaultPrettyPrinter()
+ .writeValueAsString(jsonMapper.createObjectNode().set("resources", linkList));
}
}
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/CoredumpGatherer.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/CoredumpGatherer.java
index d105eaa9d98..f1ef7894511 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/CoredumpGatherer.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/CoredumpGatherer.java
@@ -1,17 +1,16 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.jdisc.state;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.yahoo.vespa.defaults.Defaults;
-import org.json.JSONException;
-import org.json.JSONObject;
import java.io.IOException;
import java.io.UncheckedIOException;
-import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.time.Instant;
-import java.util.Set;
import java.util.stream.Stream;
/**
@@ -19,19 +18,17 @@ import java.util.stream.Stream;
*/
public class CoredumpGatherer {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
private static final Path COREDUMP_PATH = Path.of(Defaults.getDefaults().underVespaHome("var/crash/processing"));
- public static JSONObject gatherCoredumpMetrics(FileWrapper fileWrapper) {
+ public static JsonNode gatherCoredumpMetrics(FileWrapper fileWrapper) {
int coredumps = getNumberOfCoredumps(fileWrapper);
- JSONObject packet = new JSONObject();
-
- try {
- packet.put("status_code", coredumps == 0 ? 0 : 1);
- packet.put("status_msg", coredumps == 0 ? "OK" : String.format("Found %d coredump(s)", coredumps));
- packet.put("timestamp", Instant.now().getEpochSecond());
- packet.put("application", "system-coredumps-processing");
-
- } catch (JSONException e) {}
+ ObjectNode packet = jsonMapper.createObjectNode();
+ packet.put("status_code", coredumps == 0 ? 0 : 1);
+ packet.put("status_msg", coredumps == 0 ? "OK" : String.format("Found %d coredump(s)", coredumps));
+ packet.put("timestamp", Instant.now().getEpochSecond());
+ packet.put("application", "system-coredumps-processing");
return packet;
}
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/HostLifeGatherer.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/HostLifeGatherer.java
index 730f7bc13cd..28f99096d84 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/HostLifeGatherer.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/HostLifeGatherer.java
@@ -1,8 +1,9 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.jdisc.state;
-import org.json.JSONException;
-import org.json.JSONObject;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.nio.file.Path;
@@ -13,9 +14,11 @@ import java.time.Instant;
*/
public class HostLifeGatherer {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
private static final Path UPTIME_PATH = Path.of("/proc");
- public static JSONObject getHostLifePacket(FileWrapper fileWrapper) {
+ public static JsonNode getHostLifePacket(FileWrapper fileWrapper) {
long upTime;
int statusCode = 0;
String statusMessage = "OK";
@@ -29,19 +32,15 @@ public class HostLifeGatherer {
}
- JSONObject jsonObject = new JSONObject();
- try {
- jsonObject.put("status_code", statusCode);
- jsonObject.put("status_msg", statusMessage);
- jsonObject.put("timestamp", Instant.now().getEpochSecond());
- jsonObject.put("application", "host_life");
- JSONObject metrics = new JSONObject();
- metrics.put("uptime", upTime);
- metrics.put("alive", 1);
- jsonObject.put("metrics", metrics);
-
- } catch (JSONException e) {}
-
+ ObjectNode jsonObject = jsonMapper.createObjectNode();
+ jsonObject.put("status_code", statusCode);
+ jsonObject.put("status_msg", statusMessage);
+ jsonObject.put("timestamp", Instant.now().getEpochSecond());
+ jsonObject.put("application", "host_life");
+ ObjectNode metrics = jsonMapper.createObjectNode();
+ metrics.put("uptime", upTime);
+ metrics.put("alive", 1);
+ jsonObject.set("metrics", metrics);
return jsonObject;
}
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/JSONObjectWithLegibleException.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/JSONObjectWithLegibleException.java
deleted file mode 100644
index d22dd9d6f4b..00000000000
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/JSONObjectWithLegibleException.java
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.jdisc.state;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.Collection;
-import java.util.Map;
-
-/**
- * A JSONObject that wraps the checked JSONException in a RuntimeException with a legible error message.
- *
- * @author gjoranv
- */
-class JSONObjectWithLegibleException extends JSONObject {
-
- @Override
- public JSONObject put(String s, boolean b) {
- try {
- return super.put(s, b);
- } catch (JSONException e) {
- throw new RuntimeException(getErrorMessage(s, b, e), e);
- }
- }
-
- @Override
- public JSONObject put(String s, double v) {
- try {
- Double guardedVal = (((Double) v).isNaN() || ((Double) v).isInfinite()) ?
- 0.0 : v;
- return super.put(s, guardedVal);
- } catch (JSONException e) {
- throw new RuntimeException(getErrorMessage(s, v, e), e);
- }
- }
-
- @Override
- public JSONObject put(String s, int i) {
- try {
- return super.put(s, i);
- } catch (JSONException e) {
- throw new RuntimeException(getErrorMessage(s, i, e), e);
- }
- }
-
- @Override
- public JSONObject put(String s, long l) {
- try {
- return super.put(s, l);
- } catch (JSONException e) {
- throw new RuntimeException(getErrorMessage(s, l, e), e);
- }
- }
-
- @Override
- public JSONObject put(String s, Collection collection) {
- try {
- return super.put(s, collection);
- } catch (JSONException e) {
- throw new RuntimeException(getErrorMessage(s, collection, e), e);
- }
- }
-
- @Override
- public JSONObject put(String s, Map map) {
- try {
- return super.put(s, map);
- } catch (JSONException e) {
- throw new RuntimeException(getErrorMessage(s, map, e), e);
- }
- }
-
- @Override
- public JSONObject put(String s, Object o) {
- try {
- return super.put(s, o);
- } catch (JSONException e) {
- throw new RuntimeException(getErrorMessage(s, o, e), e);
- }
- }
-
- private String getErrorMessage(String key, Object value, JSONException e) {
- return "Trying to add invalid JSON object with key '" + key +
- "' and value '" + value + "' - " + e.getMessage();
- }
-
-}
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/JsonUtil.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/JsonUtil.java
new file mode 100644
index 00000000000..4c697fb5ada
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/JsonUtil.java
@@ -0,0 +1,15 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc.state;
+
+/**
+ * @author bjorncs
+ */
+class JsonUtil {
+
+ private JsonUtil() {}
+
+ static double sanitizeDouble(double value) {
+ return (((Double) value).isNaN() || ((Double) value).isInfinite()) ? 0.0 : value;
+ }
+
+}
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricGatherer.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricGatherer.java
index 6a06a6362f5..add69403455 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricGatherer.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricGatherer.java
@@ -1,7 +1,7 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.jdisc.state;
-import org.json.JSONObject;
+import com.fasterxml.jackson.databind.JsonNode;
import java.util.ArrayList;
import java.util.List;
@@ -13,9 +13,9 @@ import java.util.List;
*/
public class MetricGatherer {
- static List<JSONObject> getAdditionalMetrics() {
+ static List<JsonNode> getAdditionalMetrics() {
FileWrapper fileWrapper = new FileWrapper();
- List<JSONObject> packetList = new ArrayList<>();
+ List<JsonNode> packetList = new ArrayList<>();
packetList.add(CoredumpGatherer.gatherCoredumpMetrics(fileWrapper));
if (System.getProperty("os.name").contains("nux"))
packetList.add(HostLifeGatherer.getHostLifePacket(fileWrapper));
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricsPacketsHandler.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricsPacketsHandler.java
index 3d3f0e4b677..824323af3d6 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricsPacketsHandler.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/MetricsPacketsHandler.java
@@ -1,6 +1,11 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.jdisc.state;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.inject.Inject;
import com.yahoo.collections.Tuple2;
import com.yahoo.component.provider.ComponentRegistry;
@@ -13,9 +18,6 @@ import com.yahoo.jdisc.handler.ResponseDispatch;
import com.yahoo.jdisc.handler.ResponseHandler;
import com.yahoo.jdisc.http.HttpHeaders;
import com.yahoo.metrics.MetricsPresentationConfig;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
@@ -26,6 +28,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
+import static com.yahoo.container.jdisc.state.JsonUtil.sanitizeDouble;
import static com.yahoo.container.jdisc.state.StateHandler.getSnapshotPreprocessor;
/**
@@ -44,6 +47,8 @@ import static com.yahoo.container.jdisc.state.StateHandler.getSnapshotPreprocess
*/
public class MetricsPacketsHandler extends AbstractRequestHandler {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
static final String APPLICATION_KEY = "application";
static final String TIMESTAMP_KEY = "timestamp";
static final String STATUS_CODE_KEY = "status_code";
@@ -97,19 +102,19 @@ public class MetricsPacketsHandler extends AbstractRequestHandler {
}
String output = jsonToString(getStatusPacket()) + getAllMetricsPackets() + "\n";
return output.getBytes(StandardCharsets.UTF_8);
- } catch (JSONException e) {
+ } catch (JsonProcessingException e) {
throw new RuntimeException("Bad JSON construction.", e);
}
}
- private byte[] getMetricsArray() throws JSONException {
- JSONObject root = new JSONObject();
- JSONArray jsonArray = new JSONArray();
- jsonArray.put(getStatusPacket());
+ private byte[] getMetricsArray() throws JsonProcessingException {
+ ObjectNode root = jsonMapper.createObjectNode();
+ ArrayNode jsonArray = jsonMapper.createArrayNode();
+ jsonArray.add(getStatusPacket());
getPacketsForSnapshot(getSnapshot(), applicationName, timer.currentTimeMillis())
- .forEach(jsonArray::put);
- MetricGatherer.getAdditionalMetrics().forEach(jsonArray::put);
- root.put("metrics", jsonArray);
+ .forEach(jsonArray::add);
+ MetricGatherer.getAdditionalMetrics().forEach(jsonArray::add);
+ root.set("metrics", jsonArray);
return jsonToString(root)
.getBytes(StandardCharsets.UTF_8);
}
@@ -117,8 +122,8 @@ public class MetricsPacketsHandler extends AbstractRequestHandler {
/**
* Exactly one status packet is added to the response.
*/
- private JSONObject getStatusPacket() throws JSONException {
- JSONObject packet = new JSONObjectWithLegibleException();
+ private JsonNode getStatusPacket() {
+ ObjectNode packet = jsonMapper.createObjectNode();
packet.put(APPLICATION_KEY, applicationName);
StateMonitor.Status status = monitor.status();
@@ -127,14 +132,15 @@ public class MetricsPacketsHandler extends AbstractRequestHandler {
return packet;
}
- private String jsonToString(JSONObject jsonObject) throws JSONException {
- return jsonObject.toString(4);
+ private static String jsonToString(JsonNode jsonObject) throws JsonProcessingException {
+ return jsonMapper.writerWithDefaultPrettyPrinter()
+ .writeValueAsString(jsonObject);
}
- private String getAllMetricsPackets() throws JSONException {
+ private String getAllMetricsPackets() throws JsonProcessingException {
StringBuilder ret = new StringBuilder();
- List<JSONObject> metricsPackets = getPacketsForSnapshot(getSnapshot(), applicationName, timer.currentTimeMillis());
- for (JSONObject packet : metricsPackets) {
+ List<JsonNode> metricsPackets = getPacketsForSnapshot(getSnapshot(), applicationName, timer.currentTimeMillis());
+ for (JsonNode packet : metricsPackets) {
ret.append(PACKET_SEPARATOR); // For legibility and parsing in unit tests
ret.append(jsonToString(packet));
}
@@ -150,16 +156,16 @@ public class MetricsPacketsHandler extends AbstractRequestHandler {
}
}
- private List<JSONObject> getPacketsForSnapshot(MetricSnapshot metricSnapshot, String application, long timestamp) throws JSONException {
+ private List<JsonNode> getPacketsForSnapshot(MetricSnapshot metricSnapshot, String application, long timestamp) {
if (metricSnapshot == null) return Collections.emptyList();
- List<JSONObject> packets = new ArrayList<>();
+ List<JsonNode> packets = new ArrayList<>();
for (Map.Entry<MetricDimensions, MetricSet> snapshotEntry : metricSnapshot) {
MetricDimensions metricDimensions = snapshotEntry.getKey();
MetricSet metricSet = snapshotEntry.getValue();
- JSONObjectWithLegibleException packet = new JSONObjectWithLegibleException();
+ ObjectNode packet = jsonMapper.createObjectNode();
addMetaData(timestamp, application, packet);
addDimensions(metricDimensions, packet);
addMetrics(metricSet, packet);
@@ -168,27 +174,27 @@ public class MetricsPacketsHandler extends AbstractRequestHandler {
return packets;
}
- private void addMetaData(long timestamp, String application, JSONObjectWithLegibleException packet) {
+ private void addMetaData(long timestamp, String application, ObjectNode packet) {
packet.put(APPLICATION_KEY, application);
packet.put(TIMESTAMP_KEY, TimeUnit.MILLISECONDS.toSeconds(timestamp));
}
- private void addDimensions(MetricDimensions metricDimensions, JSONObjectWithLegibleException packet) throws JSONException {
+ private void addDimensions(MetricDimensions metricDimensions, ObjectNode packet) {
if (metricDimensions == null) return;
Iterator<Map.Entry<String, String>> dimensionsIterator = metricDimensions.iterator();
if (dimensionsIterator.hasNext()) {
- JSONObject jsonDim = new JSONObjectWithLegibleException();
- packet.put(DIMENSIONS_KEY, jsonDim);
+ ObjectNode jsonDim = jsonMapper.createObjectNode();
+ packet.set(DIMENSIONS_KEY, jsonDim);
for (Map.Entry<String, String> dimensionEntry : metricDimensions) {
jsonDim.put(dimensionEntry.getKey(), dimensionEntry.getValue());
}
}
}
- private void addMetrics(MetricSet metricSet, JSONObjectWithLegibleException packet) throws JSONException {
- JSONObjectWithLegibleException metrics = new JSONObjectWithLegibleException();
- packet.put(METRICS_KEY, metrics);
+ private void addMetrics(MetricSet metricSet, ObjectNode packet) {
+ ObjectNode metrics = jsonMapper.createObjectNode();
+ packet.set(METRICS_KEY, metrics);
for (Map.Entry<String, MetricValue> metric : metricSet) {
String name = metric.getKey();
MetricValue value = metric.getValue();
@@ -196,9 +202,9 @@ public class MetricsPacketsHandler extends AbstractRequestHandler {
metrics.put(name + ".count", ((CountMetric) value).getCount());
} else if (value instanceof GaugeMetric) {
GaugeMetric gauge = (GaugeMetric) value;
- metrics.put(name + ".average", gauge.getAverage())
- .put(name + ".last", gauge.getLast())
- .put(name + ".max", gauge.getMax());
+ metrics.put(name + ".average", sanitizeDouble(gauge.getAverage()))
+ .put(name + ".last", sanitizeDouble(gauge.getLast()))
+ .put(name + ".max", sanitizeDouble(gauge.getMax()));
if (gauge.getPercentiles().isPresent()) {
for (Tuple2<String, Double> prefixAndValue : gauge.getPercentiles().get()) {
metrics.put(name + "." + prefixAndValue.first + "percentile", prefixAndValue.second.doubleValue());
diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/state/StateHandler.java b/container-core/src/main/java/com/yahoo/container/jdisc/state/StateHandler.java
index b14dc50edcb..5ac2871d9dd 100644
--- a/container-core/src/main/java/com/yahoo/container/jdisc/state/StateHandler.java
+++ b/container-core/src/main/java/com/yahoo/container/jdisc/state/StateHandler.java
@@ -1,6 +1,11 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.jdisc.state;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.inject.Inject;
import com.yahoo.collections.Tuple2;
import com.yahoo.component.Vtag;
@@ -16,21 +21,18 @@ import com.yahoo.jdisc.handler.ResponseHandler;
import com.yahoo.jdisc.http.HttpHeaders;
import com.yahoo.metrics.MetricsPresentationConfig;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
import java.net.URI;
import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
-import java.io.PrintStream;
-import java.io.ByteArrayOutputStream;
+
+import static com.yahoo.container.jdisc.state.JsonUtil.sanitizeDouble;
/**
* A handler which returns state (health) information from this container instance: Status, metrics and vespa version.
@@ -39,6 +41,8 @@ import java.io.ByteArrayOutputStream;
*/
public class StateHandler extends AbstractRequestHandler {
+ private static final ObjectMapper jsonMapper = new ObjectMapper();
+
public static final String STATE_API_ROOT = "/state/v1";
private static final String METRICS_PATH = "metrics";
private static final String HISTOGRAMS_PATH = "metrics/histograms";
@@ -124,17 +128,16 @@ public class StateHandler extends AbstractRequestHandler {
}
base.append(STATE_API_ROOT);
String uriBase = base.toString();
- JSONArray linkList = new JSONArray();
+ ArrayNode linkList = jsonMapper.createArrayNode();
for (String api : new String[] {METRICS_PATH, CONFIG_GENERATION_PATH, HEALTH_PATH, VERSION_PATH}) {
- JSONObject resource = new JSONObject();
+ ObjectNode resource = jsonMapper.createObjectNode();
resource.put("url", uriBase + "/" + api);
- linkList.put(resource);
+ linkList.add(resource);
}
- return new JSONObjectWithLegibleException()
- .put("resources", linkList)
- .toString(4).getBytes(StandardCharsets.UTF_8);
- } catch (JSONException e) {
- throw new RuntimeException("Bad JSON construction.", e);
+ JsonNode resources = jsonMapper.createObjectNode().set("resources", linkList);
+ return toPrettyString(resources);
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException("Bad JSON construction", e);
}
}
@@ -154,31 +157,31 @@ public class StateHandler extends AbstractRequestHandler {
private static byte[] buildConfigOutput(ApplicationMetadataConfig config) {
try {
- return new JSONObjectWithLegibleException()
- .put(CONFIG_GENERATION_PATH, new JSONObjectWithLegibleException()
- .put("generation", config.generation())
- .put("container", new JSONObjectWithLegibleException()
- .put("generation", config.generation())))
- .toString(4).getBytes(StandardCharsets.UTF_8);
- } catch (JSONException e) {
+ return toPrettyString(
+ jsonMapper.createObjectNode()
+ .set(CONFIG_GENERATION_PATH, jsonMapper.createObjectNode()
+ .put("generation", config.generation())
+ .set("container", jsonMapper.createObjectNode()
+ .put("generation", config.generation()))));
+ } catch (JsonProcessingException e) {
throw new RuntimeException("Bad JSON construction.", e);
}
}
private static byte[] buildVersionOutput() {
try {
- return new JSONObjectWithLegibleException()
- .put("version", Vtag.currentVersion)
- .toString(4).getBytes(StandardCharsets.UTF_8);
- } catch (JSONException e) {
+ return toPrettyString(
+ jsonMapper.createObjectNode()
+ .put("version", Vtag.currentVersion.toString()));
+ } catch (JsonProcessingException e) {
throw new RuntimeException("Bad JSON construction.", e);
}
}
private byte[] buildMetricOutput(String consumer) {
try {
- return buildJsonForConsumer(consumer).toString(4).getBytes(StandardCharsets.UTF_8);
- } catch (JSONException e) {
+ return toPrettyString(buildJsonForConsumer(consumer));
+ } catch (JsonProcessingException e) {
throw new RuntimeException("Bad JSON construction.", e);
}
}
@@ -191,11 +194,11 @@ public class StateHandler extends AbstractRequestHandler {
return baos.toByteArray();
}
- private JSONObjectWithLegibleException buildJsonForConsumer(String consumer) throws JSONException {
- JSONObjectWithLegibleException ret = new JSONObjectWithLegibleException();
+ private ObjectNode buildJsonForConsumer(String consumer) {
+ ObjectNode ret = jsonMapper.createObjectNode();
ret.put("time", timer.currentTimeMillis());
- ret.put("status", new JSONObjectWithLegibleException().put("code", getStatus().name()));
- ret.put(METRICS_PATH, buildJsonForSnapshot(consumer, getSnapshot()));
+ ret.set("status", jsonMapper.createObjectNode().put("code", getStatus().name()));
+ ret.set(METRICS_PATH, buildJsonForSnapshot(consumer, getSnapshot()));
return ret;
}
@@ -212,57 +215,62 @@ public class StateHandler extends AbstractRequestHandler {
return monitor.status();
}
- private JSONObjectWithLegibleException buildJsonForSnapshot(String consumer, MetricSnapshot metricSnapshot) throws JSONException {
+ private ObjectNode buildJsonForSnapshot(String consumer, MetricSnapshot metricSnapshot) {
if (metricSnapshot == null) {
- return new JSONObjectWithLegibleException();
+ return jsonMapper.createObjectNode();
}
- JSONObjectWithLegibleException jsonMetric = new JSONObjectWithLegibleException();
- jsonMetric.put("snapshot", new JSONObjectWithLegibleException()
- .put("from", metricSnapshot.getFromTime(TimeUnit.MILLISECONDS) / 1000.0)
- .put("to", metricSnapshot.getToTime(TimeUnit.MILLISECONDS) / 1000.0));
+ ObjectNode jsonMetric = jsonMapper.createObjectNode();
+ jsonMetric.set("snapshot", jsonMapper.createObjectNode()
+ .put("from", sanitizeDouble(metricSnapshot.getFromTime(TimeUnit.MILLISECONDS) / 1000.0))
+ .put("to", sanitizeDouble(metricSnapshot.getToTime(TimeUnit.MILLISECONDS) / 1000.0)));
boolean includeDimensions = !consumer.equals(HEALTH_PATH);
long periodInMillis = metricSnapshot.getToTime(TimeUnit.MILLISECONDS) -
metricSnapshot.getFromTime(TimeUnit.MILLISECONDS);
for (Tuple tuple : collapseMetrics(metricSnapshot, consumer)) {
- JSONObjectWithLegibleException jsonTuple = new JSONObjectWithLegibleException();
+ ObjectNode jsonTuple = jsonMapper.createObjectNode();
jsonTuple.put("name", tuple.key);
if (tuple.val instanceof CountMetric) {
CountMetric count = (CountMetric)tuple.val;
- jsonTuple.put("values", new JSONObjectWithLegibleException()
+ jsonTuple.set("values", jsonMapper.createObjectNode()
.put("count", count.getCount())
- .put("rate", (count.getCount() * 1000.0) / periodInMillis));
+ .put("rate", sanitizeDouble(count.getCount() * 1000.0) / periodInMillis));
} else if (tuple.val instanceof GaugeMetric) {
GaugeMetric gauge = (GaugeMetric) tuple.val;
- JSONObjectWithLegibleException valueFields = new JSONObjectWithLegibleException();
- valueFields.put("average", gauge.getAverage())
- .put("sum", gauge.getSum())
+ ObjectNode valueFields = jsonMapper.createObjectNode();
+ valueFields.put("average", sanitizeDouble(gauge.getAverage()))
+ .put("sum", sanitizeDouble(gauge.getSum()))
.put("count", gauge.getCount())
- .put("last", gauge.getLast())
- .put("max", gauge.getMax())
- .put("min", gauge.getMin())
- .put("rate", (gauge.getCount() * 1000.0) / periodInMillis);
+ .put("last", sanitizeDouble(gauge.getLast()))
+ .put("max", sanitizeDouble(gauge.getMax()))
+ .put("min", sanitizeDouble(gauge.getMin()))
+ .put("rate", sanitizeDouble((gauge.getCount() * 1000.0) / periodInMillis));
if (gauge.getPercentiles().isPresent()) {
for (Tuple2<String, Double> prefixAndValue : gauge.getPercentiles().get()) {
- valueFields.put(prefixAndValue.first + "percentile", prefixAndValue.second.doubleValue());
+ valueFields.put(prefixAndValue.first + "percentile", sanitizeDouble(prefixAndValue.second));
}
}
- jsonTuple.put("values", valueFields);
+ jsonTuple.set("values", valueFields);
} else {
throw new UnsupportedOperationException(tuple.val.getClass().getName());
}
if (tuple.dim != null) {
Iterator<Map.Entry<String, String>> it = tuple.dim.iterator();
if (it.hasNext() && includeDimensions) {
- JSONObjectWithLegibleException jsonDim = new JSONObjectWithLegibleException();
+ ObjectNode jsonDim = jsonMapper.createObjectNode();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
jsonDim.put(entry.getKey(), entry.getValue());
}
- jsonTuple.put("dimensions", jsonDim);
+ jsonTuple.set("dimensions", jsonDim);
}
}
- jsonMetric.append("values", jsonTuple);
+ ArrayNode values = (ArrayNode) jsonMetric.get("values");
+ if (values == null) {
+ values = jsonMapper.createArrayNode();
+ jsonMetric.set("values", values);
+ }
+ values.add(jsonTuple);
}
return jsonMetric;
}
@@ -316,6 +324,12 @@ public class StateHandler extends AbstractRequestHandler {
return metrics;
}
+ private static byte[] toPrettyString(JsonNode resources) throws JsonProcessingException {
+ return jsonMapper.writerWithDefaultPrettyPrinter()
+ .writeValueAsString(resources)
+ .getBytes();
+ }
+
static class Tuple {
final MetricDimensions dim;