From 4944fa0e34b6adc20ecc5d98178e630224ae0dba Mon Sep 17 00:00:00 2001 From: Sylvain Wallez Date: Mon, 31 Mar 2025 17:53:33 +0200 Subject: [PATCH 1/6] Update dependencies, and use Jackson as the default JSONP implementation --- java-client/build.gradle.kts | 31 +++++++-------- .../co/elastic/clients/json/JsonpUtils.java | 38 ++++++++++++++++++- .../clients/json/SimpleJsonpMapper.java | 2 +- .../json/jackson/JacksonJsonProvider.java | 4 +- .../clients/json/jackson/JsonValueParser.java | 16 ++++---- .../clients/json/jsonb/JsonbJsonpMapper.java | 3 +- .../elastic/clients/json/JsonpUtilsTest.java | 6 --- 7 files changed, 62 insertions(+), 38 deletions(-) diff --git a/java-client/build.gradle.kts b/java-client/build.gradle.kts index 84046069f..f8ec8b6d0 100644 --- a/java-client/build.gradle.kts +++ b/java-client/build.gradle.kts @@ -198,15 +198,16 @@ signing { } dependencies { - // Compile and test with the last 7.x version to make sure transition scenarios where - // the Java API client coexists with a 7.x HLRC work fine + // Compile and test with the last 8.x version to make sure transition scenarios where + // the Java API client coexists with a 8.x HLRC work fine val elasticsearchVersion = "8.17.0" - val jacksonVersion = "2.17.0" + val jacksonVersion = "2.18.3" val openTelemetryVersion = "1.29.0" // Apache 2.0 // https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-low.html - api("org.elasticsearch.client", "elasticsearch-rest-client", elasticsearchVersion) + compileOnly("org.elasticsearch.client", "elasticsearch-rest-client", elasticsearchVersion) + testImplementation("org.elasticsearch.client", "elasticsearch-rest-client", elasticsearchVersion) api("org.apache.httpcomponents.client5","httpclient5","5.4") @@ -216,12 +217,12 @@ dependencies { // EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 // https://github.com/eclipse-ee4j/jsonp - api("jakarta.json:jakarta.json-api:2.0.1") + api("jakarta.json:jakarta.json-api:2.1.3") // Needed even if using Jackson to have an implementation of the Jsonp object model // EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 // https://github.com/eclipse-ee4j/parsson - api("org.eclipse.parsson:parsson:1.0.5") + api("org.eclipse.parsson:parsson:1.1.7") // OpenTelemetry API for native instrumentation of the client. // Apache 2.0 @@ -229,25 +230,21 @@ dependencies { implementation("io.opentelemetry", "opentelemetry-api", openTelemetryVersion) // Use it once it's stable (see Instrumentation.java). Limited to tests for now. testImplementation("io.opentelemetry", "opentelemetry-semconv", "$openTelemetryVersion-alpha") + testImplementation("io.opentelemetry", "opentelemetry-sdk", openTelemetryVersion) // EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 // https://github.com/eclipse-ee4j/jsonb-api - compileOnly("jakarta.json.bind", "jakarta.json.bind-api", "2.0.0") - testImplementation("jakarta.json.bind", "jakarta.json.bind-api", "2.0.0") + compileOnly("jakarta.json.bind", "jakarta.json.bind-api", "3.0.1") + testImplementation("jakarta.json.bind", "jakarta.json.bind-api", "3.0.1") // Apache 2.0 // https://github.com/FasterXML/jackson - compileOnly("com.fasterxml.jackson.core", "jackson-core", jacksonVersion) - compileOnly("com.fasterxml.jackson.core", "jackson-databind", jacksonVersion) - testImplementation("com.fasterxml.jackson.core", "jackson-core", jacksonVersion) - testImplementation("com.fasterxml.jackson.core", "jackson-databind", jacksonVersion) + implementation("com.fasterxml.jackson.core", "jackson-core", jacksonVersion) + implementation("com.fasterxml.jackson.core", "jackson-databind", jacksonVersion) // EPL-2.0 OR BSD-3-Clause // https://eclipse-ee4j.github.io/yasson/ - testImplementation("org.eclipse", "yasson", "2.0.4") { - // Exclude Glassfish as we use Parsson (basically Glassfish renamed in the Jakarta namespace). - exclude(group = "org.glassfish", module = "jakarta.json") - } + testImplementation("org.eclipse", "yasson", "3.0.4") // Apache-2.0 testImplementation("commons-io:commons-io:2.17.0") @@ -268,8 +265,6 @@ dependencies { // updating transitive dependency from testcontainers testImplementation("org.apache.commons","commons-compress","1.26.1") - testImplementation("io.opentelemetry", "opentelemetry-sdk", openTelemetryVersion) - // Apache-2.0 // https://github.com/awaitility/awaitility testImplementation("org.awaitility", "awaitility", "4.2.0") diff --git a/java-client/src/main/java/co/elastic/clients/json/JsonpUtils.java b/java-client/src/main/java/co/elastic/clients/json/JsonpUtils.java index 74f888d04..e8a6006f4 100644 --- a/java-client/src/main/java/co/elastic/clients/json/JsonpUtils.java +++ b/java-client/src/main/java/co/elastic/clients/json/JsonpUtils.java @@ -19,6 +19,7 @@ package co.elastic.clients.json; +import co.elastic.clients.json.jackson.JacksonJsonProvider; import co.elastic.clients.util.AllowForbiddenApis; import jakarta.json.JsonException; import jakarta.json.JsonObject; @@ -43,6 +44,7 @@ public class JsonpUtils { private static JsonProvider systemJsonProvider = null; + private static JsonProvider defaultJsonProvider = null; /** * Get a JsonProvider instance. This method first calls the standard `JsonProvider.provider()` that is based on @@ -50,16 +52,48 @@ public class JsonpUtils { * value is cached for subsequent calls. */ public static JsonProvider provider() { - JsonProvider result = systemJsonProvider; + JsonProvider result = defaultJsonProvider; if (result == null) { result = findProvider(); + defaultJsonProvider = result; + } + return result; + } + + /** + * Sets the JsonProvider that will be returned by {@link JsonProvider}. + */ + public static void setProvider(JsonProvider provider) { + defaultJsonProvider = provider; + } + + static JsonProvider findProvider() { + try { + // Default to Jackson + return new JacksonJsonProvider(); + } catch (NoClassDefFoundError e) { + // Ignore + } + return findSystemProvider(); + } + + /** + * Get the system's JsonProvider instance return by {@code ServiceLoader}. First calls the standard + * `JsonProvider.provider()` that is based on the current thread's context classloader, and in case of failure tries to + * find a provider in other classloaders. The value is cached for subsequent calls. + */ + public static JsonProvider systemProvider() { + JsonProvider result = systemJsonProvider; + if (result == null) { + result = findSystemProvider(); systemJsonProvider = result; } return result; } @AllowForbiddenApis("Implementation of the JsonProvider lookup") - static JsonProvider findProvider() { + static JsonProvider findSystemProvider() { + RuntimeException exception; try { return JsonProvider.provider(); diff --git a/java-client/src/main/java/co/elastic/clients/json/SimpleJsonpMapper.java b/java-client/src/main/java/co/elastic/clients/json/SimpleJsonpMapper.java index 67a37428e..4d6ccf874 100644 --- a/java-client/src/main/java/co/elastic/clients/json/SimpleJsonpMapper.java +++ b/java-client/src/main/java/co/elastic/clients/json/SimpleJsonpMapper.java @@ -90,7 +90,7 @@ public boolean ignoreUnknownFields() { @Override public JsonProvider jsonProvider() { - return JsonpUtils.provider(); + return JsonpUtils.systemProvider(); } @Override diff --git a/java-client/src/main/java/co/elastic/clients/json/jackson/JacksonJsonProvider.java b/java-client/src/main/java/co/elastic/clients/json/jackson/JacksonJsonProvider.java index 37590afb6..d42159b14 100644 --- a/java-client/src/main/java/co/elastic/clients/json/jackson/JacksonJsonProvider.java +++ b/java-client/src/main/java/co/elastic/clients/json/jackson/JacksonJsonProvider.java @@ -133,7 +133,7 @@ public JsonParser createParser(InputStream in, Charset charset) { */ @Override public JsonParser createParser(JsonObject obj) { - return JsonpUtils.provider().createParserFactory(null).createParser(obj); + return JsonpUtils.systemProvider().createParserFactory(null).createParser(obj); } /** @@ -141,7 +141,7 @@ public JsonParser createParser(JsonObject obj) { */ @Override public JsonParser createParser(JsonArray array) { - return JsonpUtils.provider().createParserFactory(null).createParser(array); + return JsonpUtils.systemProvider().createParserFactory(null).createParser(array); } /** diff --git a/java-client/src/main/java/co/elastic/clients/json/jackson/JsonValueParser.java b/java-client/src/main/java/co/elastic/clients/json/jackson/JsonValueParser.java index 51cbd2099..6f9572388 100644 --- a/java-client/src/main/java/co/elastic/clients/json/jackson/JsonValueParser.java +++ b/java-client/src/main/java/co/elastic/clients/json/jackson/JsonValueParser.java @@ -40,11 +40,11 @@ * object (e.g. START_OBJECT, VALUE_NUMBER, etc). */ class JsonValueParser { - private final JsonProvider provider = JsonpUtils.provider(); + private final JsonProvider systemProvider = JsonpUtils.systemProvider(); public JsonObject parseObject(JsonParser parser) throws IOException { - JsonObjectBuilder ob = provider.createObjectBuilder(); + JsonObjectBuilder ob = systemProvider.createObjectBuilder(); JsonToken token; while((token = parser.nextToken()) != JsonToken.END_OBJECT) { @@ -59,7 +59,7 @@ public JsonObject parseObject(JsonParser parser) throws IOException { } public JsonArray parseArray(JsonParser parser) throws IOException { - JsonArrayBuilder ab = provider.createArrayBuilder(); + JsonArrayBuilder ab = systemProvider.createArrayBuilder(); while(parser.nextToken() != JsonToken.END_ARRAY) { ab.add(parseValue(parser)); @@ -86,23 +86,23 @@ public JsonValue parseValue(JsonParser parser) throws IOException { return JsonValue.NULL; case VALUE_STRING: - return provider.createValue(parser.getText()); + return systemProvider.createValue(parser.getText()); case VALUE_NUMBER_FLOAT: case VALUE_NUMBER_INT: switch(parser.getNumberType()) { case INT: - return provider.createValue(parser.getIntValue()); + return systemProvider.createValue(parser.getIntValue()); case LONG: - return provider.createValue(parser.getLongValue()); + return systemProvider.createValue(parser.getLongValue()); case FLOAT: case DOUBLE: // Use double also for floats, as JSON-P has no support for float return new DoubleNumber(parser.getDoubleValue()); case BIG_DECIMAL: - return provider.createValue(parser.getDecimalValue()); + return systemProvider.createValue(parser.getDecimalValue()); case BIG_INTEGER: - return provider.createValue(parser.getBigIntegerValue()); + return systemProvider.createValue(parser.getBigIntegerValue()); } default: diff --git a/java-client/src/main/java/co/elastic/clients/json/jsonb/JsonbJsonpMapper.java b/java-client/src/main/java/co/elastic/clients/json/jsonb/JsonbJsonpMapper.java index c936f92f4..6bb15a409 100644 --- a/java-client/src/main/java/co/elastic/clients/json/jsonb/JsonbJsonpMapper.java +++ b/java-client/src/main/java/co/elastic/clients/json/jsonb/JsonbJsonpMapper.java @@ -52,7 +52,8 @@ public JsonbJsonpMapper(JsonProvider jsonProvider, JsonbProvider jsonbProvider) } public JsonbJsonpMapper() { - this(JsonpUtils.provider(), JsonbProvider.provider()); + // Use a native JSON-P/JSON-B implementations. + this(JsonpUtils.systemProvider(), JsonbProvider.provider()); } @Override diff --git a/java-client/src/test/java/co/elastic/clients/json/JsonpUtilsTest.java b/java-client/src/test/java/co/elastic/clients/json/JsonpUtilsTest.java index a7f9b7a75..7247cd2b1 100644 --- a/java-client/src/test/java/co/elastic/clients/json/JsonpUtilsTest.java +++ b/java-client/src/test/java/co/elastic/clients/json/JsonpUtilsTest.java @@ -58,13 +58,7 @@ public Enumeration getResources(String name) { ClassLoader savedLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(emptyLoader); - - assertThrows(JsonException.class, () -> { - assertNotNull(JsonProvider.provider()); - }); - assertNotNull(JsonpUtils.provider()); - } finally { Thread.currentThread().setContextClassLoader(savedLoader); } From 7889f5c980266cfd2f38d96d46068c975e014301 Mon Sep 17 00:00:00 2001 From: Sylvain Wallez Date: Mon, 31 Mar 2025 18:01:46 +0200 Subject: [PATCH 2/6] Jackson is now a require dependency, remove it from the docs --- docs/reference/getting-started.md | 7 ------- docs/reference/installation.md | 7 ------- .../java/co/elastic/clients/testkit/ModelTestCase.java | 2 +- 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/docs/reference/getting-started.md b/docs/reference/getting-started.md index 073a7cd70..e365d854d 100644 --- a/docs/reference/getting-started.md +++ b/docs/reference/getting-started.md @@ -22,7 +22,6 @@ This page guides you through the installation process of the Java client, shows ```groovy dependencies { implementation 'co.elastic.clients:elasticsearch-java:9.0.0-beta1' - implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.0' } ``` @@ -41,12 +40,6 @@ In the `pom.xml` of your project, add the following repository definition and de 9.0.0-beta1 - - com.fasterxml.jackson.core - jackson-databind - 2.17.0 - - ``` diff --git a/docs/reference/installation.md b/docs/reference/installation.md index b174e9395..dacc9767d 100644 --- a/docs/reference/installation.md +++ b/docs/reference/installation.md @@ -18,7 +18,6 @@ Releases are hosted on [Maven Central](https://search.maven.org/search?q=g:co.el ```groovy dependencies { implementation 'co.elastic.clients:elasticsearch-java:9.0.0-beta1' - implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.0' } ``` @@ -37,12 +36,6 @@ In the `pom.xml` of your project, add the following repository definition and de 9.0.0-beta1 - - com.fasterxml.jackson.core - jackson-databind - 2.17.0 - - ``` diff --git a/java-client/src/test/java/co/elastic/clients/testkit/ModelTestCase.java b/java-client/src/test/java/co/elastic/clients/testkit/ModelTestCase.java index 7d6cdbbb2..f935c9d82 100644 --- a/java-client/src/test/java/co/elastic/clients/testkit/ModelTestCase.java +++ b/java-client/src/test/java/co/elastic/clients/testkit/ModelTestCase.java @@ -50,7 +50,7 @@ protected enum JsonImpl { Jsonb, Jackson, Simple }; protected final JsonpMapper mapper; private static JsonImpl chooseJsonImpl(EnumSet jsonImplCandidates, int rand) { - // Converting an EnumSet to an array always uses the same order. + // Converting an EnumSet an array always uses the same order. return jsonImplCandidates.toArray(new JsonImpl[jsonImplCandidates.size()])[rand % jsonImplCandidates.size()]; } From fd5ecaf51d1ab1f669e39e67738ac14a8aed6888 Mon Sep 17 00:00:00 2001 From: Sylvain Wallez Date: Mon, 31 Mar 2025 20:00:21 +0200 Subject: [PATCH 3/6] checkstyle --- .../src/test/java/co/elastic/clients/json/JsonpUtilsTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/java-client/src/test/java/co/elastic/clients/json/JsonpUtilsTest.java b/java-client/src/test/java/co/elastic/clients/json/JsonpUtilsTest.java index 7247cd2b1..24e13934d 100644 --- a/java-client/src/test/java/co/elastic/clients/json/JsonpUtilsTest.java +++ b/java-client/src/test/java/co/elastic/clients/json/JsonpUtilsTest.java @@ -26,7 +26,6 @@ import co.elastic.clients.elasticsearch.security.RoleTemplateScript; import co.elastic.clients.elasticsearch.security.UserIndicesPrivileges; import co.elastic.clients.util.AllowForbiddenApis; -import jakarta.json.JsonException; import jakarta.json.spi.JsonProvider; import jakarta.json.stream.JsonGenerator; import jakarta.json.stream.JsonParser; From e25fa2b0480596e9e0402129197b9758a9cec93e Mon Sep 17 00:00:00 2001 From: Sylvain Wallez Date: Tue, 1 Apr 2025 08:53:30 +0200 Subject: [PATCH 4/6] More explicit class test, add tests --- .../main/java/co/elastic/clients/json/JsonpUtils.java | 6 +++--- .../java/co/elastic/clients/json/JsonpUtilsTest.java | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/java-client/src/main/java/co/elastic/clients/json/JsonpUtils.java b/java-client/src/main/java/co/elastic/clients/json/JsonpUtils.java index e8a6006f4..bb628a5c7 100644 --- a/java-client/src/main/java/co/elastic/clients/json/JsonpUtils.java +++ b/java-client/src/main/java/co/elastic/clients/json/JsonpUtils.java @@ -70,11 +70,11 @@ public static void setProvider(JsonProvider provider) { static JsonProvider findProvider() { try { // Default to Jackson + Class.forName("com.fasterxml.jackson.databind.ObjectMapper"); return new JacksonJsonProvider(); - } catch (NoClassDefFoundError e) { - // Ignore + } catch (ClassNotFoundException e) { + return findSystemProvider(); } - return findSystemProvider(); } /** diff --git a/java-client/src/test/java/co/elastic/clients/json/JsonpUtilsTest.java b/java-client/src/test/java/co/elastic/clients/json/JsonpUtilsTest.java index 24e13934d..0d7187394 100644 --- a/java-client/src/test/java/co/elastic/clients/json/JsonpUtilsTest.java +++ b/java-client/src/test/java/co/elastic/clients/json/JsonpUtilsTest.java @@ -20,6 +20,7 @@ package co.elastic.clients.json; import co.elastic.clients.elasticsearch.core.search.Hit; +import co.elastic.clients.json.jackson.JacksonJsonProvider; import co.elastic.clients.testkit.ModelTestCase; import co.elastic.clients.elasticsearch.security.IndexPrivilege; import co.elastic.clients.elasticsearch.security.IndicesPrivileges; @@ -84,6 +85,15 @@ public void testObjectToString() { assertEquals("Hit: {\"_index\":\"idx\",\"_id\":\"id1\",\"_source\":\"Some user data\"}", hit.toString()); } + @Test + public void testDefaultProvider() { + // Provider defaults to Jackson + assertTrue(JsonpUtils.provider() instanceof JacksonJsonProvider); + + // System provider uses service lookup + assertFalse(JsonpUtils.systemProvider() instanceof JacksonJsonProvider); + } + private static class SomeUserData { @Override public String toString() { From 4f424daae91bede91573fa990d9b51bb333efdd8 Mon Sep 17 00:00:00 2001 From: Sylvain Wallez Date: Tue, 1 Apr 2025 14:05:33 +0200 Subject: [PATCH 5/6] Add Jackson dependency info --- java-client/build.gradle.kts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/java-client/build.gradle.kts b/java-client/build.gradle.kts index f8ec8b6d0..5420cbdeb 100644 --- a/java-client/build.gradle.kts +++ b/java-client/build.gradle.kts @@ -319,10 +319,15 @@ class SpdxReporter(val dest: File) : ReportRenderer { val depName = dep.group + ":" + dep.name val info = LicenseDataCollector.multiModuleLicenseInfo(dep) - val depUrl = if (depName.startsWith("org.apache.httpcomponents")) { - "https://hc.apache.org/" - } else { - info.moduleUrls.first() + val depUrl = when(dep.group) { + "org.apache.httpcomponents.client5" -> "https://hc.apache.org/" + "org.apache.httpcomponents.core5" -> "https://hc.apache.org/" + "com.fasterxml.jackson" -> "https://github.com/FasterXML/jackson" + else -> if (info.moduleUrls.isEmpty()) { + throw RuntimeException("No URL found for module '$depName'") + } else { + info.moduleUrls.first() + } } val licenseIds = info.licenses.mapNotNull { license -> From 919dbe5c67b409ae28a4c4707e84f84b36e4acd5 Mon Sep 17 00:00:00 2001 From: Sylvain Wallez Date: Tue, 1 Apr 2025 14:43:14 +0200 Subject: [PATCH 6/6] LLRC is no more a require dependency --- java-client/build.gradle.kts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/java-client/build.gradle.kts b/java-client/build.gradle.kts index 5420cbdeb..114cbd19e 100644 --- a/java-client/build.gradle.kts +++ b/java-client/build.gradle.kts @@ -160,6 +160,9 @@ publishing { } withXml { + // Note: org.elasticsearch.client is now an optional dependency, so the below is no more useful. + // It's kept in case it ever comes back as a required dependency. + // Set the version of dependencies of the org.elasticsearch.client group to the one that we are building. // Since the unified release process releases everything at once, this ensures all published artifacts depend // on the exact same version. This assumes of course that the binary API and the behavior of these dependencies @@ -169,21 +172,14 @@ publishing { .compile("/project/dependencies/dependency[groupId/text() = 'org.elasticsearch.client']") val versionSelector = xPathFactory.newXPath().compile("version") - var foundVersion = false; - val deps = depSelector.evaluate(asElement().ownerDocument, javax.xml.xpath.XPathConstants.NODESET) as org.w3c.dom.NodeList for (i in 0 until deps.length) { val dep = deps.item(i) val version = versionSelector.evaluate(dep, javax.xml.xpath.XPathConstants.NODE) as org.w3c.dom.Element - foundVersion = true; version.textContent = project.version.toString() } - - if (!foundVersion) { - throw GradleException("Could not find a 'org.elasticsearch.client' to update dependency version in the POM.") - } } } }