From 9b21d93a8c3e17b8ee08ae1b559773d2b5f4e974 Mon Sep 17 00:00:00 2001 From: Peter-Josef Meisch Date: Thu, 1 Jun 2023 12:22:58 +0200 Subject: [PATCH] Make JsonpMapper a customizable bean. Closes #2566 --- .../reference/elasticsearch-clients.adoc | 15 +++++- .../client/elc/ElasticsearchClients.java | 52 +++++++++++++------ .../elc/ElasticsearchConfiguration.java | 22 ++++++-- .../ReactiveElasticsearchConfiguration.java | 20 +++++-- .../elasticsearch/client/elc/DevTests.java | 6 ++- 5 files changed, 91 insertions(+), 24 deletions(-) diff --git a/src/main/asciidoc/reference/elasticsearch-clients.adoc b/src/main/asciidoc/reference/elasticsearch-clients.adoc index 0fec5493aa..dfc3bb1551 100644 --- a/src/main/asciidoc/reference/elasticsearch-clients.adoc +++ b/src/main/asciidoc/reference/elasticsearch-clients.adoc @@ -32,12 +32,15 @@ public class MyClientConfig extends ElasticsearchConfiguration { <.> for a detailed description of the builder methods see <> ==== +The `ElasticsearchConfiguration` class allows further configuration by overriding for example the `jsonpMapper()` or `transportOptions()` methods. + + The following beans can then be injected in other Spring components: ==== [source,java] ---- -@Autowired +import org.springframework.beans.factory.annotation.Autowired;@Autowired ElasticsearchOperations operations; <.> @Autowired @@ -45,11 +48,15 @@ ElasticsearchClient elasticsearchClient; <.> @Autowired RestClient restClient; <.> + +@Autowired +JsonpMapper jsonpMapper; <.> ---- <.> an implementation of `ElasticsearchOperations` <.> the `co.elastic.clients.elasticsearch.ElasticsearchClient` that is used. <.> the low level `RestClient` from the Elasticsearch libraries +<.> the `JsonpMapper` user by the Elasticsearch `Transport` ==== Basically one should just use the `ElasticsearchOperations` to interact with the Elasticsearch cluster. @@ -79,6 +86,8 @@ public class MyClientConfig extends ReactiveElasticsearchConfiguration { <.> for a detailed description of the builder methods see <> ==== +The `ReactiveElasticsearchConfiguration` class allows further configuration by overriding for example the `jsonpMapper()` or `transportOptions()` methods. + The following beans can then be injected in other Spring components: ==== @@ -92,6 +101,9 @@ ReactiveElasticsearchClient elasticsearchClient; <.> @Autowired RestClient restClient; <.> + +@Autowired +JsonpMapper jsonpMapper; <.> ---- the following can be injected: @@ -100,6 +112,7 @@ the following can be injected: <.> the `org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient` that is used. This is a reactive implementation based on the Elasticsearch client implementation. <.> the low level `RestClient` from the Elasticsearch libraries +<.> the `JsonpMapper` user by the Elasticsearch `Transport` ==== Basically one should just use the `ReactiveElasticsearchOperations` to interact with the Elasticsearch cluster. diff --git a/src/main/java/org/springframework/data/elasticsearch/client/elc/ElasticsearchClients.java b/src/main/java/org/springframework/data/elasticsearch/client/elc/ElasticsearchClients.java index 0a52bffdf0..a9d126303d 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/elc/ElasticsearchClients.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/elc/ElasticsearchClients.java @@ -16,6 +16,7 @@ package org.springframework.data.elasticsearch.client.elc; import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.json.JsonpMapper; import co.elastic.clients.json.jackson.JacksonJsonpMapper; import co.elastic.clients.transport.ElasticsearchTransport; import co.elastic.clients.transport.TransportOptions; @@ -24,8 +25,6 @@ import co.elastic.clients.transport.rest_client.RestClientOptions; import co.elastic.clients.transport.rest_client.RestClientTransport; -import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.net.InetSocketAddress; import java.time.Duration; import java.util.Arrays; @@ -60,11 +59,12 @@ public final class ElasticsearchClients { /** * Name of whose value can be used to correlate log messages for this request. */ - private static final String LOG_ID_ATTRIBUTE = ElasticsearchClients.class.getName() + ".LOG_ID"; private static final String X_SPRING_DATA_ELASTICSEARCH_CLIENT = "X-SpringDataElasticsearch-Client"; private static final String IMPERATIVE_CLIENT = "imperative"; private static final String REACTIVE_CLIENT = "reactive"; + private static final JsonpMapper DEFAULT_JSONP_MAPPER = new JacksonJsonpMapper(); + /** * Creates a new {@link ReactiveElasticsearchClient} * @@ -75,7 +75,7 @@ public static ReactiveElasticsearchClient createReactive(ClientConfiguration cli Assert.notNull(clientConfiguration, "clientConfiguration must not be null"); - return createReactive(getRestClient(clientConfiguration), null); + return createReactive(getRestClient(clientConfiguration), null, DEFAULT_JSONP_MAPPER); } /** @@ -90,7 +90,24 @@ public static ReactiveElasticsearchClient createReactive(ClientConfiguration cli Assert.notNull(clientConfiguration, "ClientConfiguration must not be null!"); - return createReactive(getRestClient(clientConfiguration), transportOptions); + return createReactive(getRestClient(clientConfiguration), transportOptions, DEFAULT_JSONP_MAPPER); + } + + /** + * Creates a new {@link ReactiveElasticsearchClient} + * + * @param clientConfiguration configuration options, must not be {@literal null}. + * @param transportOptions options to be added to each request. + * @param jsonpMapper the JsonpMapper to use + * @return the {@link ReactiveElasticsearchClient} + */ + public static ReactiveElasticsearchClient createReactive(ClientConfiguration clientConfiguration, + @Nullable TransportOptions transportOptions, JsonpMapper jsonpMapper) { + + Assert.notNull(clientConfiguration, "ClientConfiguration must not be null!"); + Assert.notNull(jsonpMapper, "jsonpMapper must not be null"); + + return createReactive(getRestClient(clientConfiguration), transportOptions, jsonpMapper); } /** @@ -100,7 +117,7 @@ public static ReactiveElasticsearchClient createReactive(ClientConfiguration cli * @return the {@link ReactiveElasticsearchClient} */ public static ReactiveElasticsearchClient createReactive(RestClient restClient) { - return createReactive(restClient, null); + return createReactive(restClient, null, DEFAULT_JSONP_MAPPER); } /** @@ -111,8 +128,9 @@ public static ReactiveElasticsearchClient createReactive(RestClient restClient) * @return the {@link ReactiveElasticsearchClient} */ public static ReactiveElasticsearchClient createReactive(RestClient restClient, - @Nullable TransportOptions transportOptions) { - return new ReactiveElasticsearchClient(getElasticsearchTransport(restClient, REACTIVE_CLIENT, transportOptions)); + @Nullable TransportOptions transportOptions, JsonpMapper jsonpMapper) { + return new ReactiveElasticsearchClient( + getElasticsearchTransport(restClient, REACTIVE_CLIENT, transportOptions, jsonpMapper)); } /** @@ -122,7 +140,7 @@ public static ReactiveElasticsearchClient createReactive(RestClient restClient, * @return the {@link ElasticsearchClient} */ public static ElasticsearchClient createImperative(ClientConfiguration clientConfiguration) { - return createImperative(getRestClient(clientConfiguration), null); + return createImperative(getRestClient(clientConfiguration), null, DEFAULT_JSONP_MAPPER); } /** @@ -134,7 +152,7 @@ public static ElasticsearchClient createImperative(ClientConfiguration clientCon */ public static ElasticsearchClient createImperative(ClientConfiguration clientConfiguration, TransportOptions transportOptions) { - return createImperative(getRestClient(clientConfiguration), transportOptions); + return createImperative(getRestClient(clientConfiguration), transportOptions, DEFAULT_JSONP_MAPPER); } /** @@ -144,7 +162,7 @@ public static ElasticsearchClient createImperative(ClientConfiguration clientCon * @return the {@link ElasticsearchClient} */ public static ElasticsearchClient createImperative(RestClient restClient) { - return createImperative(restClient, null); + return createImperative(restClient, null, DEFAULT_JSONP_MAPPER); } /** @@ -152,14 +170,16 @@ public static ElasticsearchClient createImperative(RestClient restClient) { * * @param restClient the RestClient to use * @param transportOptions options to be added to each request. + * @param jsonpMapper the mapper for the transport to use * @return the {@link ElasticsearchClient} */ - public static ElasticsearchClient createImperative(RestClient restClient, - @Nullable TransportOptions transportOptions) { + public static ElasticsearchClient createImperative(RestClient restClient, @Nullable TransportOptions transportOptions, + JsonpMapper jsonpMapper) { Assert.notNull(restClient, "restClient must not be null"); - ElasticsearchTransport transport = getElasticsearchTransport(restClient, IMPERATIVE_CLIENT, transportOptions); + ElasticsearchTransport transport = getElasticsearchTransport(restClient, IMPERATIVE_CLIENT, transportOptions, + jsonpMapper); return new AutoCloseableElasticsearchClient(transport); } @@ -236,7 +256,7 @@ private static RestClientBuilder getRestClientBuilder(ClientConfiguration client } private static ElasticsearchTransport getElasticsearchTransport(RestClient restClient, String clientType, - @Nullable TransportOptions transportOptions) { + @Nullable TransportOptions transportOptions, JsonpMapper jsonpMapper) { TransportOptions.Builder transportOptionsBuilder = transportOptions != null ? transportOptions.toBuilder() : new RestClientOptions(RequestOptions.DEFAULT).toBuilder(); @@ -260,7 +280,7 @@ private static ElasticsearchTransport getElasticsearchTransport(RestClient restC TransportOptions transportOptionsWithHeader = transportOptionsBuilder .addHeader(X_SPRING_DATA_ELASTICSEARCH_CLIENT, clientType).build(); - return new RestClientTransport(restClient, new JacksonJsonpMapper(), transportOptionsWithHeader); + return new RestClientTransport(restClient, jsonpMapper, transportOptionsWithHeader); } private static List formattedHosts(List hosts, boolean useSsl) { diff --git a/src/main/java/org/springframework/data/elasticsearch/client/elc/ElasticsearchConfiguration.java b/src/main/java/org/springframework/data/elasticsearch/client/elc/ElasticsearchConfiguration.java index 2f06eb2a61..51bb1bd502 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/elc/ElasticsearchConfiguration.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/elc/ElasticsearchConfiguration.java @@ -16,11 +16,14 @@ package org.springframework.data.elasticsearch.client.elc; import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.json.JsonpMapper; +import co.elastic.clients.json.jackson.JacksonJsonpMapper; import co.elastic.clients.transport.TransportOptions; import co.elastic.clients.transport.rest_client.RestClientOptions; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestClient; +import org.jetbrains.annotations.NotNull; import org.springframework.context.annotation.Bean; import org.springframework.data.elasticsearch.client.ClientConfiguration; import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport; @@ -42,7 +45,7 @@ public abstract class ElasticsearchConfiguration extends ElasticsearchConfigurat * * @return configuration, must not be {@literal null} */ - @Bean(name="elasticsearchClientConfiguration") + @Bean(name = "elasticsearchClientConfiguration") public abstract ClientConfiguration clientConfiguration(); /** @@ -63,14 +66,15 @@ public RestClient elasticsearchRestClient(ClientConfiguration clientConfiguratio * Provides the {@link ElasticsearchClient} to be used. * * @param restClient the low level RestClient to use + * @param jsonpMapper the JsonpMapper to use * @return ElasticsearchClient instance */ @Bean - public ElasticsearchClient elasticsearchClient(RestClient restClient) { + public ElasticsearchClient elasticsearchClient(RestClient restClient, JsonpMapper jsonpMapper) { Assert.notNull(restClient, "restClient must not be null"); - return ElasticsearchClients.createImperative(restClient, transportOptions()); + return ElasticsearchClients.createImperative(restClient, transportOptions(), jsonpMapper); } /** @@ -89,6 +93,18 @@ public ElasticsearchOperations elasticsearchOperations(ElasticsearchConverter el return template; } + /** + * Provides the JsonpMapper bean that is used in the {@link #elasticsearchClient(RestClient, JsonpMapper)} method. + * This can be adapted by overriding tge {@link #getJsonpMapper()} method. + * + * @return the {@link JsonpMapper} to use + * @since 5.2 + */ + @Bean + public JsonpMapper jsonpMapper() { + return new JacksonJsonpMapper(); + } + /** * @return the options that should be added to every request. Must not be {@literal null} */ diff --git a/src/main/java/org/springframework/data/elasticsearch/client/elc/ReactiveElasticsearchConfiguration.java b/src/main/java/org/springframework/data/elasticsearch/client/elc/ReactiveElasticsearchConfiguration.java index d1feed24b9..7c7b28303a 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/elc/ReactiveElasticsearchConfiguration.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/elc/ReactiveElasticsearchConfiguration.java @@ -15,6 +15,8 @@ */ package org.springframework.data.elasticsearch.client.elc; +import co.elastic.clients.json.JsonpMapper; +import co.elastic.clients.json.jackson.JacksonJsonpMapper; import co.elastic.clients.transport.TransportOptions; import co.elastic.clients.transport.rest_client.RestClientOptions; @@ -41,7 +43,7 @@ public abstract class ReactiveElasticsearchConfiguration extends ElasticsearchCo * * @return configuration, must not be {@literal null} */ - @Bean(name="elasticsearchClientConfiguration") + @Bean(name = "elasticsearchClientConfiguration") public abstract ClientConfiguration clientConfiguration(); /** @@ -65,11 +67,11 @@ public RestClient elasticsearchRestClient(ClientConfiguration clientConfiguratio * @return ReactiveElasticsearchClient instance. */ @Bean - public ReactiveElasticsearchClient reactiveElasticsearchClient(RestClient restClient) { + public ReactiveElasticsearchClient reactiveElasticsearchClient(RestClient restClient, JsonpMapper jsonpMapper) { Assert.notNull(restClient, "restClient must not be null"); - return ElasticsearchClients.createReactive(restClient, transportOptions()); + return ElasticsearchClients.createReactive(restClient, transportOptions(), jsonpMapper); } /** @@ -88,6 +90,18 @@ public ReactiveElasticsearchOperations reactiveElasticsearchOperations(Elasticse return template; } + /** + * Provides the JsonpMapper that is used in the {@link #reactiveElasticsearchClient(RestClient, JsonpMapper)} method + * and exposes it as a bean. + * + * @return the {@link JsonpMapper} to use + * @since 5.2 + */ + @Bean + public JsonpMapper jsonpMapper() { + return new JacksonJsonpMapper(); + } + /** * @return the options that should be added to every request. Must not be {@literal null} */ diff --git a/src/test/java/org/springframework/data/elasticsearch/client/elc/DevTests.java b/src/test/java/org/springframework/data/elasticsearch/client/elc/DevTests.java index 49a7d8c04f..a6202849e9 100644 --- a/src/test/java/org/springframework/data/elasticsearch/client/elc/DevTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/client/elc/DevTests.java @@ -30,6 +30,8 @@ import co.elastic.clients.elasticsearch.indices.GetIndicesSettingsRequest; import co.elastic.clients.elasticsearch.indices.GetIndicesSettingsResponse; import co.elastic.clients.elasticsearch.indices.IndexSettings; +import co.elastic.clients.json.JsonpMapper; +import co.elastic.clients.json.jackson.JacksonJsonpMapper; import co.elastic.clients.transport.ElasticsearchTransport; import co.elastic.clients.transport.TransportOptions; import co.elastic.clients.transport.rest_client.RestClientOptions; @@ -75,10 +77,12 @@ public class DevTests { private final TransportOptions transportOptions = new RestClientOptions(RequestOptions.DEFAULT).toBuilder() .addHeader("X-SpringDataElasticsearch-AlwaysThere", "true").setParameter("pretty", "true").build(); + private final JsonpMapper jsonpMapper = new JacksonJsonpMapper(); + private final ReactiveElasticsearchClient reactiveElasticsearchClient = ElasticsearchClients .createReactive(clientConfiguration(), transportOptions); private final ElasticsearchClient imperativeElasticsearchClient = ElasticsearchClients - .createImperative(ElasticsearchClients.getRestClient(clientConfiguration()), transportOptions); + .createImperative(ElasticsearchClients.getRestClient(clientConfiguration()), transportOptions, jsonpMapper); @Test void someTest() throws IOException {