diff options
author | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2021-01-28 17:49:25 +0100 |
---|---|---|
committer | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2021-01-29 17:57:26 +0100 |
commit | c4536e50f0135c92a4093b45931178c50e9ff2f9 (patch) | |
tree | 9b5508b9c44cd2fee124d105ee50ec4284c3c94c | |
parent | 527035ccb63501f3e0b3f23157c2cd902eef551a (diff) |
Remove usage of org.json:json
Remove most usage of org.json:json Maven artifact.
This library does not have a compatible license.
Some usage is still left as it's part of our container-search public API.
We'll need a major release to fix that.
See https://github.com/vespa-engine/vespa/issues/14762 for more details.
43 files changed, 812 insertions, 908 deletions
diff --git a/bundle-plugin-test/integration-test/src/test/java/com/yahoo/container/plugin/BundleTest.java b/bundle-plugin-test/integration-test/src/test/java/com/yahoo/container/plugin/BundleTest.java index a46abce1dff..35a95ed3d89 100644 --- a/bundle-plugin-test/integration-test/src/test/java/com/yahoo/container/plugin/BundleTest.java +++ b/bundle-plugin-test/integration-test/src/test/java/com/yahoo/container/plugin/BundleTest.java @@ -91,7 +91,6 @@ public class BundleTest { // From SimpleSearcher assertThat(importPackage, containsString("com.yahoo.prelude.hitfield")); - assertThat(importPackage, containsString("org.json")); // From SimpleSearcher2 assertThat(importPackage, containsString("com.yahoo.processing")); diff --git a/bundle-plugin-test/test-bundles/main/pom.xml b/bundle-plugin-test/test-bundles/main/pom.xml index c9c9ea270eb..190e1c9d90f 100644 --- a/bundle-plugin-test/test-bundles/main/pom.xml +++ b/bundle-plugin-test/test-bundles/main/pom.xml @@ -16,11 +16,6 @@ <packaging>container-plugin</packaging> <dependencies> <dependency> - <groupId>org.json</groupId> - <artifactId>json</artifactId> - <scope>provided</scope> - </dependency> - <dependency> <groupId>com.yahoo.vespa</groupId> <artifactId>jrt</artifactId> <version>${project.version}</version> diff --git a/bundle-plugin-test/test-bundles/main/src/main/java/com/yahoo/test/SimpleSearcher.java b/bundle-plugin-test/test-bundles/main/src/main/java/com/yahoo/test/SimpleSearcher.java index dddca3f4d59..ae9644aa010 100644 --- a/bundle-plugin-test/test-bundles/main/src/main/java/com/yahoo/test/SimpleSearcher.java +++ b/bundle-plugin-test/test-bundles/main/src/main/java/com/yahoo/test/SimpleSearcher.java @@ -8,8 +8,6 @@ import com.yahoo.search.Searcher; import com.yahoo.search.result.Hit; import com.yahoo.search.searchchain.Execution; import com.yahoo.text.BooleanParser; -import org.json.JSONException; -import org.json.JSONObject; /** * A searcher adding a new hit. @@ -19,19 +17,13 @@ import org.json.JSONObject; public class SimpleSearcher extends Searcher { public Result search(Query query,Execution execution) { - try { BooleanParser.parseBoolean("true"); XMLString xmlString = new XMLString("<sampleXmlString/>"); Hit hit = new Hit("Hello world!"); - hit.setField("json", new JSONObject().put("price", 42).toString()); Result result = execution.search(query); result.hits().add(hit); return result; - - } catch (JSONException e) { - throw new RuntimeException(e); - } } } diff --git a/cloud-tenant-base-dependencies-enforcer/pom.xml b/cloud-tenant-base-dependencies-enforcer/pom.xml index a8655a82860..8bb7b75be89 100644 --- a/cloud-tenant-base-dependencies-enforcer/pom.xml +++ b/cloud-tenant-base-dependencies-enforcer/pom.xml @@ -34,7 +34,7 @@ <junit5.version>5.7.0</junit5.version> <junit5.platform.version>1.7.0</junit5.platform.version> <org.lz4.version>1.7.1</org.lz4.version> - <org.json.version>20090211</org.json.version> + <org.json.version>20090211</org.json.version><!-- TODO Vespa 8: remove as provided dependency --> <slf4j.version>1.7.5</slf4j.version> <tensorflow.version>1.12.0</tensorflow.version> <xml-apis.version>1.4.01</xml-apis.version> diff --git a/container-core/pom.xml b/container-core/pom.xml index 7c98b524c73..051b572b28f 100644 --- a/container-core/pom.xml +++ b/container-core/pom.xml @@ -42,6 +42,7 @@ <scope>test</scope> </dependency> <dependency> + <!-- TODO Vespa 8: stop providing org.json:json --> <groupId>org.json</groupId> <artifactId>json</artifactId> </dependency> 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/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..e8d829eafb1 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; @@ -44,6 +46,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 +101,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 +121,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 +131,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 +155,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 +173,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(); 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..1eb34e15258 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,16 @@ 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; /** * A handler which returns state (health) information from this container instance: Status, metrics and vespa version. @@ -39,6 +39,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 +126,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 +155,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 +192,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,12 +213,12 @@ 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() + ObjectNode jsonMetric = jsonMapper.createObjectNode(); + jsonMetric.set("snapshot", jsonMapper.createObjectNode() .put("from", metricSnapshot.getFromTime(TimeUnit.MILLISECONDS) / 1000.0) .put("to", metricSnapshot.getToTime(TimeUnit.MILLISECONDS) / 1000.0)); @@ -225,16 +226,16 @@ public class StateHandler extends AbstractRequestHandler { 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)); } else if (tuple.val instanceof GaugeMetric) { GaugeMetric gauge = (GaugeMetric) tuple.val; - JSONObjectWithLegibleException valueFields = new JSONObjectWithLegibleException(); + ObjectNode valueFields = jsonMapper.createObjectNode(); valueFields.put("average", gauge.getAverage()) .put("sum", gauge.getSum()) .put("count", gauge.getCount()) @@ -247,22 +248,27 @@ public class StateHandler extends AbstractRequestHandler { valueFields.put(prefixAndValue.first + "percentile", prefixAndValue.second.doubleValue()); } } - 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 +322,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; diff --git a/container-core/src/main/java/org/json/package-info.java b/container-core/src/main/java/org/json/package-info.java index 44630ad235a..7ca9fe91e31 100644 --- a/container-core/src/main/java/org/json/package-info.java +++ b/container-core/src/main/java/org/json/package-info.java @@ -1,5 +1,5 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. @ExportPackage package org.json; - +// TODO Vespa 8: stop providing org.json import com.yahoo.osgi.annotation.ExportPackage; diff --git a/container-core/src/test/java/com/yahoo/container/handler/metrics/MetricsV2HandlerTest.java b/container-core/src/test/java/com/yahoo/container/handler/metrics/MetricsV2HandlerTest.java index 9020ed91026..ca4bec30322 100644 --- a/container-core/src/test/java/com/yahoo/container/handler/metrics/MetricsV2HandlerTest.java +++ b/container-core/src/test/java/com/yahoo/container/handler/metrics/MetricsV2HandlerTest.java @@ -1,17 +1,18 @@ // Copyright 2019 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.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; import com.github.tomakehurst.wiremock.junit.WireMockRule; import com.yahoo.container.jdisc.RequestHandlerTestDriver; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; import org.junit.Before; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import java.io.BufferedReader; +import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.concurrent.Executors; @@ -34,6 +35,8 @@ import static org.junit.Assert.fail; */ public class MetricsV2HandlerTest { + private static final ObjectMapper jsonMapper = new ObjectMapper(); + private static final String URI_BASE = "http://localhost"; private static final String V2_URI = URI_BASE + V2_PATH; @@ -79,29 +82,29 @@ public class MetricsV2HandlerTest { @Test public void v2_response_contains_values_uri() throws Exception { String response = testDriver.sendRequest(V2_URI).readAll(); - JSONObject root = new JSONObject(response); + JsonNode root = jsonMapper.readTree(response); assertTrue(root.has("resources")); - JSONArray resources = root.getJSONArray("resources"); - assertEquals(1, resources.length()); + ArrayNode resources = (ArrayNode) root.get("resources"); + assertEquals(1, resources.size()); - JSONObject valuesUri = resources.getJSONObject(0); - assertEquals(VALUES_URI, valuesUri.getString("url")); + JsonNode valuesUri = resources.get(0); + assertEquals(VALUES_URI, valuesUri.get("url").textValue()); } @Ignore @Test - public void visually_inspect_values_response() throws Exception { - JSONObject responseJson = getResponseAsJson(null); - System.out.println(responseJson.toString(4)); + public void visually_inspect_values_response() { + JsonNode responseJson = getResponseAsJson(null); + System.out.println(responseJson); } @Test public void invalid_path_yields_error_response() throws Exception { String response = testDriver.sendRequest(V2_URI + "/invalid").readAll(); - JSONObject root = new JSONObject(response); + JsonNode root = jsonMapper.readTree(response); assertTrue(root.has("error")); - assertTrue(root.getString("error" ).startsWith("No content")); + assertTrue(root.get("error" ).textValue().startsWith("No content")); } @Test @@ -111,23 +114,23 @@ public class MetricsV2HandlerTest { } @Test - public void consumer_is_propagated_to_metrics_proxy_api() throws JSONException { - JSONObject responseJson = getResponseAsJson(CUSTOM_CONSUMER); + public void consumer_is_propagated_to_metrics_proxy_api() { + JsonNode responseJson = getResponseAsJson(CUSTOM_CONSUMER); - JSONObject firstNodeMetricsValues = - responseJson.getJSONArray("nodes").getJSONObject(0) - .getJSONObject("node") - .getJSONArray("metrics").getJSONObject(0) - .getJSONObject("values"); + JsonNode firstNodeMetricsValues = + responseJson.get("nodes").get(0) + .get("node") + .get("metrics").get(0) + .get("values"); assertTrue(firstNodeMetricsValues.has(REPLACED_CPU_METRIC)); } - private JSONObject getResponseAsJson(String consumer) { + private JsonNode getResponseAsJson(String consumer) { String response = testDriver.sendRequest(VALUES_URI + consumerQuery(consumer)).readAll(); try { - return new JSONObject(response); - } catch (JSONException e) { + return jsonMapper.readTree(response); + } catch (IOException e) { fail("Failed to create json object: " + e.getMessage()); throw new RuntimeException(e); } diff --git a/container-core/src/test/java/com/yahoo/container/handler/metrics/PrometheusV1HandlerTest.java b/container-core/src/test/java/com/yahoo/container/handler/metrics/PrometheusV1HandlerTest.java index a0e8c131c2b..9ffce6d1c28 100644 --- a/container-core/src/test/java/com/yahoo/container/handler/metrics/PrometheusV1HandlerTest.java +++ b/container-core/src/test/java/com/yahoo/container/handler/metrics/PrometheusV1HandlerTest.java @@ -1,15 +1,11 @@ // Copyright 2019 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.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; import com.github.tomakehurst.wiremock.junit.WireMockRule; import com.yahoo.container.jdisc.RequestHandlerTestDriver; -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.stream.Collectors; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; import org.junit.Before; import org.junit.Ignore; import org.junit.Rule; @@ -26,13 +22,14 @@ import static com.yahoo.container.handler.metrics.MetricsV2Handler.consumerQuery import static com.yahoo.container.handler.metrics.MetricsV2HandlerTest.getFileContents; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; /** * @author gjoranv */ public class PrometheusV1HandlerTest { + private static final ObjectMapper jsonMapper = new ObjectMapper(); + private static final String URI_BASE = "http://localhost"; private static final String V1_URI = URI_BASE + PrometheusV1Handler.V1_PATH; @@ -79,14 +76,14 @@ public class PrometheusV1HandlerTest { @Test public void v1_response_contains_values_uri() throws Exception { String response = testDriver.sendRequest(V1_URI).readAll(); - JSONObject root = new JSONObject(response); + JsonNode root = jsonMapper.readTree(response); assertTrue(root.has("resources")); - JSONArray resources = root.getJSONArray("resources"); - assertEquals(1, resources.length()); + ArrayNode resources = (ArrayNode) root.get("resources"); + assertEquals(1, resources.size()); - JSONObject valuesUri = resources.getJSONObject(0); - assertEquals(VALUES_URI, valuesUri.getString("url")); + JsonNode valuesUri = resources.get(0); + assertEquals(VALUES_URI, valuesUri.get("url").asText()); } @Ignore @@ -99,9 +96,9 @@ public class PrometheusV1HandlerTest { @Test public void invalid_path_yields_error_response() throws Exception { String response = testDriver.sendRequest(V1_URI + "/invalid").readAll(); - JSONObject root = new JSONObject(response); + JsonNode root = jsonMapper.readTree(response); assertTrue(root.has("error")); - assertTrue(root.getString("error" ).startsWith("No content")); + assertTrue(root.get("error" ).textValue().startsWith("No content")); } @Test diff --git a/container-core/src/test/java/com/yahoo/container/jdisc/state/CoredumpGathererTest.java b/container-core/src/test/java/com/yahoo/container/jdisc/state/CoredumpGathererTest.java index c1f7d790fa5..8a3d0e837c5 100644 --- a/container-core/src/test/java/com/yahoo/container/jdisc/state/CoredumpGathererTest.java +++ b/container-core/src/test/java/com/yahoo/container/jdisc/state/CoredumpGathererTest.java @@ -1,8 +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.JSONException; -import org.json.JSONObject; +import com.fasterxml.jackson.databind.JsonNode; import org.junit.Test; import java.nio.file.Path; @@ -17,12 +16,12 @@ import static org.junit.Assert.assertEquals; public class CoredumpGathererTest { @Test - public void finds_one_coredump() throws JSONException { - JSONObject packet = CoredumpGatherer.gatherCoredumpMetrics(new MockFileWrapper()); + public void finds_one_coredump() { + JsonNode packet = CoredumpGatherer.gatherCoredumpMetrics(new MockFileWrapper()); - assertEquals("system-coredumps-processing", packet.getString("application")); - assertEquals(1, packet.getInt("status_code")); - assertEquals("Found 1 coredump(s)", packet.getString("status_msg")); + assertEquals("system-coredumps-processing", packet.get("application").textValue()); + assertEquals(1, packet.get("status_code").intValue()); + assertEquals("Found 1 coredump(s)", packet.get("status_msg").textValue()); } diff --git a/container-core/src/test/java/com/yahoo/container/jdisc/state/HostLifeGathererTest.java b/container-core/src/test/java/com/yahoo/container/jdisc/state/HostLifeGathererTest.java index d025b9662d2..12852c9d54c 100644 --- a/container-core/src/test/java/com/yahoo/container/jdisc/state/HostLifeGathererTest.java +++ b/container-core/src/test/java/com/yahoo/container/jdisc/state/HostLifeGathererTest.java @@ -1,8 +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.JSONException; -import org.json.JSONObject; +import com.fasterxml.jackson.databind.JsonNode; import org.junit.Test; import java.nio.file.Path; @@ -16,15 +15,15 @@ import static org.junit.Assert.assertEquals; public class HostLifeGathererTest { @Test - public void host_is_alive() throws JSONException { - JSONObject packet = HostLifeGatherer.getHostLifePacket(new MockFileWrapper()); - JSONObject metrics = packet.getJSONObject("metrics"); - assertEquals("host_life", packet.getString("application")); - assertEquals(0, packet.getInt("status_code")); - assertEquals("OK", packet.getString("status_msg")); - - assertEquals(123l, metrics.getLong("uptime")); - assertEquals(1, metrics.getInt("alive")); + public void host_is_alive() { + JsonNode packet = HostLifeGatherer.getHostLifePacket(new MockFileWrapper()); + JsonNode metrics = packet.get("metrics"); + assertEquals("host_life", packet.get("application").textValue()); + assertEquals(0, packet.get("status_code").intValue()); + assertEquals("OK", packet.get("status_msg").textValue()); + + assertEquals(123L, metrics.get("uptime").longValue()); + assertEquals(1, metrics.get("alive").intValue()); } diff --git a/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTest.java b/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTest.java index 1258ecdc46f..385eb627427 100644 --- a/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTest.java +++ b/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTest.java @@ -98,37 +98,38 @@ public class StateHandlerTest extends StateHandlerTestBase { snapshotProvider.setSnapshot(snapshot); advanceToNextSnapshot(); assertEquals("{\n" + - " \"metrics\": {\n" + - " \"snapshot\": {\n" + - " \"from\": 0,\n" + - " \"to\": 300\n" + - " },\n" + - " \"values\": [\n" + - " {\n" + - " \"name\": \"foo\",\n" + - " \"values\": {\n" + - " \"count\": 1,\n" + - " \"rate\": 0.0033333333333333335\n" + - " }\n" + - " },\n" + - " {\n" + - " \"dimensions\": {\"component\": \"test\"},\n" + - " \"name\": \"bar\",\n" + - " \"values\": {\n" + - " \"average\": 3.5,\n" + - " \"count\": 4,\n" + - " \"last\": 5,\n" + - " \"max\": 5,\n" + - " \"min\": 2,\n" + - " \"rate\": 0.013333333333333334,\n" + - " \"sum\": 14\n" + - " }\n" + - " }\n" + - " ]\n" + - " },\n" + - " \"status\": {\"code\": \"up\"},\n" + - " \"time\": 300000\n" + - "}", + " \"time\" : 300000,\n" + + " \"status\" : {\n" + + " \"code\" : \"up\"\n" + + " },\n" + + " \"metrics\" : {\n" + + " \"snapshot\" : {\n" + + " \"from\" : 0.0,\n" + + " \"to\" : 300.0\n" + + " },\n" + + " \"values\" : [ {\n" + + " \"name\" : \"foo\",\n" + + " \"values\" : {\n" + + " \"count\" : 1,\n" + + " \"rate\" : 0.0033333333333333335\n" + + " }\n" + + " }, {\n" + + " \"name\" : \"bar\",\n" + + " \"values\" : {\n" + + " \"average\" : 3.5,\n" + + " \"sum\" : 14.0,\n" + + " \"count\" : 4,\n" + + " \"last\" : 5.0,\n" + + " \"max\" : 5.0,\n" + + " \"min\" : 2.0,\n" + + " \"rate\" : 0.013333333333333334\n" + + " },\n" + + " \"dimensions\" : {\n" + + " \"component\" : \"test\"\n" + + " }\n" + + " } ]\n" + + " }\n" + + "}", requestAsString(V1_URI + "all")); } diff --git a/container-dependency-versions/pom.xml b/container-dependency-versions/pom.xml index 8691d9a7ffb..08d6e0103bf 100644 --- a/container-dependency-versions/pom.xml +++ b/container-dependency-versions/pom.xml @@ -313,7 +313,7 @@ <artifactId>javassist</artifactId> <version>${javassist.version}</version> </dependency> - <dependency> <!-- TODO Vespa 8: upgrade to newest version. Consider removing as provided dependency --> + <dependency> <!-- TODO Vespa 8: remove as provided dependency --> <groupId>org.json</groupId> <artifactId>json</artifactId> <version>${org.json.version}</version> diff --git a/container-disc/src/main/java/com/yahoo/container/usability/BindingsOverviewHandler.java b/container-disc/src/main/java/com/yahoo/container/usability/BindingsOverviewHandler.java index 709441999d0..df7cacdc768 100644 --- a/container-disc/src/main/java/com/yahoo/container/usability/BindingsOverviewHandler.java +++ b/container-disc/src/main/java/com/yahoo/container/usability/BindingsOverviewHandler.java @@ -1,9 +1,13 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.container.usability; +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.component.ComponentId; -import com.yahoo.component.provider.ComponentRegistry; import com.yahoo.container.Container; import com.yahoo.container.jdisc.JdiscBindingsConfig; import com.yahoo.jdisc.handler.AbstractRequestHandler; @@ -15,16 +19,11 @@ import com.yahoo.jdisc.handler.ResponseDispatch; import com.yahoo.jdisc.handler.ResponseHandler; import com.yahoo.jdisc.http.HttpRequest; import com.yahoo.jdisc.http.HttpRequest.Method; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; import org.osgi.framework.Bundle; import org.osgi.framework.FrameworkUtil; import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Map; @@ -33,6 +32,8 @@ import java.util.Map; */ public class BindingsOverviewHandler extends AbstractRequestHandler { + private static final ObjectMapper jsonMapper = new ObjectMapper(); + private final JdiscBindingsConfig bindingsConfig; @Inject @@ -42,7 +43,7 @@ public class BindingsOverviewHandler extends AbstractRequestHandler { @Override public ContentChannel handleRequest(com.yahoo.jdisc.Request request, ResponseHandler handler) { - JSONObject json; + JsonNode json; int statusToReturn; if (request instanceof HttpRequest && ((HttpRequest) request).getMethod() != Method.GET) { @@ -63,7 +64,9 @@ public class BindingsOverviewHandler extends AbstractRequestHandler { }.connect(handler)); try { - writer.write(json.toString()); + writer.write(jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(json)); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); } finally { writer.close(); } @@ -71,63 +74,58 @@ public class BindingsOverviewHandler extends AbstractRequestHandler { return new IgnoredContent(); } - private JSONObject errorMessageInJson() { - JSONObject error = new JSONObject(); - try { - error.put("error", "This API, " - + this.getClass().getSimpleName() - + ", only supports HTTP GET." - + " You are probably looking for another API/path."); - } catch (org.json.JSONException e) { - // just ignore it - } + private static JsonNode errorMessageInJson() { + ObjectNode error = jsonMapper.createObjectNode(); + error.put("error", "This API, " + + BindingsOverviewHandler.class.getSimpleName() + + ", only supports HTTP GET." + + " You are probably looking for another API/path."); return error; } - static JSONArray renderRequestHandlers(JdiscBindingsConfig bindingsConfig, + static ArrayNode renderRequestHandlers(JdiscBindingsConfig bindingsConfig, Map<ComponentId, ? extends RequestHandler> handlersById) { - JSONArray ret = new JSONArray(); + ArrayNode ret = jsonMapper.createArrayNode(); for (Map.Entry<ComponentId, ? extends RequestHandler> handlerEntry : handlersById.entrySet()) { String id = handlerEntry.getKey().stringValue(); RequestHandler handler = handlerEntry.getValue(); - JSONObject handlerJson = renderComponent(handler, handlerEntry.getKey()); + ObjectNode handlerJson = renderComponent(handler, handlerEntry.getKey()); addBindings(bindingsConfig, id, handlerJson); - ret.put(handlerJson); + ret.add(handlerJson); } return ret; } - private static void addBindings(JdiscBindingsConfig bindingsConfig, String id, JSONObject handlerJson) { + private static void addBindings(JdiscBindingsConfig bindingsConfig, String id, ObjectNode handlerJson) { List<String> serverBindings = new ArrayList<>(); JdiscBindingsConfig.Handlers handlerConfig = bindingsConfig.handlers(id); if (handlerConfig != null) { serverBindings = handlerConfig.serverBindings(); } - putJson(handlerJson, "serverBindings", renderBindings(serverBindings)); + handlerJson.set("serverBindings", renderBindings(serverBindings)); } - private static JSONArray renderBindings(List<String> bindings) { - JSONArray array = new JSONArray(); + private static JsonNode renderBindings(List<String> bindings) { + ArrayNode array = jsonMapper.createArrayNode(); for (String binding : bindings) - array.put(binding); + array.add(binding); return array; } - private static JSONObject renderComponent(Object component, ComponentId id) { - JSONObject jc = new JSONObject(); - putJson(jc, "id", id.stringValue()); + private static ObjectNode renderComponent(Object component, ComponentId id) { + ObjectNode jc = jsonMapper.createObjectNode(); + jc.put("id", id.stringValue()); addBundleInfo(jc, component); return jc; } - private static void addBundleInfo(JSONObject jsonObject, Object component) { + private static void addBundleInfo(ObjectNode jsonObject, Object component) { BundleInfo bundleInfo = bundleInfo(component); - putJson(jsonObject, "class", bundleInfo.className); - putJson(jsonObject, "bundle", bundleInfo.bundleName); - + jsonObject.put("class", bundleInfo.className); + jsonObject.put("bundle", bundleInfo.bundleName); } private static BundleInfo bundleInfo(Object component) { @@ -143,15 +141,6 @@ public class BindingsOverviewHandler extends AbstractRequestHandler { } } - private static void putJson(JSONObject json, String key, Object value) { - try { - json.put(key, value); - } catch (JSONException e) { - // The original JSONException lacks key-value info. - throw new RuntimeException("Trying to add invalid JSON object with key '" + key + "' and value '" + value + "' - " + e.getMessage(), e); - } - } - static final class BundleInfo { public final String className; @@ -172,10 +161,10 @@ public class BindingsOverviewHandler extends AbstractRequestHandler { this.bindingsConfig = bindingsConfig; } - public JSONObject render() { - JSONObject root = new JSONObject(); + public JsonNode render() { + ObjectNode root = jsonMapper.createObjectNode(); - putJson(root, "handlers", + root.set("handlers", renderRequestHandlers(bindingsConfig, Container.get().getRequestHandlerRegistry().allComponentsById())); return root; diff --git a/container-search-and-docproc/src/main/java/com/yahoo/container/handler/observability/ApplicationStatusHandler.java b/container-search-and-docproc/src/main/java/com/yahoo/container/handler/observability/ApplicationStatusHandler.java index 57c5e768cfb..943eef1e0bf 100644 --- a/container-search-and-docproc/src/main/java/com/yahoo/container/handler/observability/ApplicationStatusHandler.java +++ b/container-search-and-docproc/src/main/java/com/yahoo/container/handler/observability/ApplicationStatusHandler.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.handler.observability; +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.component.AbstractComponent; import com.yahoo.component.ComponentId; @@ -29,15 +34,11 @@ import com.yahoo.processing.execution.chain.ChainRegistry; import com.yahoo.processing.handler.ProcessingHandler; import com.yahoo.search.handler.SearchHandler; import com.yahoo.search.searchchain.SearchChainRegistry; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; import org.osgi.framework.Bundle; import org.osgi.framework.FrameworkUtil; import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Arrays; import java.util.Date; import java.util.Iterator; import java.util.List; @@ -51,11 +52,13 @@ import java.util.Map; */ public class ApplicationStatusHandler extends AbstractRequestHandler { - private final JSONObject applicationJson; - private final JSONArray clientsJson; - private final JSONArray serversJson; - private final JSONArray requestFiltersJson; - private final JSONArray responseFiltersJson; + private static final ObjectMapper jsonMapper = new ObjectMapper(); + + private final JsonNode applicationJson; + private final JsonNode clientsJson; + private final JsonNode serversJson; + private final JsonNode requestFiltersJson; + private final JsonNode responseFiltersJson; private final JdiscBindingsConfig bindingsConfig; @Inject @@ -78,7 +81,7 @@ public class ApplicationStatusHandler extends AbstractRequestHandler { @Override public ContentChannel handleRequest(com.yahoo.jdisc.Request request, ResponseHandler handler) { - JSONObject json = new StatusResponse(applicationJson, clientsJson, serversJson, + JsonNode json = new StatusResponse(applicationJson, clientsJson, serversJson, requestFiltersJson, responseFiltersJson, bindingsConfig) .render(); @@ -91,62 +94,66 @@ public class ApplicationStatusHandler extends AbstractRequestHandler { } }.connect(handler)); - writer.write(json.toString()); + try { + writer.write(jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(json)); + } catch (JsonProcessingException e) { + throw new RuntimeException("Invalid JSON: " + e.getMessage(), e); + } writer.close(); return new IgnoredContent(); } - static JSONObject renderApplicationConfigs(ApplicationMetadataConfig metaConfig, + static JsonNode renderApplicationConfigs(ApplicationMetadataConfig metaConfig, ApplicationUserdataConfig userConfig) { - JSONObject vespa = new JSONObject(); - putJson(vespa, "version", Vtag.currentVersion); - - JSONObject meta = new JSONObject(); - putJson(meta, "name", metaConfig.name()); - putJson(meta, "user", metaConfig.user()); - putJson(meta, "path", metaConfig.path()); - putJson(meta, "generation", metaConfig.generation()); - putJson(meta, "timestamp", metaConfig.timestamp()); - putJson(meta, "date", new Date(metaConfig.timestamp()).toString()); - putJson(meta, "checksum", metaConfig.checksum()); - - JSONObject user = new JSONObject(); - putJson(user, "version", userConfig.version()); - - JSONObject application = new JSONObject(); - putJson(application, "vespa", vespa); - putJson(application, "meta", meta); - putJson(application, "user", user); + ObjectNode vespa = jsonMapper.createObjectNode(); + vespa.put("version", Vtag.currentVersion.toString()); + + ObjectNode meta = jsonMapper.createObjectNode(); + meta.put("name", metaConfig.name()); + meta.put("user", metaConfig.user()); + meta.put("path", metaConfig.path()); + meta.put("generation", metaConfig.generation()); + meta.put("timestamp", metaConfig.timestamp()); + meta.put("date", new Date(metaConfig.timestamp()).toString()); + meta.put("checksum", metaConfig.checksum()); + + ObjectNode user = jsonMapper.createObjectNode(); + user.put("version", userConfig.version()); + + ObjectNode application = jsonMapper.createObjectNode(); + application.set("vespa", vespa); + application.set("meta", meta); + application.set("user", user); return application; } - static JSONArray renderObjectComponents(Map<ComponentId, ?> componentsById) { - JSONArray ret = new JSONArray(); + static JsonNode renderObjectComponents(Map<ComponentId, ?> componentsById) { + ArrayNode ret = jsonMapper.createArrayNode(); for (Map.Entry<ComponentId, ?> componentEntry : componentsById.entrySet()) { - JSONObject jc = renderComponent(componentEntry.getValue(), componentEntry.getKey()); - ret.put(jc); + JsonNode jc = renderComponent(componentEntry.getValue(), componentEntry.getKey()); + ret.add(jc); } return ret; } - static JSONArray renderRequestHandlers(JdiscBindingsConfig bindingsConfig, + static JsonNode renderRequestHandlers(JdiscBindingsConfig bindingsConfig, Map<ComponentId, ? extends RequestHandler> handlersById) { - JSONArray ret = new JSONArray(); + ArrayNode ret = jsonMapper.createArrayNode(); for (Map.Entry<ComponentId, ? extends RequestHandler> handlerEntry : handlersById.entrySet()) { String id = handlerEntry.getKey().stringValue(); RequestHandler handler = handlerEntry.getValue(); - JSONObject handlerJson = renderComponent(handler, handlerEntry.getKey()); + ObjectNode handlerJson = renderComponent(handler, handlerEntry.getKey()); addBindings(bindingsConfig, id, handlerJson); - ret.put(handlerJson); + ret.add(handlerJson); } return ret; } - private static void addBindings(JdiscBindingsConfig bindingsConfig, String id, JSONObject handlerJson) { + private static void addBindings(JdiscBindingsConfig bindingsConfig, String id, ObjectNode handlerJson) { List<String> serverBindings = new ArrayList<>(); List<String> clientBindings = new ArrayList<>(); @@ -155,40 +162,40 @@ public class ApplicationStatusHandler extends AbstractRequestHandler { serverBindings = handlerConfig.serverBindings(); clientBindings = handlerConfig.clientBindings(); } - putJson(handlerJson, "serverBindings", renderBindings(serverBindings)); - putJson(handlerJson, "clientBindings", renderBindings(clientBindings)); + handlerJson.set("serverBindings", renderBindings(serverBindings)); + handlerJson.set("clientBindings", renderBindings(clientBindings)); } - private static JSONArray renderBindings(List<String> bindings) { - JSONArray ret = new JSONArray(); + private static JsonNode renderBindings(List<String> bindings) { + ArrayNode ret = jsonMapper.createArrayNode(); for (String binding : bindings) - ret.put(binding); + ret.add(binding); return ret; } - private static JSONArray renderAbstractComponents(List<? extends AbstractComponent> components) { - JSONArray ret = new JSONArray(); + private static JsonNode renderAbstractComponents(List<? extends AbstractComponent> components) { + ArrayNode ret = jsonMapper.createArrayNode(); for (AbstractComponent c : components) { - JSONObject jc = renderComponent(c, c.getId()); - ret.put(jc); + JsonNode jc = renderComponent(c, c.getId()); + ret.add(jc); } return ret; } - private static JSONObject renderComponent(Object component, ComponentId id) { - JSONObject jc = new JSONObject(); - putJson(jc, "id", id.stringValue()); + private static ObjectNode renderComponent(Object component, ComponentId id) { + ObjectNode jc = jsonMapper.createObjectNode(); + jc.put("id", id.stringValue()); addBundleInfo(jc, component); return jc; } - private static void addBundleInfo(JSONObject jsonObject, Object component) { + private static void addBundleInfo(ObjectNode jsonObject, Object component) { BundleInfo bundleInfo = bundleInfo(component); - putJson(jsonObject, "class", bundleInfo.className); - putJson(jsonObject, "bundle", bundleInfo.bundleName); + jsonObject.put("class", bundleInfo.className); + jsonObject.put("bundle", bundleInfo.bundleName); } @@ -205,15 +212,6 @@ public class ApplicationStatusHandler extends AbstractRequestHandler { } } - private static void putJson(JSONObject json, String key, Object value) { - try { - json.put(key, value); - } catch (JSONException e) { - // The original JSONException lacks key-value info. - throw new RuntimeException("Trying to add invalid JSON object with key '" + key + "' and value '" + value + "' - " + e.getMessage(), e); - } - } - static final class BundleInfo { public final String className; public final String bundleName; @@ -224,18 +222,18 @@ public class ApplicationStatusHandler extends AbstractRequestHandler { } static final class StatusResponse { - private final JSONObject applicationJson; - private final JSONArray clientsJson; - private final JSONArray serversJson; - private final JSONArray requestFiltersJson; - private final JSONArray responseFiltersJson; + private final JsonNode applicationJson; + private final JsonNode clientsJson; + private final JsonNode serversJson; + private final JsonNode requestFiltersJson; + private final JsonNode responseFiltersJson; private final JdiscBindingsConfig bindingsConfig; - StatusResponse(JSONObject applicationJson, - JSONArray clientsJson, - JSONArray serversJson, - JSONArray requestFiltersJson, - JSONArray responseFiltersJson, + StatusResponse(JsonNode applicationJson, + JsonNode clientsJson, + JsonNode serversJson, + JsonNode requestFiltersJson, + JsonNode responseFiltersJson, JdiscBindingsConfig bindingsConfig) { this.applicationJson = applicationJson; this.clientsJson = clientsJson; @@ -245,52 +243,52 @@ public class ApplicationStatusHandler extends AbstractRequestHandler { this.bindingsConfig = bindingsConfig; } - public JSONObject render() { - JSONObject root = new JSONObject(); + public JsonNode render() { + ObjectNode root = jsonMapper.createObjectNode(); - putJson(root, "application", applicationJson); - putJson(root, "abstractComponents", + root.set("application", applicationJson); + root.set("abstractComponents", renderAbstractComponents(Container.get().getComponentRegistry().allComponents())); - putJson(root, "handlers", + root.set("handlers", renderRequestHandlers(bindingsConfig, Container.get().getRequestHandlerRegistry().allComponentsById())); - putJson(root, "clients", clientsJson); - putJson(root, "servers", serversJson); - putJson(root, "httpRequestFilters", requestFiltersJson); - putJson(root, "httpResponseFilters", responseFiltersJson); + root.set("clients", clientsJson); + root.set("servers", serversJson); + root.set("httpRequestFilters", requestFiltersJson); + root.set("httpResponseFilters", responseFiltersJson); - putJson(root, "searchChains", renderSearchChains(Container.get())); - putJson(root, "docprocChains", renderDocprocChains(Container.get())); - putJson(root, "processingChains", renderProcessingChains(Container.get())); + root.set("searchChains", renderSearchChains(Container.get())); + root.set("docprocChains", renderDocprocChains(Container.get())); + root.set("processingChains", renderProcessingChains(Container.get())); return root; } - private static JSONObject renderSearchChains(Container container) { + private static JsonNode renderSearchChains(Container container) { for (RequestHandler h : container.getRequestHandlerRegistry().allComponents()) { if (h instanceof SearchHandler) { SearchChainRegistry scReg = ((SearchHandler) h).getSearchChainRegistry(); return renderChains(scReg); } } - return new JSONObject(); + return jsonMapper.createObjectNode(); } - private static JSONObject renderDocprocChains(Container container) { - JSONObject ret = new JSONObject(); + private static JsonNode renderDocprocChains(Container container) { + ObjectNode ret = jsonMapper.createObjectNode(); for (RequestHandler h : container.getRequestHandlerRegistry().allComponents()) { if (h instanceof DocumentProcessingHandler) { ComponentRegistry<DocprocService> registry = ((DocumentProcessingHandler) h).getDocprocServiceRegistry(); for (DocprocService service : registry.allComponents()) { - putJson(ret, service.getId().stringValue(), renderCalls(service.getCallStack().iterator())); + ret.set(service.getId().stringValue(), renderCalls(service.getCallStack().iterator())); } } } return ret; } - private static JSONObject renderProcessingChains(Container container) { - JSONObject ret = new JSONObject(); + private static JsonNode renderProcessingChains(Container container) { + JsonNode ret = jsonMapper.createObjectNode(); for (RequestHandler h : container.getRequestHandlerRegistry().allComponents()) { if (h instanceof ProcessingHandler) { ChainRegistry<Processor> registry = ((ProcessingHandler) h).getChainRegistry(); @@ -301,20 +299,20 @@ public class ApplicationStatusHandler extends AbstractRequestHandler { } // Note the generic param here! The key to make this work is '? extends Chain', but why? - static JSONObject renderChains(ComponentRegistry<? extends Chain<?>> chains) { - JSONObject ret = new JSONObject(); + static JsonNode renderChains(ComponentRegistry<? extends Chain<?>> chains) { + ObjectNode ret = jsonMapper.createObjectNode(); for (Chain<?> chain : chains.allComponents()) { - putJson(ret, chain.getId().stringValue(), renderAbstractComponents(chain.components())); + ret.set(chain.getId().stringValue(), renderAbstractComponents(chain.components())); } return ret; } - private static JSONArray renderCalls(Iterator<Call> components) { - JSONArray ret = new JSONArray(); + private static JsonNode renderCalls(Iterator<Call> components) { + ArrayNode ret = jsonMapper.createArrayNode(); while (components.hasNext()) { Call c = components.next(); - JSONObject jc = renderComponent(c.getDocumentProcessor(), c.getDocumentProcessor().getId()); - ret.put(jc); + JsonNode jc = renderComponent(c.getDocumentProcessor(), c.getDocumentProcessor().getId()); + ret.add(jc); } return ret; } diff --git a/container-search-gui/src/main/java/com/yahoo/search/query/gui/GUIHandler.java b/container-search-gui/src/main/java/com/yahoo/search/query/gui/GUIHandler.java index 9ddcc7a7e69..3132f3744c9 100644 --- a/container-search-gui/src/main/java/com/yahoo/search/query/gui/GUIHandler.java +++ b/container-search-gui/src/main/java/com/yahoo/search/query/gui/GUIHandler.java @@ -1,8 +1,10 @@ // Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.search.query.gui; +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.container.QrSearchersConfig; import com.yahoo.container.jdisc.HttpRequest; import com.yahoo.container.jdisc.HttpResponse; @@ -11,6 +13,7 @@ import com.yahoo.prelude.IndexModel; import com.yahoo.prelude.querytransform.RecallSearcher; import com.yahoo.restapi.Path; import com.yahoo.search.Query; +import com.yahoo.search.config.IndexInfoConfig; import com.yahoo.search.query.Model; import com.yahoo.search.query.Presentation; import com.yahoo.search.query.Ranking; @@ -19,19 +22,12 @@ import com.yahoo.search.query.ranking.MatchPhase; import com.yahoo.search.query.restapi.ErrorResponse; import com.yahoo.search.yql.MinimalQueryInserter; import com.yahoo.vespa.config.search.RankProfilesConfig; -import com.yahoo.search.config.IndexInfoConfig; import com.yahoo.yolean.Exceptions; -import org.json.JSONException; -import org.json.JSONObject; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; import java.util.logging.Level; @@ -42,6 +38,8 @@ import java.util.logging.Level; */ public class GUIHandler extends LoggingRequestHandler { + private static final ObjectMapper jsonMapper = new ObjectMapper(); + private final IndexModel indexModel; private final RankProfilesConfig rankProfilesConfig; @@ -112,7 +110,7 @@ public class GUIHandler extends LoggingRequestHandler { InputStream is; if (this.path.equals("config.json")){ String json = "{}"; - try { json = getGUIConfig(); } catch (JSONException e) { /*Something happened while parsing JSON */ } + try { json = getGUIConfig(); } catch (IOException e) { /*Something happened while parsing JSON */ } is = new ByteArrayInputStream(json.getBytes()); } else{ is = GUIHandler.class.getClassLoader().getResourceAsStream("gui/"+this.path); @@ -160,50 +158,50 @@ public class GUIHandler extends LoggingRequestHandler { return "text/html"; } - private String getGUIConfig() throws JSONException { - JSONObject json = new JSONObject(); - json.put("ranking_properties", Arrays.asList("propertyname")); - json.put("ranking_features", Arrays.asList("featurename")); + private String getGUIConfig() throws IOException { + ObjectNode json = jsonMapper.createObjectNode(); + json.set("ranking_properties", jsonMapper.createArrayNode().add("propertyname")); + json.set("ranking_features", jsonMapper.createArrayNode().add("featurename")); - List<String> sources = new ArrayList<>(); + ArrayNode sources = jsonMapper.createArrayNode(); try { - sources = new ArrayList<>(indexModel.getMasterClusters().keySet()); + indexModel.getMasterClusters().keySet().forEach(sources::add); } catch (NullPointerException ex){ /* clusters are not set */ } - json.put("model_sources", sources); + json.set("model_sources", sources); - List<String> rankProfiles = new ArrayList<>(); + ArrayNode rankProfiles = jsonMapper.createArrayNode(); try { rankProfilesConfig.rankprofile().forEach(rankProfile -> rankProfiles.add(rankProfile.name())); } catch (NullPointerException ex){ /* rankprofiles are not set*/ } - json.put("ranking_profile", rankProfiles); + json.set("ranking_profile", rankProfiles); // Creating map from parent to children for GUI: parameter --> child-parameters - HashMap<String, List<String>> childMap = new HashMap<>(); - childMap.put(Model.MODEL, Arrays.asList(Model.DEFAULT_INDEX, Model.ENCODING, Model.LANGUAGE, Model.QUERY_STRING, Model.RESTRICT, Model.SEARCH_PATH, Model.SOURCES, Model.TYPE)); - childMap.put(Ranking.RANKING, Arrays.asList(Ranking.LOCATION, Ranking.FEATURES, Ranking.LIST_FEATURES, Ranking.PROFILE, Ranking.PROPERTIES, Ranking.SORTING, Ranking.FRESHNESS, Ranking.QUERYCACHE, Ranking.MATCH_PHASE)); - childMap.put(Ranking.RANKING +"."+ Ranking.MATCH_PHASE, Arrays.asList(MatchPhase.MAX_HITS, MatchPhase.ATTRIBUTE, MatchPhase.ASCENDING, Ranking.DIVERSITY)); - childMap.put(Ranking.RANKING +"."+ Ranking.MATCH_PHASE +"."+Ranking.DIVERSITY, Arrays.asList(Diversity.ATTRIBUTE, Diversity.MINGROUPS)); - childMap.put(Presentation.PRESENTATION, Arrays.asList(Presentation.BOLDING, Presentation.FORMAT, Presentation.SUMMARY, "template", Presentation.TIMING )); - childMap.put("trace", Arrays.asList("timestamps")); - childMap.put("tracelevel", Arrays.asList("rules")); - childMap.put("metrics", Arrays.asList("ignore")); - childMap.put("collapse", Arrays.asList("summary")); - childMap.put("pos", Arrays.asList("ll", "radius", "bb", "attribute")); - childMap.put("streaming", Arrays.asList("userid", "groupname", "selection", "priority", "maxbucketspervisitor")); - childMap.put("rules", Arrays.asList("off", "rulebase")); - json.put("childMap", childMap); - - List<String> levelZeroParameters = Arrays.asList(MinimalQueryInserter.YQL.toString(), Query.HITS.toString(), Query.OFFSET.toString(), - "queryProfile", Query.NO_CACHE.toString(), Query.GROUPING_SESSION_CACHE.toString(), - Query.SEARCH_CHAIN.toString(), Query.TIMEOUT.toString(), "trace", "tracelevel", - Query.TRACE_LEVEL.toString(), Query.EXPLAIN_LEVEL.toString(), "explainlevel", Model.MODEL, Ranking.RANKING, "collapse", "collapsesize","collapsefield", - Presentation.PRESENTATION, "pos", "streaming", "rules", RecallSearcher.recallName.toString(), "user", - "metrics", ""); - json.put("levelZeroParameters", levelZeroParameters); - - return json.toString(); + ObjectNode childMap = jsonMapper.createObjectNode(); + childMap.set(Model.MODEL, jsonMapper.createArrayNode().add(Model.DEFAULT_INDEX).add(Model.ENCODING).add(Model.LANGUAGE).add(Model.QUERY_STRING).add(Model.RESTRICT).add(Model.SEARCH_PATH).add(Model.SOURCES).add(Model.TYPE)); + childMap.set(Ranking.RANKING, jsonMapper.createArrayNode().add(Ranking.LOCATION).add(Ranking.FEATURES).add(Ranking.LIST_FEATURES).add(Ranking.PROFILE).add(Ranking.PROPERTIES).add(Ranking.SORTING).add(Ranking.FRESHNESS).add(Ranking.QUERYCACHE).add(Ranking.MATCH_PHASE)); + childMap.set(Ranking.RANKING +"."+ Ranking.MATCH_PHASE, jsonMapper.createArrayNode().add(MatchPhase.MAX_HITS).add(MatchPhase.ATTRIBUTE).add(MatchPhase.ASCENDING).add(Ranking.DIVERSITY)); + childMap.set(Ranking.RANKING +"."+ Ranking.MATCH_PHASE +"."+Ranking.DIVERSITY, jsonMapper.createArrayNode().add(Diversity.ATTRIBUTE).add(Diversity.MINGROUPS)); + childMap.set(Presentation.PRESENTATION, jsonMapper.createArrayNode().add(Presentation.BOLDING).add(Presentation.FORMAT).add(Presentation.SUMMARY).add("template").add(Presentation.TIMING )); + childMap.set("trace", jsonMapper.createArrayNode().add("timestamps")); + childMap.set("tracelevel", jsonMapper.createArrayNode().add("rules")); + childMap.set("metrics", jsonMapper.createArrayNode().add("ignore")); + childMap.set("collapse", jsonMapper.createArrayNode().add("summary")); + childMap.set("pos", jsonMapper.createArrayNode().add("ll").add("radius").add("bb").add("attribute")); + childMap.set("streaming", jsonMapper.createArrayNode().add("userid").add("groupname").add("selection").add("priority").add("maxbucketspervisitor")); + childMap.set("rules", jsonMapper.createArrayNode().add("off").add("rulebase")); + json.set("childMap", childMap); + + ArrayNode levelZeroParameters = jsonMapper.createArrayNode().add(MinimalQueryInserter.YQL.toString()).add(Query.HITS.toString()).add(Query.OFFSET.toString()) + .add("queryProfile").add(Query.NO_CACHE.toString()).add(Query.GROUPING_SESSION_CACHE.toString()) + .add(Query.SEARCH_CHAIN.toString()).add(Query.TIMEOUT.toString()).add("trace").add("tracelevel") + .add(Query.TRACE_LEVEL.toString()).add(Query.EXPLAIN_LEVEL.toString()).add("explainlevel").add(Model.MODEL).add(Ranking.RANKING).add("collapse").add("collapsesize").add("collapsefield") + .add(Presentation.PRESENTATION).add("pos").add("streaming").add("rules").add(RecallSearcher.recallName.toString()).add("user") + .add("metrics").add(""); + json.set("levelZeroParameters", levelZeroParameters); + + return jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsString(json); } } }
\ No newline at end of file diff --git a/container-search/pom.xml b/container-search/pom.xml index 074f5827122..014b7dda14f 100644 --- a/container-search/pom.xml +++ b/container-search/pom.xml @@ -150,6 +150,11 @@ <version>${project.version}</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> </dependencies> <build> <plugins> diff --git a/container-search/src/main/java/com/yahoo/prelude/hitfield/JSONString.java b/container-search/src/main/java/com/yahoo/prelude/hitfield/JSONString.java index 209bfd08e6b..55438aa35ba 100644 --- a/container-search/src/main/java/com/yahoo/prelude/hitfield/JSONString.java +++ b/container-search/src/main/java/com/yahoo/prelude/hitfield/JSONString.java @@ -21,6 +21,7 @@ import java.util.Iterator; * * @author Steinar Knutsen */ +// TODO Vespa 8: remove methods leaking org.json types (replace with Slime equivalent?) public class JSONString implements Inspectable { private Inspector value; @@ -436,6 +437,8 @@ public class JSONString implements Inspectable { return content; } + /** @deprecated Use {@link #getContent()} instead and parse content yourself */ + @Deprecated(forRemoval = true, since = "7") public Object getParsedJSON() { initContent(); if (parsedJSON == null) { @@ -444,6 +447,7 @@ public class JSONString implements Inspectable { return parsedJSON; } + @Deprecated(forRemoval = true, since = "7") public void setParsedJSON(Object parsedJSON) { this.parsedJSON = parsedJSON; } diff --git a/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java b/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java index 31f8194b3b7..c4f850307ae 100644 --- a/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java +++ b/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java @@ -45,8 +45,6 @@ import com.yahoo.search.result.Hit; import com.yahoo.search.result.HitGroup; import com.yahoo.search.result.NanNumber; import com.yahoo.tensor.Tensor; -import org.json.JSONArray; -import org.json.JSONObject; import java.io.IOException; import java.io.OutputStream; @@ -671,14 +669,6 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> { } else if (field instanceof FieldValue) { // the null below is the field which has already been written ((FieldValue) field).serialize(null, new JsonWriter(generator)); - } else if (field instanceof JSONArray || field instanceof JSONObject) { - // org.json returns null if the object would not result in syntactically correct JSON - String s = field.toString(); - if (s == null) { - generator.writeNull(); - } else { - generator.writeRawValue(s); - } } else { generator.writeString(field.toString()); } diff --git a/container-search/src/test/java/com/yahoo/prelude/fastsearch/SlimeSummaryTestCase.java b/container-search/src/test/java/com/yahoo/prelude/fastsearch/SlimeSummaryTestCase.java index d1399cabc75..49df321e581 100644 --- a/container-search/src/test/java/com/yahoo/prelude/fastsearch/SlimeSummaryTestCase.java +++ b/container-search/src/test/java/com/yahoo/prelude/fastsearch/SlimeSummaryTestCase.java @@ -4,16 +4,21 @@ package com.yahoo.prelude.fastsearch; import com.google.common.collect.ImmutableSet; import com.yahoo.config.subscription.ConfigGetter; import com.yahoo.data.access.slime.SlimeAdapter; +import com.yahoo.prelude.hitfield.JSONString; import com.yahoo.prelude.hitfield.RawData; import com.yahoo.prelude.hitfield.XMLString; -import com.yahoo.prelude.hitfield.JSONString; import com.yahoo.search.result.FeatureData; import com.yahoo.search.result.Hit; -import com.yahoo.search.result.NanNumber; import com.yahoo.search.result.StructuredData; +import com.yahoo.slime.BinaryFormat; +import com.yahoo.slime.Cursor; +import com.yahoo.slime.Slime; +import com.yahoo.tensor.Tensor; +import com.yahoo.tensor.serialization.TypedBinaryFormat; +import org.junit.Test; + import java.nio.ByteBuffer; import java.nio.ByteOrder; - import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Iterator; @@ -21,14 +26,6 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import com.yahoo.slime.BinaryFormat; -import com.yahoo.slime.Cursor; -import com.yahoo.slime.Inspector; -import com.yahoo.slime.Slime; -import com.yahoo.tensor.Tensor; -import com.yahoo.tensor.serialization.TypedBinaryFormat; -import org.junit.Test; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -102,7 +99,7 @@ public class SlimeSummaryTestCase { if (hit.getField("jsonstring_field") instanceof JSONString) { JSONString jstr = (JSONString) hit.getField("jsonstring_field"); assertEquals("{\"foo\":1,\"bar\":2}", jstr.getContent()); - assertNotNull(jstr.getParsedJSON()); + assertNotNull(getParsedJSON(jstr)); com.yahoo.data.access.Inspector value = jstr.inspect(); assertEquals(1L, value.field("foo").asLong()); @@ -126,6 +123,8 @@ public class SlimeSummaryTestCase { assertEquals(tensor2, featureData.getTensor("tensor2_feature")); } + @SuppressWarnings("removal") private static Object getParsedJSON(JSONString jstr) { return jstr.getParsedJSON(); } + @Test public void testFieldAccessAPI() { DocsumDefinitionSet partialDocsum1 = createDocsumDefinitionSet(partial_summary1_cf); diff --git a/container-search/src/test/java/com/yahoo/search/handler/test/JSONSearchHandlerTestCase.java b/container-search/src/test/java/com/yahoo/search/handler/test/JSONSearchHandlerTestCase.java index 3cca053d0e5..80e629ca4cb 100644 --- a/container-search/src/test/java/com/yahoo/search/handler/test/JSONSearchHandlerTestCase.java +++ b/container-search/src/test/java/com/yahoo/search/handler/test/JSONSearchHandlerTestCase.java @@ -1,10 +1,13 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.search.handler.test; +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.yahoo.container.Container; import com.yahoo.container.core.config.testutil.HandlersConfigurerTestWrapper; import com.yahoo.container.jdisc.HttpRequest; - import com.yahoo.container.jdisc.RequestHandlerTestDriver; import com.yahoo.container.protect.Error; import com.yahoo.io.IOUtils; @@ -13,8 +16,8 @@ import com.yahoo.search.handler.SearchHandler; import com.yahoo.search.searchchain.config.test.SearchChainConfigurerTestCase; import com.yahoo.slime.Inspector; import com.yahoo.slime.SlimeUtils; -import org.json.JSONArray; -import org.json.JSONObject; +import com.yahoo.test.json.JsonTestHelper; +import org.assertj.core.api.Assertions; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -23,13 +26,19 @@ import org.junit.rules.TemporaryFolder; import java.io.File; import java.io.IOException; -import java.util.Map; +import java.nio.charset.StandardCharsets; import java.util.HashMap; +import java.util.Map; import static com.yahoo.jdisc.http.HttpRequest.Method.GET; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; /** * Tests submitting the query as JSON. @@ -38,6 +47,8 @@ import static org.junit.Assert.*; */ public class JSONSearchHandlerTestCase { + private static final ObjectMapper jsonMapper = new ObjectMapper(); + private static final String testDir = "src/test/java/com/yahoo/search/handler/test/config"; private static final String myHostnameHeader = "my-hostname-header"; private static final String selfHostname = HostName.getLocalhost(); @@ -97,8 +108,8 @@ public class JSONSearchHandlerTestCase { } @Test - public void testFailing() throws Exception { - JSONObject json = new JSONObject(); + public void testFailing() { + ObjectNode json = jsonMapper.createObjectNode(); json.put("query", "test"); json.put("searchChain", "classLoadingError"); assertTrue(driver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE).readAll().contains("NoClassDefFoundError")); @@ -106,16 +117,16 @@ public class JSONSearchHandlerTestCase { @Test - public synchronized void testPluginError() throws Exception { - JSONObject json = new JSONObject(); + public synchronized void testPluginError() { + ObjectNode json = jsonMapper.createObjectNode(); json.put("query", "test"); json.put("searchChain", "exceptionInPlugin"); assertTrue(driver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE).readAll().contains("NullPointerException")); } @Test - public synchronized void testWorkingReconfiguration() throws Exception { - JSONObject json = new JSONObject(); + public synchronized void testWorkingReconfiguration() throws IOException { + ObjectNode json = jsonMapper.createObjectNode(); json.put("query", "abc"); assertJsonResult(json, driver); @@ -135,7 +146,7 @@ public class JSONSearchHandlerTestCase { } @Test - public void testInvalidYqlQuery() throws Exception { + public void testInvalidYqlQuery() throws IOException { IOUtils.copyDirectory(new File(testDir, "config_yql"), new File(tempDir), 1); generateComponentsConfigForActive(); configurer.reloadConfig(); @@ -143,7 +154,7 @@ public class JSONSearchHandlerTestCase { SearchHandler newSearchHandler = fetchSearchHandler(configurer); assertTrue("Do I have a new instance of the search handler?", searchHandler != newSearchHandler); try (RequestHandlerTestDriver newDriver = new RequestHandlerTestDriver(newSearchHandler)) { - JSONObject json = new JSONObject(); + ObjectNode json = jsonMapper.createObjectNode(); json.put("yql", "select * from foo where bar > 1453501295"); RequestHandlerTestDriver.MockResponseHandler responseHandler = newDriver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE); responseHandler.readAll(); @@ -153,14 +164,14 @@ public class JSONSearchHandlerTestCase { // Query handling takes a different code path when a query profile is active, so we test both paths. @Test - public void testInvalidQueryParamWithQueryProfile() throws Exception { + public void testInvalidQueryParamWithQueryProfile() throws IOException { try (RequestHandlerTestDriver newDriver = driverWithConfig("config_invalid_param")) { testInvalidQueryParam(newDriver); } } - private void testInvalidQueryParam(final RequestHandlerTestDriver testDriver) throws Exception { - JSONObject json = new JSONObject(); + private void testInvalidQueryParam(final RequestHandlerTestDriver testDriver) { + ObjectNode json = jsonMapper.createObjectNode(); json.put("query", "status_code:0"); json.put("hits", 20); json.put("offset", -20); @@ -173,16 +184,16 @@ public class JSONSearchHandlerTestCase { } @Test - public void testNormalResultJsonAliasRendering() throws Exception { - JSONObject json = new JSONObject(); + public void testNormalResultJsonAliasRendering() { + ObjectNode json = jsonMapper.createObjectNode(); json.put("format", "json"); json.put("query", "abc"); assertJsonResult(json, driver); } @Test - public void testNullQuery() throws Exception { - JSONObject json = new JSONObject(); + public void testNullQuery() { + ObjectNode json = jsonMapper.createObjectNode(); json.put("format", "xml"); assertEquals("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" + @@ -195,8 +206,8 @@ public class JSONSearchHandlerTestCase { } @Test - public void testWebServiceStatus() throws Exception { - JSONObject json = new JSONObject(); + public void testWebServiceStatus() { + ObjectNode json = jsonMapper.createObjectNode(); json.put("query", "web_service_status_code"); RequestHandlerTestDriver.MockResponseHandler responseHandler = driver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE); @@ -206,39 +217,39 @@ public class JSONSearchHandlerTestCase { } @Test - public void testNormalResultImplicitDefaultRendering() throws Exception { - JSONObject json = new JSONObject(); + public void testNormalResultImplicitDefaultRendering() { + ObjectNode json = jsonMapper.createObjectNode(); json.put("query", "abc"); assertJsonResult(json, driver); } @Test - public void testNormalResultExplicitDefaultRendering() throws Exception { - JSONObject json = new JSONObject(); + public void testNormalResultExplicitDefaultRendering() { + ObjectNode json = jsonMapper.createObjectNode(); json.put("query", "abc"); json.put("format", "default"); assertJsonResult(json, driver); } @Test - public void testNormalResultXmlAliasRendering() throws Exception { - JSONObject json = new JSONObject(); + public void testNormalResultXmlAliasRendering() { + ObjectNode json = jsonMapper.createObjectNode(); json.put("query", "abc"); json.put("format", "xml"); assertXmlResult(json, driver); } @Test - public void testNormalResultExplicitDefaultRenderingFullRendererName1() throws Exception { - JSONObject json = new JSONObject(); + public void testNormalResultExplicitDefaultRenderingFullRendererName1() { + ObjectNode json = jsonMapper.createObjectNode(); json.put("query", "abc"); json.put("format", "XmlRenderer"); assertXmlResult(json, driver); } @Test - public void testNormalResultExplicitDefaultRenderingFullRendererName2() throws Exception { - JSONObject json = new JSONObject(); + public void testNormalResultExplicitDefaultRenderingFullRendererName2() { + ObjectNode json = jsonMapper.createObjectNode(); json.put("query", "abc"); json.put("format", "JsonRenderer"); assertJsonResult(json, driver); @@ -253,7 +264,7 @@ public class JSONSearchHandlerTestCase { " </hit>\n" + "</result>\n"; - private void assertXmlResult(JSONObject json, RequestHandlerTestDriver driver) { + private void assertXmlResult(JsonNode json, RequestHandlerTestDriver driver) { assertOkResult(driver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE), xmlResult); } @@ -263,7 +274,7 @@ public class JSONSearchHandlerTestCase { + "{\"id\":\"testHit\",\"relevance\":1.0,\"fields\":{\"uri\":\"testHit\"}}" + "]}}"; - private void assertJsonResult(JSONObject json, RequestHandlerTestDriver driver) { + private void assertJsonResult(JsonNode json, RequestHandlerTestDriver driver) { assertOkResult(driver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), JSON_CONTENT_TYPE), jsonResult); } @@ -288,7 +299,7 @@ public class JSONSearchHandlerTestCase { } - private RequestHandlerTestDriver driverWithConfig(String configDirectory) throws Exception { + private RequestHandlerTestDriver driverWithConfig(String configDirectory) throws IOException { IOUtils.copyDirectory(new File(testDir, configDirectory), new File(tempDir), 1); generateComponentsConfigForActive(); configurer.reloadConfig(); @@ -299,44 +310,44 @@ public class JSONSearchHandlerTestCase { } @Test - public void testSelectParameters() throws Exception { - JSONObject json = new JSONObject(); + public void testSelectParameters() throws IOException { + ObjectNode json = jsonMapper.createObjectNode(); - JSONObject select = new JSONObject(); + ObjectNode select = jsonMapper.createObjectNode(); - JSONObject where = new JSONObject(); + ObjectNode where = jsonMapper.createObjectNode(); where.put("where", "where"); - JSONObject grouping = new JSONObject(); + ObjectNode grouping = jsonMapper.createObjectNode(); grouping.put("grouping", "grouping"); - select.put("where", where); - select.put("grouping", grouping); + select.set("where", where); + select.set("grouping", grouping); - json.put("select", select); + json.set("select", select); - Inspector inspector = SlimeUtils.jsonToSlime(json.toString().getBytes("utf-8")).get(); + Inspector inspector = SlimeUtils.jsonToSlime(json.toString().getBytes(StandardCharsets.UTF_8)).get(); Map<String, String> map = new HashMap<>(); searchHandler.createRequestMapping(inspector, map, ""); - JSONObject processedWhere = new JSONObject(map.get("select.where")); - assertEquals(where.toString(), processedWhere.toString()); + JsonNode processedWhere = jsonMapper.readTree(map.get("select.where")); + JsonTestHelper.assertJsonEquals(where.toString(), processedWhere.toString()); - JSONObject processedGrouping = new JSONObject(map.get("select.grouping")); - assertEquals(grouping.toString(), processedGrouping.toString()); + JsonNode processedGrouping = jsonMapper.readTree(map.get("select.grouping")); + JsonTestHelper.assertJsonEquals(grouping.toString(), processedGrouping.toString()); } @Test - public void testJsonQueryWithSelectWhere() throws Exception { - JSONObject root = new JSONObject(); - JSONObject select = new JSONObject(); - JSONObject where = new JSONObject(); - JSONArray term = new JSONArray(); - term.put("default"); - term.put("bad"); - where.put("contains", term); - select.put("where", where); - root.put("select", select); + public void testJsonQueryWithSelectWhere() { + ObjectNode root = jsonMapper.createObjectNode(); + ObjectNode select = jsonMapper.createObjectNode(); + ObjectNode where = jsonMapper.createObjectNode(); + ArrayNode term = jsonMapper.createArrayNode(); + term.add("default"); + term.add("bad"); + where.set("contains", term); + select.set("where", where); + root.set("select", select); // Run query String result = driver.sendRequest(uri + "searchChain=echoingQuery", com.yahoo.jdisc.http.HttpRequest.Method.POST, root.toString(), JSON_CONTENT_TYPE).readAll(); @@ -393,8 +404,8 @@ public class JSONSearchHandlerTestCase { } @Test - public void testJsonQueryWithYQL() throws Exception { - JSONObject root = new JSONObject(); + public void testJsonQueryWithYQL() { + ObjectNode root = jsonMapper.createObjectNode(); root.put("yql", "select * from sources * where default contains 'bad';"); // Run query @@ -404,10 +415,10 @@ public class JSONSearchHandlerTestCase { } @Test - public void testRequestMapping() throws Exception { - JSONObject json = new JSONObject(); + public void testRequestMapping() { + ObjectNode json = jsonMapper.createObjectNode(); json.put("yql", "select * from sources * where sddocname contains \"blog_post\" limit 0 | all(group(date) max(3) order(-count())each(output(count())));"); - json.put("hits", 10.0); + json.put("hits", 10); json.put("offset", 5); json.put("queryProfile", "foo"); json.put("nocache", false); @@ -417,7 +428,7 @@ public class JSONSearchHandlerTestCase { json.put("select", "_all"); - JSONObject model = new JSONObject(); + ObjectNode model = jsonMapper.createObjectNode(); model.put("defaultIndex", 1); model.put("encoding", "json"); model.put("filter", "default"); @@ -427,9 +438,9 @@ public class JSONSearchHandlerTestCase { model.put("searchPath", "node1"); model.put("sources", "source1,source2"); model.put("type", "yql"); - json.put("model", model); + json.set("model", model); - JSONObject ranking = new JSONObject(); + ObjectNode ranking = jsonMapper.createObjectNode(); ranking.put("location", "123789.89123N;128123W"); ranking.put("features", "none"); ranking.put("listFeatures", false); @@ -439,61 +450,61 @@ public class JSONSearchHandlerTestCase { ranking.put("freshness", "0.05"); ranking.put("queryCache", false); - JSONObject matchPhase = new JSONObject(); + ObjectNode matchPhase = jsonMapper.createObjectNode(); matchPhase.put("maxHits", "100"); matchPhase.put("attribute", "title"); matchPhase.put("ascending", true); - JSONObject diversity = new JSONObject(); + ObjectNode diversity = jsonMapper.createObjectNode(); diversity.put("attribute", "title"); diversity.put("minGroups", 1); - matchPhase.put("diversity", diversity); - ranking.put("matchPhase", matchPhase); - json.put("ranking", ranking); + matchPhase.set("diversity", diversity); + ranking.set("matchPhase", matchPhase); + json.set("ranking", ranking); - JSONObject presentation = new JSONObject(); + ObjectNode presentation = jsonMapper.createObjectNode(); presentation.put("bolding", true); presentation.put("format", "json"); presentation.put("summary", "none"); presentation.put("template", "json"); presentation.put("timing", false); - json.put("presentation", presentation); + json.set("presentation", presentation); - JSONObject collapse = new JSONObject(); + ObjectNode collapse = jsonMapper.createObjectNode(); collapse.put("field", "none"); collapse.put("size", 2); collapse.put("summary", "default"); - json.put("collapse", collapse); + json.set("collapse", collapse); - JSONObject trace = new JSONObject(); + ObjectNode trace = jsonMapper.createObjectNode(); trace.put("level", 1); trace.put("timestamps", false); trace.put("rules", "none"); - json.put("trace", trace); + json.set("trace", trace); - JSONObject pos = new JSONObject(); + ObjectNode pos = jsonMapper.createObjectNode(); pos.put("ll", "1263123N;1231.9W"); pos.put("radius", "71234m"); pos.put("bb", "1237123W;123218N"); pos.put("attribute", "default"); - json.put("pos", pos); + json.set("pos", pos); - JSONObject streaming = new JSONObject(); + ObjectNode streaming = jsonMapper.createObjectNode(); streaming.put("userid", 123); streaming.put("groupname", "abc"); streaming.put("selection", "none"); streaming.put("priority", 10); streaming.put("maxbucketspervisitor", 5); - json.put("streaming", streaming); + json.set("streaming", streaming); - JSONObject rules = new JSONObject(); + ObjectNode rules = jsonMapper.createObjectNode(); rules.put("off", false); rules.put("rulebase", "default"); - json.put("rules", rules); + json.set("rules", rules); - JSONObject metrics = new JSONObject(); + ObjectNode metrics = jsonMapper.createObjectNode(); metrics.put("ignore", "_all"); - json.put("metrics", metrics); + json.set("metrics", metrics); json.put("recall", "none"); json.put("user", 123); @@ -501,7 +512,7 @@ public class JSONSearchHandlerTestCase { json.put("hitcountestimate", true); // Create mapping - Inspector inspector = SlimeUtils.jsonToSlime(json.toString().getBytes("utf-8")).get(); + Inspector inspector = SlimeUtils.jsonToSlime(json.toString().getBytes(StandardCharsets.UTF_8)).get(); Map<String, String> map = new HashMap<>(); searchHandler.createRequestMapping(inspector, map, ""); @@ -518,12 +529,12 @@ public class JSONSearchHandlerTestCase { // Get mapping Map<String, String> propertyMap = request.propertyMap(); - assertEquals("Should have same mapping for properties", map, propertyMap); + Assertions.assertThat(propertyMap).isEqualTo(map); } @Test - public void testContentTypeParsing() throws Exception { - JSONObject json = new JSONObject(); + public void testContentTypeParsing() { + ObjectNode json = jsonMapper.createObjectNode(); json.put("query", "abc"); assertOkResult(driver.sendRequest(uri, com.yahoo.jdisc.http.HttpRequest.Method.POST, json.toString(), "Application/JSON; charset=utf-8"), jsonResult); } diff --git a/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java b/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java index c4d49c11f5e..48003f6586f 100644 --- a/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java +++ b/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java @@ -1,7 +1,6 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.search.rendering; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.util.concurrent.ListenableFuture; @@ -56,9 +55,6 @@ import com.yahoo.tensor.TensorType; import com.yahoo.tensor.serialization.TypedBinaryFormat; import com.yahoo.text.Utf8; import com.yahoo.yolean.trace.TraceNode; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; import org.junit.Before; import org.junit.Test; @@ -67,7 +63,6 @@ import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import java.nio.charset.StandardCharsets; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; @@ -83,6 +78,8 @@ import static org.junit.Assert.assertTrue; */ public class JsonRendererTestCase { + private static final ObjectMapper jsonMapper = new ObjectMapper(); + private JsonRenderer originalRenderer; private JsonRenderer renderer; @@ -959,7 +956,7 @@ public class JsonRendererTestCase { } @Test - public void testJsonObjects() throws InterruptedException, ExecutionException, IOException, JSONException { + public void testJsonObjects() throws InterruptedException, ExecutionException, IOException { String expected = "{" + " \"root\": {" + " \"children\": [" @@ -973,14 +970,6 @@ public class JsonRendererTestCase { + " }," + " \"json producer\": {" + " \"long in structured\": 7809531904" - + " }," - + " \"org.json array\": [" - + " true," - + " true," - + " false" - + " ]," - + " \"org.json object\": {" - + " \"forty-two\": 42" + " }" + " }," + " \"id\": \"json objects\"," @@ -996,26 +985,17 @@ public class JsonRendererTestCase { + "}"; Result r = newEmptyResult(); Hit h = new Hit("json objects"); - JSONObject o = new JSONObject(); - JSONArray a = new JSONArray(); - ObjectMapper mapper = new ObjectMapper(); - JsonNode j = mapper.createObjectNode(); + ObjectNode j = jsonMapper.createObjectNode(); JSONString s = new JSONString("{\"a\": \"b\"}"); Slime slime = new Slime(); Cursor c = slime.setObject(); c.setLong("long in structured", 7809531904L); SlimeAdapter slimeInit = new SlimeAdapter(slime.get()); StructuredData struct = new StructuredData(slimeInit); - ((ObjectNode) j).put("Nineteen-eighty-four", 1984); - o.put("forty-two", 42); - a.put(true); - a.put(true); - a.put(false); + j.put("Nineteen-eighty-four", 1984); h.setField("inspectable", s); h.setField("jackson", j); h.setField("json producer", struct); - h.setField("org.json array", a); - h.setField("org.json object", o); r.hits().add(h); String summary = render(r); assertEqualJson(expected, summary); @@ -1236,11 +1216,13 @@ public class JsonRendererTestCase { public void testThatTheJsonValidatorCanCatchErrors() { String json = "{" + " \"root\": {" - + " \"duplicate\": 1," - + " \"duplicate\": 2" + + " \"invalidvalue\": 1adsf," + " }" + "}"; - assertEquals("Duplicate key \"duplicate\"", validateJSON(json)); + assertEquals( + "Unexpected character ('a' (code 97)): was expecting comma to separate Object entries\n" + + " at [Source: { \"root\": { \"invalidvalue\": 1adsf, }}; line: 1, column: 41]", + validateJSON(json)); } @Test @@ -1316,9 +1298,9 @@ public class JsonRendererTestCase { private String validateJSON(String presumablyValidJson) { try { - new JSONObject(presumablyValidJson); + jsonMapper.readTree(presumablyValidJson); return ""; - } catch (JSONException e) { + } catch (IOException e) { return e.getMessage(); } } diff --git a/container-search/src/test/java/com/yahoo/select/SelectTestCase.java b/container-search/src/test/java/com/yahoo/select/SelectTestCase.java index 3239a97a094..9bcd3addd92 100644 --- a/container-search/src/test/java/com/yahoo/select/SelectTestCase.java +++ b/container-search/src/test/java/com/yahoo/select/SelectTestCase.java @@ -1,6 +1,9 @@ // Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.select; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.yahoo.prelude.query.AndItem; import com.yahoo.prelude.query.ExactStringItem; import com.yahoo.prelude.query.Item; @@ -17,19 +20,15 @@ import com.yahoo.prelude.query.WordItem; import com.yahoo.processing.IllegalInputException; import com.yahoo.search.Query; import com.yahoo.search.grouping.GroupingRequest; -import com.yahoo.search.grouping.request.AllOperation; import com.yahoo.search.query.QueryTree; import com.yahoo.search.query.Select; import com.yahoo.search.query.SelectParser; import com.yahoo.search.query.parser.Parsable; import com.yahoo.search.query.parser.ParserEnvironment; import com.yahoo.search.yql.VespaGroupingStep; -import org.json.JSONException; -import org.json.JSONObject; import org.junit.Test; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import static org.junit.Assert.assertEquals; @@ -48,15 +47,18 @@ import static org.junit.Assert.fail; */ public class SelectTestCase { + private static final ObjectMapper jsonMapper = new ObjectMapper(); + private final SelectParser parser = new SelectParser(new ParserEnvironment()); //------------------------------------------------------------------- "where" tests @Test - public void test_contains() throws Exception { - JSONObject json = new JSONObject(); - List<String> contains = Arrays.asList("default", "foo"); - json.put("contains", contains); + public void test_contains() { + ObjectNode json = jsonMapper.createObjectNode(); + ArrayNode arrayNode = jsonMapper.createArrayNode(); + arrayNode.add("default").add("foo"); + json.set("contains", arrayNode); assertParse(json.toString(), "default:foo"); } @@ -77,21 +79,21 @@ public class SelectTestCase { @Test public void testOr() throws Exception { - JSONObject json_two_or = new JSONObject(); - JSONObject json_three_or = new JSONObject(); - List<String> contains1 = Arrays.asList("title", "madonna"); - List<String> contains2 = Arrays.asList("title", "saint"); - List<String> contains3 = Arrays.asList("title", "angel"); - - JSONObject contains_json1 = new JSONObject(); - JSONObject contains_json2 = new JSONObject(); - JSONObject contains_json3 = new JSONObject(); - contains_json1.put("contains", contains1); - contains_json2.put("contains", contains2); - contains_json3.put("contains", contains3); - - json_two_or.put("or", Arrays.asList(contains_json1, contains_json2)); - json_three_or.put("or", Arrays.asList(contains_json1, contains_json2, contains_json3)); + ObjectNode json_two_or = jsonMapper.createObjectNode(); + ObjectNode json_three_or = jsonMapper.createObjectNode(); + ArrayNode contains1 = jsonMapper.createArrayNode().add("title").add("madonna"); + ArrayNode contains2 = jsonMapper.createArrayNode().add("title").add("saint"); + ArrayNode contains3 = jsonMapper.createArrayNode().add("title").add("angel"); + + ObjectNode contains_json1 = jsonMapper.createObjectNode(); + ObjectNode contains_json2 = jsonMapper.createObjectNode(); + ObjectNode contains_json3 = jsonMapper.createObjectNode(); + contains_json1.set("contains", contains1); + contains_json2.set("contains", contains2); + contains_json3.set("contains", contains3); + + json_two_or.set("or", jsonMapper.createArrayNode().add(contains_json1).add(contains_json2)); + json_three_or.set("or", jsonMapper.createArrayNode().add(contains_json1).add(contains_json2).add(contains_json3)); assertParse(json_two_or.toString(), "OR title:madonna title:saint"); assertParse(json_three_or.toString(), "OR title:madonna title:saint title:angel"); @@ -99,178 +101,178 @@ public class SelectTestCase { @Test public void testAnd() throws Exception{ - JSONObject json_two_and = new JSONObject(); - JSONObject json_three_and = new JSONObject(); - List<String> contains1 = Arrays.asList("title", "madonna"); - List<String> contains2 = Arrays.asList("title", "saint"); - List<String> contains3 = Arrays.asList("title", "angel"); - - JSONObject contains_json1 = new JSONObject(); - JSONObject contains_json2 = new JSONObject(); - JSONObject contains_json3 = new JSONObject(); - contains_json1.put("contains", contains1); - contains_json2.put("contains", contains2); - contains_json3.put("contains", contains3); - - json_two_and.put("and", Arrays.asList(contains_json1, contains_json2)); - json_three_and.put("and", Arrays.asList(contains_json1, contains_json2, contains_json3)); + ObjectNode json_two_and = jsonMapper.createObjectNode(); + ObjectNode json_three_and = jsonMapper.createObjectNode(); + ArrayNode contains1 = jsonMapper.createArrayNode().add("title").add("madonna"); + ArrayNode contains2 = jsonMapper.createArrayNode().add("title").add("saint"); + ArrayNode contains3 = jsonMapper.createArrayNode().add("title").add("angel"); + + ObjectNode contains_json1 = jsonMapper.createObjectNode(); + ObjectNode contains_json2 = jsonMapper.createObjectNode(); + ObjectNode contains_json3 = jsonMapper.createObjectNode(); + contains_json1.set("contains", contains1); + contains_json2.set("contains", contains2); + contains_json3.set("contains", contains3); + + json_two_and.set("and", jsonMapper.createArrayNode().add(contains_json1).add(contains_json2)); + json_three_and.set("and", jsonMapper.createArrayNode().add(contains_json1).add(contains_json2).add(contains_json3)); assertParse(json_two_and.toString(), "AND title:madonna title:saint"); assertParse(json_three_and.toString(), "AND title:madonna title:saint title:angel"); } @Test - public void testAndNot() throws JSONException { - JSONObject json_and_not = new JSONObject(); - List<String> contains1 = Arrays.asList("title", "madonna"); - List<String> contains2 = Arrays.asList("title", "saint"); + public void testAndNot() { + ObjectNode json_and_not = jsonMapper.createObjectNode(); + ArrayNode contains1 = jsonMapper.createArrayNode().add("title").add("madonna"); + ArrayNode contains2 = jsonMapper.createArrayNode().add("title").add("saint"); - JSONObject contains_json1 = new JSONObject(); - JSONObject contains_json2 = new JSONObject(); - contains_json1.put("contains", contains1); - contains_json2.put("contains", contains2); + ObjectNode contains_json1 = jsonMapper.createObjectNode(); + ObjectNode contains_json2 = jsonMapper.createObjectNode(); + contains_json1.set("contains", contains1); + contains_json2.set("contains", contains2); - json_and_not.put("and_not", Arrays.asList(contains_json1, contains_json2)); + json_and_not.set("and_not", jsonMapper.createArrayNode().add(contains_json1).add(contains_json2)); assertParse(json_and_not.toString(), "+title:madonna -title:saint"); } @Test - public void testLessThan() throws JSONException { - JSONObject range_json = new JSONObject(); - JSONObject operators = new JSONObject(); + public void testLessThan() { + ObjectNode range_json = jsonMapper.createObjectNode(); + ObjectNode operators = jsonMapper.createObjectNode(); operators.put("<", 500); - List<Object> range = Arrays.asList("price", operators); + ArrayNode range = jsonMapper.createArrayNode().add("price").add(operators); - range_json.put("range", range); + range_json.set("range", range); assertParse(range_json.toString(), "price:<500"); } @Test - public void testGreaterThan() throws JSONException { - JSONObject range_json = new JSONObject(); - JSONObject operators = new JSONObject(); + public void testGreaterThan() { + ObjectNode range_json = jsonMapper.createObjectNode(); + ObjectNode operators = jsonMapper.createObjectNode(); operators.put(">", 500); - List<Object> range = Arrays.asList("price", operators); + ArrayNode range = jsonMapper.createArrayNode().add("price").add(operators); - range_json.put("range", range); + range_json.set("range", range); assertParse(range_json.toString(), "price:>500"); } @Test - public void testLessThanOrEqual() throws JSONException { - JSONObject range_json = new JSONObject(); - JSONObject operators = new JSONObject(); + public void testLessThanOrEqual() { + ObjectNode range_json = jsonMapper.createObjectNode(); + ObjectNode operators = jsonMapper.createObjectNode(); operators.put("<=", 500); - List<Object> range = Arrays.asList("price", operators); + ArrayNode range = jsonMapper.createArrayNode().add("price").add(operators); - range_json.put("range", range); + range_json.set("range", range); assertParse(range_json.toString(), "price:[;500]"); } @Test - public void testGreaterThanOrEqual() throws JSONException { - JSONObject range_json = new JSONObject(); - JSONObject operators = new JSONObject(); + public void testGreaterThanOrEqual() { + ObjectNode range_json = jsonMapper.createObjectNode(); + ObjectNode operators = jsonMapper.createObjectNode(); operators.put(">=", 500); - List<Object> range = Arrays.asList("price", operators); + ArrayNode range = jsonMapper.createArrayNode().add("price").add(operators); - range_json.put("range", range); + range_json.set("range", range); assertParse(range_json.toString(), "price:[500;]"); } @Test - public void testEquality() throws JSONException { - JSONObject range_json = new JSONObject(); - JSONObject operators = new JSONObject(); + public void testEquality() { + ObjectNode range_json = jsonMapper.createObjectNode(); + ObjectNode operators = jsonMapper.createObjectNode(); operators.put("=", 500); - List<Object> range = Arrays.asList("price", operators); + ArrayNode range = jsonMapper.createArrayNode().add("price").add(operators); - range_json.put("range", range); + range_json.set("range", range); assertParse(range_json.toString(), "price:500"); } @Test - public void testNegativeLessThan() throws JSONException { - JSONObject range_json = new JSONObject(); - JSONObject operators = new JSONObject(); + public void testNegativeLessThan() { + ObjectNode range_json = jsonMapper.createObjectNode(); + ObjectNode operators = jsonMapper.createObjectNode(); operators.put("<", -500); - List<Object> range = Arrays.asList("price", operators); + ArrayNode range = jsonMapper.createArrayNode().add("price").add(operators); - range_json.put("range", range); + range_json.set("range", range); assertParse(range_json.toString(), "price:<-500"); } @Test - public void testNegativeGreaterThan() throws JSONException { - JSONObject range_json = new JSONObject(); - JSONObject operators = new JSONObject(); + public void testNegativeGreaterThan() { + ObjectNode range_json = jsonMapper.createObjectNode(); + ObjectNode operators = jsonMapper.createObjectNode(); operators.put(">", -500); - List<Object> range = Arrays.asList("price", operators); + ArrayNode range = jsonMapper.createArrayNode().add("price").add(operators); - range_json.put("range", range); + range_json.set("range", range); assertParse(range_json.toString(), "price:>-500"); } @Test - public void testNegativeLessThanOrEqual() throws JSONException { - JSONObject range_json = new JSONObject(); - JSONObject operators = new JSONObject(); + public void testNegativeLessThanOrEqual() { + ObjectNode range_json = jsonMapper.createObjectNode(); + ObjectNode operators = jsonMapper.createObjectNode(); operators.put("<=", -500); - List<Object> range = Arrays.asList("price", operators); + ArrayNode range = jsonMapper.createArrayNode().add("price").add(operators); - range_json.put("range", range); + range_json.set("range", range); assertParse(range_json.toString(), "price:[;-500]"); } @Test - public void testNegativeGreaterThanOrEqual() throws JSONException { - JSONObject range_json = new JSONObject(); - JSONObject operators = new JSONObject(); + public void testNegativeGreaterThanOrEqual() { + ObjectNode range_json = jsonMapper.createObjectNode(); + ObjectNode operators = jsonMapper.createObjectNode(); operators.put(">=", -500); - List<Object> range = Arrays.asList("price", operators); + ArrayNode range = jsonMapper.createArrayNode().add("price").add(operators); - range_json.put("range", range); + range_json.set("range", range); assertParse(range_json.toString(), "price:[-500;]"); } @Test - public void testNegativeEquality() throws JSONException { - JSONObject range_json = new JSONObject(); - JSONObject operators = new JSONObject(); + public void testNegativeEquality() { + ObjectNode range_json = jsonMapper.createObjectNode(); + ObjectNode operators = jsonMapper.createObjectNode(); operators.put("=", -500); - List<Object> range = Arrays.asList("price", operators); + ArrayNode range = jsonMapper.createArrayNode().add("price").add(operators); - range_json.put("range", range); + range_json.set("range", range); assertParse(range_json.toString(), "price:-500"); diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/ProtonMetrics.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/ProtonMetrics.java index c6d907ec7fc..ed88902e094 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/ProtonMetrics.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/application/v4/model/ProtonMetrics.java @@ -1,14 +1,19 @@ package com.yahoo.vespa.hosted.controller.api.application.v4.model; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + import java.util.HashMap; import java.util.Map; +import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.Logger; -import org.json.JSONException; -import org.json.JSONObject; public class ProtonMetrics { + private static final ObjectMapper jsonMapper = new ObjectMapper(); + private static final Logger logger = LogManager.getLogManager().getLogger(ProtonMetrics.class.getName()); public static final String DOCUMENTS_ACTIVE_COUNT = "documentsActiveCount"; @@ -45,19 +50,19 @@ public class ProtonMetrics { return this; } - public JSONObject toJson() { + public JsonNode toJson() { try { - JSONObject protonMetrics = new JSONObject(); + ObjectNode protonMetrics = jsonMapper.createObjectNode(); protonMetrics.put("clusterId", clusterId); - JSONObject jsonMetrics = new JSONObject(); + ObjectNode jsonMetrics = jsonMapper.createObjectNode(); for (Map.Entry<String, Double> entry : metrics.entrySet()) { jsonMetrics.put(entry.getKey(), entry.getValue()); } - protonMetrics.put("metrics", jsonMetrics); + protonMetrics.set("metrics", jsonMetrics); return protonMetrics; - } catch (JSONException e) { - logger.severe("Unable to convert Proton Metrics to JSON Object"); + } catch (Exception e) { + logger.log(Level.SEVERE, "Unable to convert Proton Metrics to JSON Object: " + e.getMessage(), e); } - return new JSONObject(); + return jsonMapper.createObjectNode(); } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java index 5a1496bf507..431a694fcd8 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java @@ -2,6 +2,8 @@ package com.yahoo.vespa.hosted.controller.restapi.application; import ai.vespa.hosted.api.Signatures; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableSet; import com.google.inject.Inject; @@ -100,9 +102,6 @@ import com.yahoo.vespa.hosted.controller.versions.VersionStatus; import com.yahoo.vespa.hosted.controller.versions.VespaVersion; import com.yahoo.vespa.serviceview.bindings.ApplicationView; import com.yahoo.yolean.Exceptions; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; import javax.ws.rs.ForbiddenException; import javax.ws.rs.InternalServerErrorException; @@ -151,6 +150,8 @@ import static java.util.stream.Collectors.toUnmodifiableList; @SuppressWarnings("unused") // created by injection public class ApplicationApiHandler extends LoggingRequestHandler { + private static final ObjectMapper jsonMapper = new ObjectMapper(); + private static final String OPTIONAL_PREFIX = "/api"; private final Controller controller; @@ -789,15 +790,15 @@ public class ApplicationApiHandler extends LoggingRequestHandler { private JsonResponse buildResponseFromProtonMetrics(List<ProtonMetrics> protonMetrics) { try { - var jsonObject = new JSONObject(); - var jsonArray = new JSONArray(); + var jsonObject = jsonMapper.createObjectNode(); + var jsonArray = jsonMapper.createArrayNode(); for (ProtonMetrics metrics : protonMetrics) { - jsonArray.put(metrics.toJson()); + jsonArray.add(metrics.toJson()); } - jsonObject.put("metrics", jsonArray); - return new JsonResponse(200, jsonObject.toString()); - } catch (JSONException e) { - log.severe("Unable to build JsonResponse with Proton data"); + jsonObject.set("metrics", jsonArray); + return new JsonResponse(200, jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject)); + } catch (JsonProcessingException e) { + log.log(Level.SEVERE, "Unable to build JsonResponse with Proton data: " + e.getMessage(), e); return new JsonResponse(500, ""); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java index 828e2856cae..c43abf276c5 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java @@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.controller.restapi.application; import com.yahoo.component.Version; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.container.jdisc.HttpResponse; +import com.yahoo.test.json.JsonTestHelper; import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions; import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; @@ -12,8 +13,6 @@ import com.yahoo.vespa.hosted.controller.api.integration.deployment.TestReport; import com.yahoo.vespa.hosted.controller.application.ApplicationPackage; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; -import org.json.JSONException; -import org.json.JSONObject; import org.junit.Test; import java.io.ByteArrayOutputStream; @@ -180,12 +179,10 @@ public class JobControllerApiHandlerHelperTest { "jobs-direct-deployment.json"); } - private void compare(HttpResponse response, String expected) throws JSONException, IOException { + private void compare(HttpResponse response, String expected) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); response.render(baos); - JSONObject actualJSON = new JSONObject(new String(baos.toByteArray())); - JSONObject expectedJSON = new JSONObject(expected); - assertEquals(expectedJSON.toString(), actualJSON.toString()); + JsonTestHelper.assertJsonEquals(expected, baos.toString()); } private void assertResponse(HttpResponse response, String fileName) { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/proton-metrics.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/proton-metrics.json index a7e5b3918d8..3fba9b3c91c 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/proton-metrics.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/proton-metrics.json @@ -1,23 +1,26 @@ { - "metrics": [{ - "clusterId": "content/doc/", - "metrics": { - "resourceMemoryUsageAverage": 0.103482, - "documentsReadyCount": 11430, - "documentDiskUsage": 44021, - "resourceDiskUsageAverage": 0.0168421, - "documentsTotalCount": 11430, - "documentsActiveCount": 11430 + "metrics": [ + { + "clusterId": "content/doc/", + "metrics": { + "resourceMemoryUsageAverage": 0.103482, + "documentsReadyCount": 11430.0, + "documentDiskUsage": 44021.0, + "resourceDiskUsageAverage": 0.0168421, + "documentsTotalCount": 11430.0, + "documentsActiveCount": 11430.0 + } + }, + { + "clusterId": "content/music/", + "metrics": { + "resourceMemoryUsageAverage": 0.00912, + "documentsReadyCount": 32000.0, + "documentDiskUsage": 90113.0, + "resourceDiskUsageAverage": 0.23912, + "documentsTotalCount": 32210.0, + "documentsActiveCount": 32210.0 + } } - }, { - "clusterId": "content/music/", - "metrics": { - "resourceMemoryUsageAverage": 0.00912, - "documentsReadyCount": 32000, - "documentDiskUsage": 90113, - "resourceDiskUsageAverage": 0.23912, - "documentsTotalCount": 32210, - "documentsActiveCount": 32210 - } - }] -}
\ No newline at end of file + ] +} diff --git a/metrics-proxy/pom.xml b/metrics-proxy/pom.xml index 8bf5a30e584..90d9f093da1 100644 --- a/metrics-proxy/pom.xml +++ b/metrics-proxy/pom.xml @@ -101,11 +101,6 @@ <version>${project.version}</version> <scope>provided</scope> </dependency> - <dependency> - <groupId>org.json</groupId> - <artifactId>json</artifactId> - <scope>provided</scope> - </dependency> <!-- compile scope --> diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/node/NodeMetricGatherer.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/node/NodeMetricGatherer.java index 3cd9f526387..60e8e3deee5 100644 --- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/node/NodeMetricGatherer.java +++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/node/NodeMetricGatherer.java @@ -9,13 +9,11 @@ import ai.vespa.metricsproxy.metric.model.MetricsPacket; import ai.vespa.metricsproxy.metric.model.ServiceId; import ai.vespa.metricsproxy.service.SystemPollerProvider; import ai.vespa.metricsproxy.service.VespaServices; +import com.fasterxml.jackson.databind.JsonNode; import com.google.inject.Inject; import com.yahoo.container.jdisc.state.CoredumpGatherer; import com.yahoo.container.jdisc.state.FileWrapper; import com.yahoo.container.jdisc.state.HostLifeGatherer; -import com.yahoo.yolean.Exceptions; -import org.json.JSONException; -import org.json.JSONObject; import java.util.ArrayList; import java.util.Iterator; @@ -54,10 +52,10 @@ public class NodeMetricGatherer { List<MetricsPacket.Builder> metricPacketBuilders = new ArrayList<>(); metricPacketBuilders.addAll(gatherServiceHealthMetrics(vespaServices)); - JSONObject coredumpPacket = CoredumpGatherer.gatherCoredumpMetrics(fileWrapper); + JsonNode coredumpPacket = CoredumpGatherer.gatherCoredumpMetrics(fileWrapper); addObjectToBuilders(metricPacketBuilders, coredumpPacket); if (SystemPollerProvider.runningOnLinux()) { - JSONObject packet = HostLifeGatherer.getHostLifePacket(fileWrapper); + JsonNode packet = HostLifeGatherer.getHostLifePacket(fileWrapper); addObjectToBuilders(metricPacketBuilders, packet); } @@ -69,24 +67,20 @@ public class NodeMetricGatherer { ).collect(Collectors.toList()); } - protected static void addObjectToBuilders(List<MetricsPacket.Builder> builders, JSONObject object) { - try { - MetricsPacket.Builder builder = new MetricsPacket.Builder(ServiceId.toServiceId(object.getString("application"))); - builder.timestamp(object.getLong("timestamp")); - if (object.has("status_code")) builder.statusCode(object.getInt("status_code")); - if (object.has("status_msg")) builder.statusMessage(object.getString("status_msg")); - if (object.has("metrics")) { - JSONObject metrics = object.getJSONObject("metrics"); - Iterator<?> keys = metrics.keys(); - while(keys.hasNext()) { - String key = (String) keys.next(); - builder.putMetric(MetricId.toMetricId(key), metrics.getLong(key)); - } + protected static void addObjectToBuilders(List<MetricsPacket.Builder> builders, JsonNode object) { + MetricsPacket.Builder builder = new MetricsPacket.Builder(ServiceId.toServiceId(object.get("application").textValue())); + builder.timestamp(object.get("timestamp").longValue()); + if (object.has("status_code")) builder.statusCode(object.get("status_code").intValue()); + if (object.has("status_msg")) builder.statusMessage(object.get("status_msg").textValue()); + if (object.has("metrics")) { + JsonNode metrics = object.get("metrics"); + Iterator<?> keys = metrics.fieldNames(); + while(keys.hasNext()) { + String key = (String) keys.next(); + builder.putMetric(MetricId.toMetricId(key), metrics.get(key).longValue()); } - builders.add(builder); - } catch (JSONException e) { - Exceptions.toMessageString(e); } + builders.add(builder); } } diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteHealthMetricFetcher.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteHealthMetricFetcher.java index 1cab1a859a9..6e7bb37d761 100644 --- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteHealthMetricFetcher.java +++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteHealthMetricFetcher.java @@ -2,9 +2,10 @@ package ai.vespa.metricsproxy.service; import ai.vespa.metricsproxy.metric.HealthMetric; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + import java.util.logging.Level; -import org.json.JSONException; -import org.json.JSONObject; import java.io.IOException; import java.util.logging.Logger; @@ -15,6 +16,7 @@ import java.util.logging.Logger; * @author Jo Kristian Bergum */ public class RemoteHealthMetricFetcher extends HttpMetricFetcher { + private static final ObjectMapper jsonMapper = new ObjectMapper(); private final static Logger log = Logger.getLogger(RemoteHealthMetricFetcher.class.getPackage().getName()); private final static String HEALTH_PATH = STATE_PATH + "health"; @@ -54,16 +56,16 @@ public class RemoteHealthMetricFetcher extends HttpMetricFetcher { return HealthMetric.getUnknown("Empty response from status page"); } try { - JSONObject o = new JSONObject(data); - JSONObject status = o.getJSONObject("status"); - String code = status.getString("code"); + JsonNode o = jsonMapper.readTree(data); + JsonNode status = o.get("status"); + String code = status.get("code").textValue(); String message = ""; if (status.has("message")) { - message = status.getString("message"); + message = status.get("message").textValue(); } return HealthMetric.get(code, message); - } catch (JSONException e) { + } catch (IOException e) { log.log(Level.FINE, "Failed to parse json response from metrics page:" + e + ":" + data); return HealthMetric.getUnknown("Not able to parse json from status page"); } diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteMetricsFetcher.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteMetricsFetcher.java index 442ebc0d38d..c2935761179 100644 --- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteMetricsFetcher.java +++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/service/RemoteMetricsFetcher.java @@ -4,9 +4,9 @@ package ai.vespa.metricsproxy.service; import ai.vespa.metricsproxy.metric.Metric; import ai.vespa.metricsproxy.metric.Metrics; import ai.vespa.metricsproxy.metric.model.DimensionId; -import org.json.JSONArray; -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.ArrayNode; import java.io.IOException; import java.util.Collections; @@ -23,6 +23,8 @@ import static ai.vespa.metricsproxy.metric.model.DimensionId.toDimensionId; */ public class RemoteMetricsFetcher extends HttpMetricFetcher { + private static final ObjectMapper jsonMapper = new ObjectMapper(); + final static String METRICS_PATH = STATE_PATH + "metrics"; RemoteMetricsFetcher(VespaService service, int port) { @@ -57,21 +59,21 @@ public class RemoteMetricsFetcher extends HttpMetricFetcher { return remoteMetrics; } - private Metrics parse(String data) throws JSONException { - JSONObject o = new JSONObject(data); + private Metrics parse(String data) throws IOException { + JsonNode o = jsonMapper.readTree(data); if (!(o.has("metrics"))) { return new Metrics(); //empty } - JSONObject metrics = o.getJSONObject("metrics"); - JSONArray values; + JsonNode metrics = o.get("metrics"); + ArrayNode values; long timestamp; try { - JSONObject snapshot = metrics.getJSONObject("snapshot"); - timestamp = (long) snapshot.getDouble("to"); - values = metrics.getJSONArray("values"); - } catch (JSONException e) { + JsonNode snapshot = metrics.get("snapshot"); + timestamp = (long) snapshot.get("to").doubleValue(); + values = (ArrayNode) metrics.get("values"); + } catch (Exception e) { // snapshot might not have been produced. Do not throw exception into log return new Metrics(); } @@ -81,29 +83,29 @@ public class RemoteMetricsFetcher extends HttpMetricFetcher { Map<DimensionId, String> noDims = Collections.emptyMap(); Map<String, Map<DimensionId, String>> uniqueDimensions = new HashMap<>(); - for (int i = 0; i < values.length(); i++) { - JSONObject metric = values.getJSONObject(i); - String name = metric.getString("name"); + for (int i = 0; i < values.size(); i++) { + JsonNode metric = values.get(i); + String name = metric.get("name").textValue(); String description = ""; if (metric.has("description")) { - description = metric.getString("description"); + description = metric.get("description").textValue(); } Map<DimensionId, String> dim = noDims; if (metric.has("dimensions")) { - JSONObject dimensions = metric.getJSONObject("dimensions"); + JsonNode dimensions = metric.get("dimensions"); StringBuilder sb = new StringBuilder(); - for (Iterator<?> it = dimensions.keys(); it.hasNext(); ) { + for (Iterator<?> it = dimensions.fieldNames(); it.hasNext(); ) { String k = (String) it.next(); - String v = dimensions.getString(k); + String v = dimensions.get(k).textValue(); sb.append(toDimensionId(k)).append(v); } if ( ! uniqueDimensions.containsKey(sb.toString())) { dim = new HashMap<>(); - for (Iterator<?> it = dimensions.keys(); it.hasNext(); ) { + for (Iterator<?> it = dimensions.fieldNames(); it.hasNext(); ) { String k = (String) it.next(); - String v = dimensions.getString(k); + String v = dimensions.get(k).textValue(); dim.put(toDimensionId(k), v); } uniqueDimensions.put(sb.toString(), Collections.unmodifiableMap(dim)); @@ -111,10 +113,11 @@ public class RemoteMetricsFetcher extends HttpMetricFetcher { dim = uniqueDimensions.get(sb.toString()); } - JSONObject aggregates = metric.getJSONObject("values"); - for (Iterator<?> it = aggregates.keys(); it.hasNext(); ) { + JsonNode aggregates = metric.get("values"); + for (Iterator<?> it = aggregates.fieldNames(); it.hasNext(); ) { String aggregator = (String) it.next(); - Number value = (Number) aggregates.get(aggregator); + Number value = aggregates.get(aggregator).numberValue(); + if (value == null) throw new IllegalArgumentException("Value for aggregator '" + aggregator + "' is missing"); StringBuilder metricName = (new StringBuilder()).append(name).append(".").append(aggregator); m.add(new Metric(metricName.toString(), value, timestamp, dim, description)); } diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/application/ApplicationMetricsHandlerTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/application/ApplicationMetricsHandlerTest.java index d7576718e8a..cf1eac3c691 100644 --- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/application/ApplicationMetricsHandlerTest.java +++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/application/ApplicationMetricsHandlerTest.java @@ -8,13 +8,11 @@ import ai.vespa.metricsproxy.metric.model.json.GenericApplicationModel; import ai.vespa.metricsproxy.metric.model.json.GenericJsonModel; import ai.vespa.metricsproxy.metric.model.json.GenericMetrics; import ai.vespa.metricsproxy.metric.model.json.GenericService; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; import com.github.tomakehurst.wiremock.junit.WireMockRule; import com.yahoo.container.jdisc.RequestHandlerTestDriver; -import java.util.regex.Pattern; - -import org.json.JSONArray; -import org.json.JSONObject; import org.junit.Before; import org.junit.Ignore; import org.junit.Rule; @@ -24,6 +22,7 @@ import java.io.IOException; import java.util.Arrays; import java.util.Map; import java.util.concurrent.Executors; +import java.util.regex.Pattern; import static ai.vespa.metricsproxy.TestUtil.getFileContents; import static ai.vespa.metricsproxy.http.ValuesFetcher.defaultMetricsConsumerId; @@ -49,6 +48,8 @@ import static org.junit.Assert.fail; @SuppressWarnings("UnstableApiUsage") public class ApplicationMetricsHandlerTest { + private static final ObjectMapper jsonMapper = new ObjectMapper(); + private static final String HOST = "localhost"; private static final String URI_BASE = "http://" + HOST; private static final String METRICS_V1_URI = URI_BASE + METRICS_V1_PATH; @@ -102,16 +103,16 @@ public class ApplicationMetricsHandlerTest { @Test public void v1_response_contains_values_uri() throws Exception { String response = testDriver.sendRequest(METRICS_V1_URI).readAll(); - JSONObject root = new JSONObject(response); + JsonNode root = jsonMapper.readTree(response); assertTrue(root.has("resources")); - JSONArray resources = root.getJSONArray("resources"); - assertEquals(2, resources.length()); + ArrayNode resources = (ArrayNode) root.get("resources"); + assertEquals(2, resources.size()); - JSONObject valuesUrl = resources.getJSONObject(0); - assertEquals(METRICS_VALUES_URI, valuesUrl.getString("url")); - JSONObject prometheusUrl = resources.getJSONObject(1); - assertEquals(PROMETHEUS_VALUES_URI, prometheusUrl.getString("url")); + JsonNode valuesUrl = resources.get(0); + assertEquals(METRICS_VALUES_URI, valuesUrl.get("url").textValue()); + JsonNode prometheusUrl = resources.get(1); + assertEquals(PROMETHEUS_VALUES_URI, prometheusUrl.get("url").textValue()); } @Ignore @@ -199,7 +200,7 @@ public class ApplicationMetricsHandlerTest { @Test public void invalid_path_yields_error_response() throws Exception { String response = testDriver.sendRequest(METRICS_V1_URI + "/invalid").readAll(); - JSONObject root = new JSONObject(response); + JsonNode root = jsonMapper.readTree(response); assertTrue(root.has("error")); } diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/metrics/MetricsHandlerTestBase.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/metrics/MetricsHandlerTestBase.java index 1c5ce695155..379ef04d38d 100644 --- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/metrics/MetricsHandlerTestBase.java +++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/metrics/MetricsHandlerTestBase.java @@ -6,9 +6,9 @@ import ai.vespa.metricsproxy.metric.model.json.GenericJsonModel; import ai.vespa.metricsproxy.metric.model.json.GenericMetrics; import ai.vespa.metricsproxy.metric.model.json.GenericService; import ai.vespa.metricsproxy.service.DownService; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import org.json.JSONArray; -import org.json.JSONObject; +import com.fasterxml.jackson.databind.node.ArrayNode; import org.junit.Ignore; import org.junit.Test; @@ -32,6 +32,8 @@ import static org.junit.Assert.fail; */ public abstract class MetricsHandlerTestBase<MODEL> extends HttpHandlerTestBase { + private static final ObjectMapper jsonMapper = new ObjectMapper(); + static String rootUri; static String valuesUri; @@ -56,21 +58,21 @@ public abstract class MetricsHandlerTestBase<MODEL> extends HttpHandlerTestBase @Test public void invalid_path_yields_error_response() throws Exception { String response = testDriver.sendRequest(rootUri + "/invalid").readAll(); - JSONObject root = new JSONObject(response); + JsonNode root = jsonMapper.readTree(response); assertTrue(root.has("error")); } @Test public void root_response_contains_values_uri() throws Exception { String response = testDriver.sendRequest(rootUri).readAll(); - JSONObject root = new JSONObject(response); + JsonNode root = jsonMapper.readTree(response); assertTrue(root.has("resources")); - JSONArray resources = root.getJSONArray("resources"); - assertEquals(1, resources.length()); + ArrayNode resources = (ArrayNode) root.get("resources"); + assertEquals(1, resources.size()); - JSONObject valuesUrl = resources.getJSONObject(0); - assertEquals(valuesUri, valuesUrl.getString("url")); + JsonNode valuesUrl = resources.get(0); + assertEquals(valuesUri, valuesUrl.get("url").textValue()); } @Ignore diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/prometheus/PrometheusHandlerTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/prometheus/PrometheusHandlerTest.java index a224c4090b3..89186e63b93 100644 --- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/prometheus/PrometheusHandlerTest.java +++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/prometheus/PrometheusHandlerTest.java @@ -3,9 +3,10 @@ package ai.vespa.metricsproxy.http.prometheus; import ai.vespa.metricsproxy.http.HttpHandlerTestBase; import ai.vespa.metricsproxy.service.DummyService; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; import com.yahoo.container.jdisc.RequestHandlerTestDriver; -import org.json.JSONArray; -import org.json.JSONObject; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; @@ -22,6 +23,8 @@ import static org.junit.Assert.assertTrue; @SuppressWarnings("UnstableApiUsage") public class PrometheusHandlerTest extends HttpHandlerTestBase { + private static final ObjectMapper jsonMapper = new ObjectMapper(); + private static final String V1_URI = URI_BASE + PrometheusHandler.V1_PATH; private static final String VALUES_URI = URI_BASE + PrometheusHandler.VALUES_PATH; @@ -40,14 +43,14 @@ public class PrometheusHandlerTest extends HttpHandlerTestBase { @Test public void v1_response_contains_values_uri() throws Exception { String response = testDriver.sendRequest(V1_URI).readAll(); - JSONObject root = new JSONObject(response); + JsonNode root = jsonMapper.readTree(response); assertTrue(root.has("resources")); - JSONArray resources = root.getJSONArray("resources"); - assertEquals(1, resources.length()); + ArrayNode resources = (ArrayNode) root.get("resources"); + assertEquals(1, resources.size()); - JSONObject valuesUrl = resources.getJSONObject(0); - assertEquals(VALUES_URI, valuesUrl.getString("url")); + JsonNode valuesUrl = resources.get(0); + assertEquals(VALUES_URI, valuesUrl.get("url").textValue()); } @Ignore diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/node/NodeMetricGathererTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/node/NodeMetricGathererTest.java index e2ad0ccd504..c2fc23a878d 100644 --- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/node/NodeMetricGathererTest.java +++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/node/NodeMetricGathererTest.java @@ -3,8 +3,9 @@ package ai.vespa.metricsproxy.node; import ai.vespa.metricsproxy.metric.model.MetricId; import ai.vespa.metricsproxy.metric.model.MetricsPacket; -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 org.junit.Test; import java.util.ArrayList; @@ -17,10 +18,12 @@ import static org.junit.Assert.assertEquals; */ public class NodeMetricGathererTest { + private static final ObjectMapper jsonMapper = new ObjectMapper(); + @Test - public void testJSONObjectIsCorrectlyConvertedToMetricsPacket() throws JSONException { + public void testJSONObjectIsCorrectlyConvertedToMetricsPacket() { List<MetricsPacket.Builder> builders = new ArrayList<>(); - JSONObject hostLifePacket = generateHostLifePacket(); + JsonNode hostLifePacket = generateHostLifePacket(); NodeMetricGatherer.addObjectToBuilders(builders, hostLifePacket); MetricsPacket packet = builders.remove(0).build(); @@ -32,17 +35,17 @@ public class NodeMetricGathererTest { assertEquals(1l, packet.metrics().get(MetricId.toMetricId("alive"))); } - private JSONObject generateHostLifePacket() throws JSONException { + private JsonNode generateHostLifePacket() { - JSONObject jsonObject = new JSONObject(); + ObjectNode jsonObject = jsonMapper.createObjectNode(); jsonObject.put("status_code", 0); jsonObject.put("status_msg", "OK"); jsonObject.put("timestamp", 123); jsonObject.put("application", "host_life"); - JSONObject metrics = new JSONObject(); + ObjectNode metrics = jsonMapper.createObjectNode(); metrics.put("uptime", 12); metrics.put("alive", 1); - jsonObject.put("metrics", metrics); + jsonObject.set("metrics", metrics); return jsonObject; } } diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/rpc/RpcMetricsTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/rpc/RpcMetricsTest.java index 8d5bba77844..70970bfe8da 100644 --- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/rpc/RpcMetricsTest.java +++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/rpc/RpcMetricsTest.java @@ -5,17 +5,18 @@ import ai.vespa.metricsproxy.metric.Metric; import ai.vespa.metricsproxy.metric.Metrics; import ai.vespa.metricsproxy.metric.model.ConsumerId; import ai.vespa.metricsproxy.service.VespaService; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; import com.yahoo.jrt.Request; import com.yahoo.jrt.Spec; import com.yahoo.jrt.StringValue; import com.yahoo.jrt.Supervisor; import com.yahoo.jrt.Target; import com.yahoo.jrt.Transport; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; import org.junit.Test; +import java.io.IOException; import java.util.List; import static ai.vespa.metricsproxy.TestUtil.getFileContents; @@ -40,6 +41,8 @@ import static org.junit.Assert.assertTrue; */ public class RpcMetricsTest { + private static final ObjectMapper jsonMapper = new ObjectMapper(); + private static final String METRICS_RESPONSE = getFileContents("metrics-storage-simple.json").trim(); private static final String EXTRA_APP = "extra"; @@ -67,9 +70,9 @@ public class RpcMetricsTest { String allServicesResponse = getMetricsForYamas(ALL_SERVICES, rpcClient).trim(); // Verify that application is used as serviceId, and that metric exists. - JSONObject extraMetrics = findExtraMetricsObject(allServicesResponse); - assertThat(extraMetrics.getJSONObject("metrics").getInt("foo.count"), is(3)); - assertThat(extraMetrics.getJSONObject("dimensions").getString("role"), is("extra-role")); + JsonNode extraMetrics = findExtraMetricsObject(allServicesResponse); + assertThat(extraMetrics.get("metrics").get("foo.count").intValue(), is(3)); + assertThat(extraMetrics.get("dimensions").get("role").textValue(), is("extra-role")); } } } @@ -85,7 +88,7 @@ public class RpcMetricsTest { // Verify that no extra metrics exists String allServicesResponse = getMetricsForYamas(ALL_SERVICES, rpcClient).trim(); - JSONObject extraMetrics = findExtraMetricsObject(allServicesResponse); + JsonNode extraMetrics = findExtraMetricsObject(allServicesResponse); assertEquals(extraMetrics.toString(), "{}"); } } @@ -130,28 +133,28 @@ public class RpcMetricsTest { } } - private static void verifyMetricsFromRpcRequest(VespaService service, RpcClient client) throws JSONException { + private static void verifyMetricsFromRpcRequest(VespaService service, RpcClient client) throws IOException { String jsonResponse = getMetricsForYamas(service.getMonitoringName(), client).trim(); - JSONArray metrics = new JSONObject(jsonResponse).getJSONArray("metrics"); - assertThat("Expected 3 metric messages", metrics.length(), is(3)); - for (int i = 0; i < metrics.length() - 1; i++) { // The last "metric message" contains only status code/message - JSONObject jsonObject = metrics.getJSONObject(i); + ArrayNode metrics = (ArrayNode) jsonMapper.readTree(jsonResponse).get("metrics"); + assertThat("Expected 3 metric messages", metrics.size(), is(3)); + for (int i = 0; i < metrics.size() - 1; i++) { // The last "metric message" contains only status code/message + JsonNode jsonObject = metrics.get(i); assertFalse(jsonObject.has("status_code")); assertFalse(jsonObject.has("status_msg")); - assertThat(jsonObject.getJSONObject("dimensions").getString("foo"), is("bar")); - assertThat(jsonObject.getJSONObject("dimensions").getString("bar"), is("foo")); - assertThat(jsonObject.getJSONObject("dimensions").getString("serviceDim"), is("serviceDimValue")); - assertThat(jsonObject.getJSONObject("routing").getJSONObject("yamas").getJSONArray("namespaces").length(), is(1)); - if (jsonObject.getJSONObject("metrics").has("foo_count")) { - assertThat(jsonObject.getJSONObject("metrics").getInt("foo_count"), is(1)); - assertThat(jsonObject.getJSONObject("routing").getJSONObject("yamas").getJSONArray("namespaces").get(0), is(vespaMetricsConsumerId.id)); + assertThat(jsonObject.get("dimensions").get("foo").textValue(), is("bar")); + assertThat(jsonObject.get("dimensions").get("bar").textValue(), is("foo")); + assertThat(jsonObject.get("dimensions").get("serviceDim").textValue(), is("serviceDimValue")); + assertThat(jsonObject.get("routing").get("yamas").get("namespaces").size(), is(1)); + if (jsonObject.get("metrics").has("foo_count")) { + assertThat(jsonObject.get("metrics").get("foo_count").intValue(), is(1)); + assertThat(jsonObject.get("routing").get("yamas").get("namespaces").get(0).textValue(), is(vespaMetricsConsumerId.id)); } else { - assertThat(jsonObject.getJSONObject("metrics").getInt("foo.count"), is(1)); - assertThat(jsonObject.getJSONObject("routing").getJSONObject("yamas").getJSONArray("namespaces").get(0), is(CUSTOM_CONSUMER_ID.id)); + assertThat(jsonObject.get("metrics").get("foo.count").intValue(), is(1)); + assertThat(jsonObject.get("routing").get("yamas").get("namespaces").get(0).textValue(), is(CUSTOM_CONSUMER_ID.id)); } } - verifyStatusMessage(metrics.getJSONObject(metrics.length() - 1)); + verifyStatusMessage(metrics.get(metrics.size() - 1)); } private void verfiyMetricsFromServiceObject(VespaService service) { @@ -166,15 +169,15 @@ public class RpcMetricsTest { assertThat("Metric foo did not contain correct dimension for key = bar", foo.getDimensions().get(toDimensionId("bar")), is("foo")); } - private void verifyMetricsFromRpcRequestForAllServices(RpcClient client) throws JSONException { + private void verifyMetricsFromRpcRequestForAllServices(RpcClient client) throws IOException { // Verify that metrics for all services can be retrieved in one request. String allServicesResponse = getMetricsForYamas(ALL_SERVICES, client).trim(); - JSONArray allServicesMetrics = new JSONObject(allServicesResponse).getJSONArray("metrics"); - assertThat(allServicesMetrics.length(), is(5)); + ArrayNode allServicesMetrics = (ArrayNode) jsonMapper.readTree(allServicesResponse).get("metrics"); + assertThat(allServicesMetrics.size(), is(5)); } @Test - public void testGetAllMetricNames() throws Exception { + public void testGetAllMetricNames() { try (IntegrationTester tester = new IntegrationTester()) { tester.httpServer().setResponse(METRICS_RESPONSE); @@ -205,14 +208,14 @@ public class RpcMetricsTest { invoke(req, rpcClient, false); } - private JSONObject findExtraMetricsObject(String jsonResponse) throws JSONException { - JSONArray metrics = new JSONObject(jsonResponse).getJSONArray("metrics"); - for (int i = 0; i < metrics.length(); i++) { - JSONObject jsonObject = metrics.getJSONObject(i); + private JsonNode findExtraMetricsObject(String jsonResponse) throws IOException { + ArrayNode metrics = (ArrayNode) jsonMapper.readTree(jsonResponse).get("metrics"); + for (int i = 0; i < metrics.size(); i++) { + JsonNode jsonObject = metrics.get(i); assertTrue(jsonObject.has("application")); - if (jsonObject.getString("application").equals(EXTRA_APP)) return jsonObject; + if (jsonObject.get("application").textValue().equals(EXTRA_APP)) return jsonObject; } - return new JSONObject(); + return jsonMapper.createObjectNode(); } private static String getMetricsForYamas(String service, RpcClient client) { @@ -250,12 +253,12 @@ public class RpcMetricsTest { return returnValue; } - private static void verifyStatusMessage(JSONObject jsonObject) throws JSONException { - assertThat(jsonObject.getInt("status_code"), is(0)); - assertThat(jsonObject.getString("status_msg"), notNullValue()); - assertThat(jsonObject.getString("application"), notNullValue()); - assertThat(jsonObject.getString("routing"), notNullValue()); - assertThat(jsonObject.length(), is(4)); + private static void verifyStatusMessage(JsonNode jsonObject) { + assertThat(jsonObject.get("status_code").intValue(), is(0)); + assertThat(jsonObject.get("status_msg").textValue(), notNullValue()); + assertThat(jsonObject.get("application").textValue(), notNullValue()); + assertThat(jsonObject.get("routing"), notNullValue()); + assertThat(jsonObject.size(), is(4)); } } diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/ContainerServiceTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/ContainerServiceTest.java index 0d53f988ac7..7ff179e5528 100644 --- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/ContainerServiceTest.java +++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/service/ContainerServiceTest.java @@ -2,7 +2,6 @@ package ai.vespa.metricsproxy.service; import ai.vespa.metricsproxy.metric.Metric; -import org.json.JSONException; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; @@ -37,7 +36,7 @@ public class ContainerServiceTest { } @Test - public void testMultipleQueryDimensions() throws JSONException { + public void testMultipleQueryDimensions() { int count = 0; VespaService service = VespaService.create("service1", "id", httpServer.port()); for (Metric m : service.getMetrics().getMetrics()) { |