diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle b/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle index 97c1fcafa1..552e51be62 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle @@ -103,7 +103,6 @@ dependencies { optional("org.eclipse.jetty:jetty-server") { exclude group: "org.eclipse.jetty.toolchain", module: "jetty-jakarta-servlet-api" } - optional("org.elasticsearch:elasticsearch") optional("org.elasticsearch.client:elasticsearch-rest-client") { exclude group: "commons-logging", module: "commons-logging" } @@ -168,9 +167,6 @@ dependencies { testImplementation("org.eclipse.jetty:jetty-webapp") { exclude group: "org.eclipse.jetty.toolchain", module: "jetty-jakarta-servlet-api" } - testImplementation("org.elasticsearch.client:elasticsearch-rest-high-level-client") { - exclude(group: "commons-logging", module: "commons-logging") - } testImplementation("org.hamcrest:hamcrest") testImplementation("org.hsqldb:hsqldb") testImplementation("org.junit.jupiter:junit-jupiter") diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticSearchReactiveHealthContributorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticsearchReactiveHealthContributorAutoConfiguration.java similarity index 94% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticSearchReactiveHealthContributorAutoConfiguration.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticsearchReactiveHealthContributorAutoConfiguration.java index e9ff1c9e5e..ab3c640b7b 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticSearchReactiveHealthContributorAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticsearchReactiveHealthContributorAutoConfiguration.java @@ -31,7 +31,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.elasticsearch.ReactiveElasticsearchClientAutoConfiguration; import org.springframework.context.annotation.Bean; -import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient; +import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient; /** * {@link EnableAutoConfiguration Auto-configuration} for @@ -45,7 +45,7 @@ import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearch @ConditionalOnClass({ ReactiveElasticsearchClient.class, Flux.class }) @ConditionalOnBean(ReactiveElasticsearchClient.class) @ConditionalOnEnabledHealthIndicator("elasticsearch") -public class ElasticSearchReactiveHealthContributorAutoConfiguration extends +public class ElasticsearchReactiveHealthContributorAutoConfiguration extends CompositeReactiveHealthContributorConfiguration { @Bean diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticSearchRestHealthContributorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticsearchRestHealthContributorAutoConfiguration.java similarity index 97% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticSearchRestHealthContributorAutoConfiguration.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticsearchRestHealthContributorAutoConfiguration.java index adc2194cd7..c76c1e5752 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticSearchRestHealthContributorAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticsearchRestHealthContributorAutoConfiguration.java @@ -43,7 +43,7 @@ import org.springframework.context.annotation.Bean; @ConditionalOnClass(RestClient.class) @ConditionalOnBean(RestClient.class) @ConditionalOnEnabledHealthIndicator("elasticsearch") -public class ElasticSearchRestHealthContributorAutoConfiguration +public class ElasticsearchRestHealthContributorAutoConfiguration extends CompositeHealthContributorConfiguration { @Bean diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 6fdfc58211..c6f4f89c4f 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -14,8 +14,8 @@ org.springframework.boot.actuate.autoconfigure.context.properties.ConfigurationP org.springframework.boot.actuate.autoconfigure.context.ShutdownEndpointAutoConfiguration org.springframework.boot.actuate.autoconfigure.couchbase.CouchbaseHealthContributorAutoConfiguration org.springframework.boot.actuate.autoconfigure.couchbase.CouchbaseReactiveHealthContributorAutoConfiguration -org.springframework.boot.actuate.autoconfigure.elasticsearch.ElasticSearchReactiveHealthContributorAutoConfiguration -org.springframework.boot.actuate.autoconfigure.elasticsearch.ElasticSearchRestHealthContributorAutoConfiguration +org.springframework.boot.actuate.autoconfigure.elasticsearch.ElasticsearchReactiveHealthContributorAutoConfiguration +org.springframework.boot.actuate.autoconfigure.elasticsearch.ElasticsearchRestHealthContributorAutoConfiguration org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointAutoConfiguration org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticsearchReactiveHealthContributorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticsearchReactiveHealthContributorAutoConfigurationTests.java index 3b9646ba62..fbe84d42af 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticsearchReactiveHealthContributorAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticsearchReactiveHealthContributorAutoConfigurationTests.java @@ -30,7 +30,7 @@ import org.springframework.boot.test.context.runner.ApplicationContextRunner; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link ElasticSearchReactiveHealthContributorAutoConfiguration}. + * Tests for {@link ElasticsearchReactiveHealthContributorAutoConfiguration}. * * @author Aleksander Lech */ @@ -39,7 +39,7 @@ class ElasticsearchReactiveHealthContributorAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(ElasticsearchDataAutoConfiguration.class, ReactiveElasticsearchClientAutoConfiguration.class, ElasticsearchRestClientAutoConfiguration.class, - ElasticSearchReactiveHealthContributorAutoConfiguration.class, + ElasticsearchReactiveHealthContributorAutoConfiguration.class, HealthContributorAutoConfiguration.class)); @Test @@ -51,7 +51,7 @@ class ElasticsearchReactiveHealthContributorAutoConfigurationTests { @Test void runWithRegularIndicatorShouldOnlyCreateReactiveIndicator() { this.contextRunner - .withConfiguration(AutoConfigurations.of(ElasticSearchRestHealthContributorAutoConfiguration.class)) + .withConfiguration(AutoConfigurations.of(ElasticsearchRestHealthContributorAutoConfiguration.class)) .run((context) -> assertThat(context).hasSingleBean(ElasticsearchReactiveHealthIndicator.class) .hasBean("elasticsearchHealthContributor") .doesNotHaveBean(ElasticsearchRestClientHealthIndicator.class)); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticSearchRestHealthContributorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticsearchRestHealthContributorAutoConfigurationTests.java similarity index 68% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticSearchRestHealthContributorAutoConfigurationTests.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticsearchRestHealthContributorAutoConfigurationTests.java index 5c1f4d9aa4..a1294a97f7 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticSearchRestHealthContributorAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticsearchRestHealthContributorAutoConfigurationTests.java @@ -32,16 +32,16 @@ import org.springframework.context.annotation.Configuration; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link ElasticSearchRestHealthContributorAutoConfiguration}. + * Tests for {@link ElasticsearchRestHealthContributorAutoConfiguration}. * * @author Filip Hrisafov * @author Andy Wilkinson */ -class ElasticSearchRestHealthContributorAutoConfigurationTests { +class ElasticsearchRestHealthContributorAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(ElasticsearchRestClientAutoConfiguration.class, - ElasticSearchRestHealthContributorAutoConfiguration.class, + ElasticsearchRestHealthContributorAutoConfiguration.class, HealthContributorAutoConfiguration.class)); @Test @@ -51,29 +51,19 @@ class ElasticSearchRestHealthContributorAutoConfigurationTests { } @Test - @SuppressWarnings("deprecation") - void runWithoutRestHighLevelClientAndWithoutRestClientShouldNotCreateIndicator() { - this.contextRunner - .withClassLoader( - new FilteredClassLoader(org.elasticsearch.client.RestHighLevelClient.class, RestClient.class)) + void runWithoutRestClientShouldNotCreateIndicator() { + this.contextRunner.withClassLoader(new FilteredClassLoader(RestClient.class)) .run((context) -> assertThat(context).doesNotHaveBean(ElasticsearchRestClientHealthIndicator.class) .doesNotHaveBean("elasticsearchHealthContributor")); } @Test - void runWithoutRestHighLevelClientAndWithRestClientShouldCreateIndicator() { + void runWithRestClientShouldCreateIndicator() { this.contextRunner.withUserConfiguration(CustomRestClientConfiguration.class) .run((context) -> assertThat(context).hasSingleBean(ElasticsearchRestClientHealthIndicator.class) .hasBean("elasticsearchHealthContributor")); } - @Test - void runWithRestHighLevelClientAndWithRestClientShouldCreateIndicator() { - this.contextRunner.withUserConfiguration(CustomRestHighClientConfiguration.class) - .run((context) -> assertThat(context).hasSingleBean(ElasticsearchRestClientHealthIndicator.class) - .hasBean("elasticsearchHealthContributor")); - } - @Test void runWhenDisabledShouldNotCreateIndicator() { this.contextRunner.withPropertyValues("management.health.elasticsearch.enabled:false") @@ -91,20 +81,4 @@ class ElasticSearchRestHealthContributorAutoConfigurationTests { } - @Configuration(proxyBeanMethods = false) - @SuppressWarnings("deprecation") - static class CustomRestHighClientConfiguration { - - @Bean - org.elasticsearch.client.RestHighLevelClient customRestHighClient(RestClientBuilder builder) { - return new org.elasticsearch.client.RestHighLevelClient(builder); - } - - @Bean - RestClient customClient(org.elasticsearch.client.RestHighLevelClient restHighLevelClient) { - return restHighLevelClient.getLowLevelClient(); - } - - } - } diff --git a/spring-boot-project/spring-boot-actuator/build.gradle b/spring-boot-project/spring-boot-actuator/build.gradle index def8ded437..050afcf1e4 100644 --- a/spring-boot-project/spring-boot-actuator/build.gradle +++ b/spring-boot-project/spring-boot-actuator/build.gradle @@ -43,13 +43,9 @@ dependencies { optional("org.eclipse.jetty:jetty-server") { exclude(group: "org.eclipse.jetty.toolchain", module: "jetty-jakarta-servlet-api") } - optional("org.elasticsearch:elasticsearch") optional("org.elasticsearch.client:elasticsearch-rest-client") { exclude(group: "commons-logging", module: "commons-logging") } - optional("org.elasticsearch.client:elasticsearch-rest-high-level-client") { - exclude(group: "commons-logging", module: "commons-logging") - } optional("org.flywaydb:flyway-core") optional("org.hibernate.validator:hibernate-validator") optional("org.influxdb:influxdb-java") diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchReactiveHealthIndicator.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchReactiveHealthIndicator.java index a64bce3877..93a872777d 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchReactiveHealthIndicator.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchReactiveHealthIndicator.java @@ -16,20 +16,15 @@ package org.springframework.boot.actuate.elasticsearch; -import java.util.Map; - +import co.elastic.clients.elasticsearch._types.HealthStatus; +import co.elastic.clients.elasticsearch.cluster.HealthResponse; import reactor.core.publisher.Mono; import org.springframework.boot.actuate.health.AbstractReactiveHealthIndicator; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.actuate.health.Status; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient; -import org.springframework.http.HttpStatus; -import org.springframework.http.HttpStatusCode; -import org.springframework.web.reactive.function.client.ClientResponse; -import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient; /** * {@link HealthIndicator} for an Elasticsearch cluster using a @@ -42,11 +37,6 @@ import org.springframework.web.reactive.function.client.WebClient; */ public class ElasticsearchReactiveHealthIndicator extends AbstractReactiveHealthIndicator { - private static final ParameterizedTypeReference> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() { - }; - - private static final String RED_STATUS = "red"; - private final ReactiveElasticsearchClient client; public ElasticsearchReactiveHealthIndicator(ReactiveElasticsearchClient client) { @@ -56,32 +46,33 @@ public class ElasticsearchReactiveHealthIndicator extends AbstractReactiveHealth @Override protected Mono doHealthCheck(Health.Builder builder) { - return this.client.execute((webClient) -> getHealth(builder, webClient)); + return this.client.cluster().health((b) -> b).map((response) -> processResponse(builder, response)); } - private Mono getHealth(Health.Builder builder, WebClient webClient) { - return webClient.get().uri("/_cluster/health/").exchangeToMono((response) -> doHealthCheck(builder, response)); - } - - private Mono doHealthCheck(Health.Builder builder, ClientResponse response) { - HttpStatusCode httpStatusCode = response.statusCode(); - HttpStatus httpStatus = HttpStatus.resolve(httpStatusCode.value()); - if (httpStatusCode.is2xxSuccessful()) { - return response.bodyToMono(STRING_OBJECT_MAP).map((body) -> getHealth(builder, body)); + private Health processResponse(Health.Builder builder, HealthResponse response) { + if (!response.timedOut()) { + HealthStatus status = response.status(); + builder.status((HealthStatus.Red == status) ? Status.OUT_OF_SERVICE : Status.UP); + builder.withDetail("cluster_name", response.clusterName()); + builder.withDetail("status", response.status().jsonValue()); + builder.withDetail("timed_out", response.timedOut()); + builder.withDetail("number_of_nodes", response.numberOfNodes()); + builder.withDetail("number_of_data_nodes", response.numberOfDataNodes()); + builder.withDetail("active_primary_shards", response.activePrimaryShards()); + builder.withDetail("active_shards", response.activeShards()); + builder.withDetail("relocating_shards", response.relocatingShards()); + builder.withDetail("initializing_shards", response.initializingShards()); + builder.withDetail("unassigned_shards", response.unassignedShards()); + builder.withDetail("delayed_unassigned_shards", response.delayedUnassignedShards()); + builder.withDetail("number_of_pending_tasks", response.numberOfPendingTasks()); + builder.withDetail("number_of_in_flight_fetch", response.numberOfInFlightFetch()); + builder.withDetail("task_max_waiting_in_queue_millis", + response.taskMaxWaitingInQueueMillis().toEpochMilli()); + builder.withDetail("active_shards_percent_as_number", + Double.parseDouble(response.activeShardsPercentAsNumber())); + return builder.build(); } - builder.down(); - builder.withDetail("statusCode", httpStatusCode.value()); - if (httpStatus != null) { - builder.withDetail("reasonPhrase", httpStatus.getReasonPhrase()); - } - return response.releaseBody().thenReturn(builder.build()); - } - - private Health getHealth(Health.Builder builder, Map body) { - String status = (String) body.get("status"); - builder.status(RED_STATUS.equals(status) ? Status.OUT_OF_SERVICE : Status.UP); - builder.withDetails(body); - return builder.build(); + return builder.down().build(); } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchReactiveHealthIndicatorTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchReactiveHealthIndicatorTests.java index 87a9cdfc9e..cbdcba1753 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchReactiveHealthIndicatorTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchReactiveHealthIndicatorTests.java @@ -19,16 +19,20 @@ package org.springframework.boot.actuate.elasticsearch; import java.time.Duration; import java.util.Map; +import co.elastic.clients.json.jackson.JacksonJsonpMapper; +import co.elastic.clients.transport.rest_client.RestClientTransport; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; +import org.apache.http.HttpHost; +import org.elasticsearch.client.ResponseException; +import org.elasticsearch.client.RestClient; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.Status; -import org.springframework.data.elasticsearch.client.ClientConfiguration; -import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient; +import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -50,13 +54,13 @@ class ElasticsearchReactiveHealthIndicatorTests { private ElasticsearchReactiveHealthIndicator healthIndicator; - @SuppressWarnings("deprecation") @BeforeEach void setup() throws Exception { this.server = new MockWebServer(); this.server.start(); - ReactiveElasticsearchClient client = org.springframework.data.elasticsearch.client.erhlc.DefaultReactiveElasticsearchClient - .create(ClientConfiguration.create(this.server.getHostName() + ":" + this.server.getPort())); + ReactiveElasticsearchClient client = new ReactiveElasticsearchClient(new RestClientTransport( + RestClient.builder(HttpHost.create(this.server.getHostName() + ":" + this.server.getPort())).build(), + new JacksonJsonpMapper())); this.healthIndicator = new ElasticsearchReactiveHealthIndicator(client); } @@ -86,20 +90,15 @@ class ElasticsearchReactiveHealthIndicatorTests { this.server.shutdown(); Health health = this.healthIndicator.health().block(TIMEOUT); assertThat(health.getStatus()).isEqualTo(Status.DOWN); - assertThat(health.getDetails().get("error")).asString() - .contains("org.springframework.data.elasticsearch.client.NoReachableHostException"); + assertThat(health.getDetails().get("error")).asString().contains("Connection refused"); } @Test void elasticsearchIsDownByResponseCode() { - // first enqueue an OK response since the HostChecker first sends a HEAD request - // to "/" - this.server.enqueue(new MockResponse().setResponseCode(HttpStatus.OK.value())); this.server.enqueue(new MockResponse().setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR.value())); Health health = this.healthIndicator.health().block(TIMEOUT); assertThat(health.getStatus()).isEqualTo(Status.DOWN); - assertThat(health.getDetails().get("statusCode")).asString().isEqualTo("500"); - assertThat(health.getDetails().get("reasonPhrase")).asString().isEqualTo("Internal Server Error"); + assertThat(health.getDetails().get("error")).asString().startsWith(ResponseException.class.getName()); } @Test @@ -116,32 +115,25 @@ class ElasticsearchReactiveHealthIndicatorTests { entry("active_primary_shards", 0), entry("active_shards", 0), entry("relocating_shards", 0), entry("initializing_shards", 0), entry("unassigned_shards", 0), entry("delayed_unassigned_shards", 0), entry("number_of_pending_tasks", 0), entry("number_of_in_flight_fetch", 0), - entry("task_max_waiting_in_queue_millis", 0), entry("active_shards_percent_as_number", 100.0)); + entry("task_max_waiting_in_queue_millis", 0L), entry("active_shards_percent_as_number", 100.0)); } private void setupMockResponse(int responseCode, String status) { - // first enqueue an OK response since the HostChecker first sends a HEAD request - // to "/" - this.server.enqueue(new MockResponse()); - MockResponse mockResponse = new MockResponse().setResponseCode(HttpStatus.valueOf(responseCode).value()) - .setBody(createJsonResult(responseCode, status)) - .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); + MockResponse mockResponse = new MockResponse().setBody(createJsonResult(status)) + .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .setHeader("X-Elastic-Product", "Elasticsearch"); this.server.enqueue(mockResponse); } - private String createJsonResult(int responseCode, String status) { - if (responseCode == 200) { - return String.format( - "{\"cluster_name\":\"elasticsearch\"," - + "\"status\":\"%s\",\"timed_out\":false,\"number_of_nodes\":1," - + "\"number_of_data_nodes\":1,\"active_primary_shards\":0," - + "\"active_shards\":0,\"relocating_shards\":0,\"initializing_shards\":0," - + "\"unassigned_shards\":0,\"delayed_unassigned_shards\":0," - + "\"number_of_pending_tasks\":0,\"number_of_in_flight_fetch\":0," - + "\"task_max_waiting_in_queue_millis\":0,\"active_shards_percent_as_number\":100.0}", - status); - } - return "{\n \"error\": \"Server Error\",\n \"status\": " + responseCode + "\n}"; + private String createJsonResult(String status) { + return String.format( + "{\"cluster_name\":\"elasticsearch\"," + "\"status\":\"%s\",\"timed_out\":false,\"number_of_nodes\":1," + + "\"number_of_data_nodes\":1,\"active_primary_shards\":0," + + "\"active_shards\":0,\"relocating_shards\":0,\"initializing_shards\":0," + + "\"unassigned_shards\":0,\"delayed_unassigned_shards\":0," + + "\"number_of_pending_tasks\":0,\"number_of_in_flight_fetch\":0," + + "\"task_max_waiting_in_queue_millis\":0,\"active_shards_percent_as_number\":100.0}", + status); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/build.gradle b/spring-boot-project/spring-boot-autoconfigure/build.gradle index c2024fa838..1bcee1cd16 100644 --- a/spring-boot-project/spring-boot-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-autoconfigure/build.gradle @@ -12,6 +12,9 @@ description = "Spring Boot AutoConfigure" dependencies { api(project(":spring-boot-project:spring-boot")) + optional("co.elastic.clients:elasticsearch-java") { + exclude group: "commons-logging", module: "commons-logging" + } optional("com.fasterxml.jackson.core:jackson-databind") optional("com.fasterxml.jackson.dataformat:jackson-dataformat-cbor") optional("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") @@ -99,9 +102,6 @@ dependencies { optional("org.elasticsearch.client:elasticsearch-rest-client-sniffer") { exclude group: "commons-logging", module: "commons-logging" } - optional("org.elasticsearch.client:elasticsearch-rest-high-level-client") { - exclude group: "commons-logging", module: "commons-logging" - } optional("org.flywaydb:flyway-core") optional("org.flywaydb:flyway-sqlserver") optional("org.freemarker:freemarker") diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataAutoConfiguration.java index 417a6ee409..dd5692b993 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataAutoConfiguration.java @@ -19,10 +19,10 @@ package org.springframework.boot.autoconfigure.data.elasticsearch; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration; +import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchClientAutoConfiguration; import org.springframework.boot.autoconfigure.elasticsearch.ReactiveElasticsearchClientAutoConfiguration; import org.springframework.context.annotation.Import; -import org.springframework.data.elasticsearch.client.erhlc.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate; import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories; import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories; @@ -38,12 +38,11 @@ import org.springframework.data.elasticsearch.repository.config.EnableReactiveEl * @see EnableReactiveElasticsearchRepositories */ @AutoConfiguration( - after = { ElasticsearchRestClientAutoConfiguration.class, ReactiveElasticsearchClientAutoConfiguration.class }) -@ConditionalOnClass({ ElasticsearchRestTemplate.class }) + after = { ElasticsearchClientAutoConfiguration.class, ReactiveElasticsearchClientAutoConfiguration.class }) +@ConditionalOnClass({ ElasticsearchTemplate.class }) @Import({ ElasticsearchDataConfiguration.BaseConfiguration.class, - ElasticsearchDataConfiguration.RestClientConfiguration.class, + ElasticsearchDataConfiguration.JavaClientConfiguration.class, ElasticsearchDataConfiguration.ReactiveRestClientConfiguration.class }) -@SuppressWarnings("deprecation") public class ElasticsearchDataAutoConfiguration { } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataConfiguration.java index 1fc7d1b5e4..ab544b13b1 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataConfiguration.java @@ -18,6 +18,8 @@ package org.springframework.boot.autoconfigure.data.elasticsearch; import java.util.Collections; +import co.elastic.clients.elasticsearch.ElasticsearchClient; + import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -26,14 +28,15 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.elasticsearch.annotations.Document; -import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient; +import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate; +import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient; +import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchTemplate; import org.springframework.data.elasticsearch.core.ElasticsearchOperations; import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations; import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; import org.springframework.data.elasticsearch.core.convert.ElasticsearchCustomConversions; import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter; import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext; -import org.springframework.web.reactive.function.client.WebClient; /** * Configuration classes for Spring Data for Elasticsearch @@ -77,33 +80,28 @@ abstract class ElasticsearchDataConfiguration { } - @SuppressWarnings("deprecation") @Configuration(proxyBeanMethods = false) - @ConditionalOnClass(org.elasticsearch.client.RestHighLevelClient.class) - static class RestClientConfiguration { + @ConditionalOnClass(ElasticsearchClient.class) + static class JavaClientConfiguration { @Bean @ConditionalOnMissingBean(value = ElasticsearchOperations.class, name = "elasticsearchTemplate") - @ConditionalOnBean(org.elasticsearch.client.RestHighLevelClient.class) - org.springframework.data.elasticsearch.client.erhlc.ElasticsearchRestTemplate elasticsearchTemplate( - org.elasticsearch.client.RestHighLevelClient client, ElasticsearchConverter converter) { - return new org.springframework.data.elasticsearch.client.erhlc.ElasticsearchRestTemplate(client, converter); + @ConditionalOnBean(ElasticsearchClient.class) + ElasticsearchTemplate elasticsearchTemplate(ElasticsearchClient client, ElasticsearchConverter converter) { + return new ElasticsearchTemplate(client, converter); } } @Configuration(proxyBeanMethods = false) - @ConditionalOnClass({ WebClient.class, ReactiveElasticsearchOperations.class }) static class ReactiveRestClientConfiguration { @Bean @ConditionalOnMissingBean(value = ReactiveElasticsearchOperations.class, name = "reactiveElasticsearchTemplate") @ConditionalOnBean(ReactiveElasticsearchClient.class) - @SuppressWarnings("deprecation") - org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchTemplate reactiveElasticsearchTemplate( - ReactiveElasticsearchClient client, ElasticsearchConverter converter) { - return new org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchTemplate(client, - converter); + ReactiveElasticsearchTemplate reactiveElasticsearchTemplate(ReactiveElasticsearchClient client, + ElasticsearchConverter converter) { + return new ReactiveElasticsearchTemplate(client, converter); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchRepositoriesAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchRepositoriesAutoConfiguration.java index 4bee66b094..ed6a7fe229 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchRepositoriesAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchRepositoriesAutoConfiguration.java @@ -16,8 +16,6 @@ package org.springframework.boot.autoconfigure.data.elasticsearch; -import org.elasticsearch.client.Client; - import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -38,7 +36,7 @@ import org.springframework.data.elasticsearch.repository.support.ElasticsearchRe * @see EnableElasticsearchRepositories */ @AutoConfiguration -@ConditionalOnClass({ Client.class, ElasticsearchRepository.class }) +@ConditionalOnClass(ElasticsearchRepository.class) @ConditionalOnProperty(prefix = "spring.data.elasticsearch.repositories", name = "enabled", havingValue = "true", matchIfMissing = true) @ConditionalOnMissingBean(ElasticsearchRepositoryFactoryBean.class) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveElasticsearchRepositoriesAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveElasticsearchRepositoriesAutoConfiguration.java index b2ea0ceb88..415b5856f7 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveElasticsearchRepositoriesAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveElasticsearchRepositoriesAutoConfiguration.java @@ -22,7 +22,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Import; -import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient; +import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient; import org.springframework.data.elasticsearch.repository.ReactiveElasticsearchRepository; import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories; import org.springframework.data.elasticsearch.repository.support.ReactiveElasticsearchRepositoryFactoryBean; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchClientAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchClientAutoConfiguration.java new file mode 100644 index 0000000000..66f17d4c5a --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchClientAutoConfiguration.java @@ -0,0 +1,42 @@ +/* + * Copyright 2012-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.elasticsearch; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchClientConfigurations.ElasticsearchClientConfiguration; +import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchClientConfigurations.ElasticsearchTransportConfiguration; +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; +import org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration; +import org.springframework.context.annotation.Import; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for Elasticsearch's Java client. + * + * @author Andy Wilkinson + * @since 3.0.0 + */ +@AutoConfiguration(after = { JacksonAutoConfiguration.class, JsonbAutoConfiguration.class, + ElasticsearchRestClientAutoConfiguration.class }) +@ConditionalOnClass(ElasticsearchClient.class) +@Import({ ElasticsearchTransportConfiguration.class, ElasticsearchClientConfiguration.class }) +public class ElasticsearchClientAutoConfiguration { + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchClientConfigurations.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchClientConfigurations.java new file mode 100644 index 0000000000..19569fb001 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchClientConfigurations.java @@ -0,0 +1,107 @@ +/* + * Copyright 2012-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.elasticsearch; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.json.JsonpMapper; +import co.elastic.clients.json.SimpleJsonpMapper; +import co.elastic.clients.json.jackson.JacksonJsonpMapper; +import co.elastic.clients.json.jsonb.JsonbJsonpMapper; +import co.elastic.clients.transport.ElasticsearchTransport; +import co.elastic.clients.transport.TransportOptions; +import co.elastic.clients.transport.rest_client.RestClientTransport; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.json.bind.Jsonb; +import jakarta.json.spi.JsonProvider; +import org.elasticsearch.client.RestClient; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * Configurations for import into {@link ElasticsearchClientAutoConfiguration}. + * + * @author Andy Wilkinson + */ +class ElasticsearchClientConfigurations { + + @ConditionalOnMissingBean(JsonpMapper.class) + @ConditionalOnBean(ObjectMapper.class) + @Configuration(proxyBeanMethods = false) + static class JacksonJsonpMapperConfiguration { + + @Bean + JacksonJsonpMapper jacksonJsonpMapper(ObjectMapper objectMapper) { + return new JacksonJsonpMapper(objectMapper); + } + + } + + @ConditionalOnMissingBean(JsonpMapper.class) + @ConditionalOnBean(Jsonb.class) + @Configuration(proxyBeanMethods = false) + static class JsonbJsonpMapperConfiguration { + + @Bean + JsonbJsonpMapper jsonbJsonpMapper(Jsonb jsonb) { + return new JsonbJsonpMapper(JsonProvider.provider(), jsonb); + } + + } + + @ConditionalOnMissingBean(JsonpMapper.class) + @Configuration(proxyBeanMethods = false) + static class SimpleJsonpMapperConfiguration { + + @Bean + SimpleJsonpMapper simpleJsonpMapper() { + return new SimpleJsonpMapper(); + } + + } + + @Import({ JacksonJsonpMapperConfiguration.class, JsonbJsonpMapperConfiguration.class, + SimpleJsonpMapperConfiguration.class }) + @ConditionalOnBean(RestClient.class) + @ConditionalOnMissingBean(ElasticsearchTransport.class) + static class ElasticsearchTransportConfiguration { + + @Bean + RestClientTransport restClientTransport(RestClient restClient, JsonpMapper jsonMapper, + ObjectProvider transportOptions) { + return new RestClientTransport(restClient, jsonMapper, transportOptions.getIfAvailable()); + } + + } + + @Configuration(proxyBeanMethods = false) + @ConditionalOnBean(ElasticsearchTransport.class) + static class ElasticsearchClientConfiguration { + + @Bean + @ConditionalOnMissingBean + ElasticsearchClient elasticsearchClient(ElasticsearchTransport transport) { + return new ElasticsearchClient(transport); + } + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchProperties.java index 4b155fd63f..38b0b0e4a2 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchProperties.java @@ -22,7 +22,6 @@ import java.util.Collections; import java.util.List; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.util.unit.DataSize; /** * Configuration properties for Elasticsearch. @@ -65,8 +64,6 @@ public class ElasticsearchProperties { private final Restclient restclient = new Restclient(); - private final Webclient webclient = new Webclient(); - public List getUris() { return this.uris; } @@ -119,10 +116,6 @@ public class ElasticsearchProperties { return this.restclient; } - public Webclient getWebclient() { - return this.webclient; - } - public static class Restclient { private final Sniffer sniffer = new Sniffer(); @@ -163,22 +156,4 @@ public class ElasticsearchProperties { } - public static class Webclient { - - /** - * Limit on the number of bytes that can be buffered whenever the input stream - * needs to be aggregated. - */ - private DataSize maxInMemorySize; - - public DataSize getMaxInMemorySize() { - return this.maxInMemorySize; - } - - public void setMaxInMemorySize(DataSize maxInMemorySize) { - this.maxInMemorySize = maxInMemorySize; - } - - } - } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientAutoConfiguration.java index 9ae88f3cdb..bbf626f07e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientAutoConfiguration.java @@ -23,9 +23,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientConfigurations.RestClientBuilderConfiguration; import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientConfigurations.RestClientConfiguration; -import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientConfigurations.RestClientFromRestHighLevelClientConfiguration; import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientConfigurations.RestClientSnifferConfiguration; -import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientConfigurations.RestHighLevelClientConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Import; @@ -39,9 +37,7 @@ import org.springframework.context.annotation.Import; @AutoConfiguration @ConditionalOnClass(RestClientBuilder.class) @EnableConfigurationProperties(ElasticsearchProperties.class) -@Import({ RestClientBuilderConfiguration.class, RestHighLevelClientConfiguration.class, - RestClientFromRestHighLevelClientConfiguration.class, RestClientConfiguration.class, - RestClientSnifferConfiguration.class }) +@Import({ RestClientBuilderConfiguration.class, RestClientConfiguration.class, RestClientSnifferConfiguration.class }) public class ElasticsearchRestClientAutoConfiguration { } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientConfigurations.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientConfigurations.java index c12c53f5b4..c7b64d07c0 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientConfigurations.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientConfigurations.java @@ -35,7 +35,6 @@ import org.elasticsearch.client.sniff.SnifferBuilder; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.context.annotation.Bean; @@ -109,36 +108,7 @@ class ElasticsearchRestClientConfigurations { } - @SuppressWarnings("deprecation") @Configuration(proxyBeanMethods = false) - @ConditionalOnClass(org.elasticsearch.client.RestHighLevelClient.class) - @ConditionalOnMissingBean({ org.elasticsearch.client.RestHighLevelClient.class, RestClient.class }) - static class RestHighLevelClientConfiguration { - - @Bean - org.elasticsearch.client.RestHighLevelClient elasticsearchRestHighLevelClient( - RestClientBuilder restClientBuilder) { - return new org.elasticsearch.client.RestHighLevelClient(restClientBuilder); - } - - } - - @SuppressWarnings("deprecation") - @Configuration(proxyBeanMethods = false) - @ConditionalOnClass(org.elasticsearch.client.RestHighLevelClient.class) - @ConditionalOnSingleCandidate(org.elasticsearch.client.RestHighLevelClient.class) - @ConditionalOnMissingBean(RestClient.class) - static class RestClientFromRestHighLevelClientConfiguration { - - @Bean - RestClient elasticsearchRestClient(org.elasticsearch.client.RestHighLevelClient restHighLevelClient) { - return restHighLevelClient.getLowLevelClient(); - } - - } - - @Configuration(proxyBeanMethods = false) - @ConditionalOnMissingClass("org.elasticsearch.client.RestHighLevelClient") @ConditionalOnMissingBean(RestClient.class) static class RestClientConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ReactiveElasticsearchClientAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ReactiveElasticsearchClientAutoConfiguration.java index 02c61a6a1a..25ceac1186 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ReactiveElasticsearchClientAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ReactiveElasticsearchClientAutoConfiguration.java @@ -16,207 +16,37 @@ package org.springframework.boot.autoconfigure.elasticsearch; -import java.net.URI; -import java.time.Duration; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.client.indices.GetIndexRequest; -import reactor.netty.http.client.HttpClient; +import co.elastic.clients.transport.ElasticsearchTransport; +import reactor.core.publisher.Mono; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.context.annotation.Bean; -import org.springframework.data.elasticsearch.client.ClientConfiguration; -import org.springframework.data.elasticsearch.client.elc.ElasticsearchClients; -import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient; -import org.springframework.data.elasticsearch.client.erhlc.ReactiveRestClients; -import org.springframework.util.Assert; -import org.springframework.util.ObjectUtils; -import org.springframework.util.unit.DataSize; -import org.springframework.web.reactive.function.client.ExchangeStrategies; -import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.context.annotation.Import; +import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient; /** - * {@link EnableAutoConfiguration Auto-configuration} for Elasticsearch Reactive REST - * clients. + * {@link EnableAutoConfiguration Auto-configuration} for Spring Data Elasticsearch's + * reactive client. * * @author Brian Clozel * @since 3.0.0 */ -@AutoConfiguration -@ConditionalOnClass({ ReactiveRestClients.class, ElasticsearchException.class, GetIndexRequest.class, WebClient.class, - HttpClient.class }) +@AutoConfiguration(after = ElasticsearchClientAutoConfiguration.class) +@ConditionalOnClass({ ReactiveElasticsearchClient.class, ElasticsearchTransport.class, Mono.class }) @EnableConfigurationProperties(ElasticsearchProperties.class) -@SuppressWarnings("deprecation") +@Import(ElasticsearchClientConfigurations.ElasticsearchTransportConfiguration.class) public class ReactiveElasticsearchClientAutoConfiguration { - private final ConsolidatedProperties properties; - - ReactiveElasticsearchClientAutoConfiguration(ElasticsearchProperties properties) { - this.properties = new ConsolidatedProperties(properties); - } - @Bean @ConditionalOnMissingBean - public ClientConfiguration clientConfiguration() { - ClientConfiguration.MaybeSecureClientConfigurationBuilder builder = ClientConfiguration.builder() - .connectedTo(this.properties.getEndpoints().toArray(new String[0])); - PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); - map.from(this.properties.isUseSsl()).whenTrue().toCall(builder::usingSsl); - map.from(this.properties.getCredentials()) - .to((credentials) -> builder.withBasicAuth(credentials.getUsername(), credentials.getPassword())); - map.from(this.properties.getConnectionTimeout()).to(builder::withConnectTimeout); - map.from(this.properties.getSocketTimeout()).to(builder::withSocketTimeout); - map.from(this.properties.getPathPrefix()).to(builder::withPathPrefix); - configureExchangeStrategies(map, builder); - return builder.build(); - } - - private void configureExchangeStrategies(PropertyMapper map, - ClientConfiguration.TerminalClientConfigurationBuilder builder) { - map.from(this.properties.getMaxInMemorySize()).asInt(DataSize::toBytes).to((maxInMemorySize) -> { - builder.withClientConfigurer(ElasticsearchClients.WebClientConfigurationCallback.from((webClient) -> { - ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder() - .codecs((configurer) -> configurer.defaultCodecs().maxInMemorySize(maxInMemorySize)).build(); - return webClient.mutate().exchangeStrategies(exchangeStrategies).build(); - })); - }); - } - - @Bean - @ConditionalOnMissingBean - public ReactiveElasticsearchClient reactiveElasticsearchClient(ClientConfiguration clientConfiguration) { - return ReactiveRestClients.create(clientConfiguration); - } - - private static final class ConsolidatedProperties { - - private final ElasticsearchProperties properties; - - private final List uris; - - private ConsolidatedProperties(ElasticsearchProperties properties) { - this.properties = properties; - this.uris = properties.getUris().stream().map((s) -> s.startsWith("http") ? s : "http://" + s) - .map(URI::create).collect(Collectors.toList()); - } - - private List getEndpoints() { - return this.uris.stream().map(this::getEndpoint).collect(Collectors.toList()); - } - - private String getEndpoint(URI uri) { - return uri.getHost() + ":" + uri.getPort(); - } - - private Credentials getCredentials() { - Credentials propertyCredentials = Credentials.from(this.properties); - Credentials uriCredentials = Credentials.from(this.uris); - if (uriCredentials == null) { - return propertyCredentials; - } - Assert.isTrue(propertyCredentials == null || uriCredentials.equals(propertyCredentials), - "Credentials from URI user info do not match those from spring.elasticsearch.username and " - + "spring.elasticsearch.password"); - return uriCredentials; - } - - private Duration getConnectionTimeout() { - return this.properties.getConnectionTimeout(); - } - - private Duration getSocketTimeout() { - return this.properties.getSocketTimeout(); - } - - private boolean isUseSsl() { - Set schemes = this.uris.stream().map(URI::getScheme).collect(Collectors.toSet()); - Assert.isTrue(schemes.size() == 1, "Configured Elasticsearch URIs have varying schemes"); - return schemes.iterator().next().equals("https"); - } - - private DataSize getMaxInMemorySize() { - return this.properties.getWebclient().getMaxInMemorySize(); - } - - private String getPathPrefix() { - return this.properties.getPathPrefix(); - } - - private static final class Credentials { - - private final String username; - - private final String password; - - private Credentials(String username, String password) { - this.username = username; - this.password = password; - } - - private String getUsername() { - return this.username; - } - - private String getPassword() { - return this.password; - } - - private static Credentials from(List uris) { - Set userInfos = uris.stream().map(URI::getUserInfo).collect(Collectors.toSet()); - Assert.isTrue(userInfos.size() == 1, "Configured Elasticsearch URIs have varying user infos"); - String userInfo = userInfos.iterator().next(); - if (userInfo == null) { - return null; - } - String[] parts = userInfo.split(":"); - String username = parts[0]; - String password = (parts.length != 2) ? "" : parts[1]; - return new Credentials(username, password); - } - - private static Credentials from(ElasticsearchProperties properties) { - return getCredentials(properties.getUsername(), properties.getPassword()); - } - - private static Credentials getCredentials(String username, String password) { - if (username == null && password == null) { - return null; - } - return new Credentials(username, password); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - Credentials other = (Credentials) obj; - return ObjectUtils.nullSafeEquals(this.username, other.username) - && ObjectUtils.nullSafeEquals(this.password, other.password); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ObjectUtils.nullSafeHashCode(this.username); - result = prime * result + ObjectUtils.nullSafeHashCode(this.password); - return result; - } - - } - + @ConditionalOnBean(ElasticsearchTransport.class) + ReactiveElasticsearchClient reactiveElasticsearchClient(ElasticsearchTransport transport) { + return new ReactiveElasticsearchClient(transport); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 8e1d611ccb..a3bbf6c0ee 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -981,6 +981,15 @@ "http://localhost:9200" ] }, + { + "name": "spring.elasticsearch.webclient.max-in-memory-size", + "type": "org.springframework.util.unit.DataSize", + "description": "Limit on the number of bytes that can be buffered whenever the input stream needs to be aggregated.", + "deprecation": { + "level": "error", + "reason": "Reactive Elasticsearch client no longer uses WebClient." + } + }, { "name": "spring.flyway.baseline-migration-prefix", "defaultValue": "B" diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 3be3ff4dd2..139e872652 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -39,6 +39,7 @@ org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration +org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchClientAutoConfiguration org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration org.springframework.boot.autoconfigure.elasticsearch.ReactiveElasticsearchClientAutoConfiguration org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataAutoConfigurationTests.java index 20f17db761..8636229c7e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataAutoConfigurationTests.java @@ -20,21 +20,20 @@ import java.math.BigDecimal; import java.util.Collections; import org.assertj.core.api.InstanceOfAssertFactories; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage; import org.springframework.boot.autoconfigure.data.elasticsearch.city.City; +import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchClientAutoConfiguration; import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration; import org.springframework.boot.autoconfigure.elasticsearch.ReactiveElasticsearchClientAutoConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.convert.converter.Converter; -import org.springframework.data.elasticsearch.client.erhlc.ElasticsearchRestTemplate; -import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchTemplate; +import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate; +import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchTemplate; import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; import org.springframework.data.elasticsearch.core.convert.ElasticsearchCustomConversions; import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext; @@ -53,26 +52,16 @@ import static org.mockito.Mockito.mock; * @author Scott Frederick * @author Stephane Nicoll */ -@SuppressWarnings("deprecation") class ElasticsearchDataAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(ElasticsearchRestClientAutoConfiguration.class, - ReactiveElasticsearchClientAutoConfiguration.class, ElasticsearchDataAutoConfiguration.class)); - - @BeforeEach - void setUp() { - System.setProperty("es.set.netty.runtime.available.processors", "false"); - } - - @AfterEach - void tearDown() { - System.clearProperty("es.set.netty.runtime.available.processors"); - } + ElasticsearchClientAutoConfiguration.class, ElasticsearchDataAutoConfiguration.class, + ReactiveElasticsearchClientAutoConfiguration.class)); @Test void defaultRestBeansRegistered() { - this.contextRunner.run((context) -> assertThat(context).hasSingleBean(ElasticsearchRestTemplate.class) + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(ElasticsearchTemplate.class) .hasSingleBean(ReactiveElasticsearchTemplate.class).hasSingleBean(ElasticsearchConverter.class) .hasSingleBean(ElasticsearchConverter.class).hasSingleBean(ElasticsearchCustomConversions.class)); } @@ -92,19 +81,19 @@ class ElasticsearchDataAutoConfigurationTests { this.contextRunner.withUserConfiguration(CustomElasticsearchCustomConversions.class).run((context) -> { assertThat(context).hasSingleBean(ElasticsearchCustomConversions.class).hasBean("testCustomConversions"); assertThat(context.getBean(ElasticsearchConverter.class).getConversionService() - .canConvert(ElasticsearchRestTemplate.class, Boolean.class)).isTrue(); + .canConvert(ElasticsearchTemplate.class, Boolean.class)).isTrue(); }); } @Test void customRestTemplateShouldBeUsed() { this.contextRunner.withUserConfiguration(CustomRestTemplate.class).run((context) -> assertThat(context) - .getBeanNames(ElasticsearchRestTemplate.class).hasSize(1).contains("elasticsearchTemplate")); + .getBeanNames(ElasticsearchTemplate.class).hasSize(1).contains("elasticsearchTemplate")); } @Test void customReactiveRestTemplateShouldBeUsed() { - this.contextRunner.withUserConfiguration(CustomReactiveRestTemplate.class) + this.contextRunner.withUserConfiguration(CustomReactiveElasticsearchTemplate.class) .run((context) -> assertThat(context).getBeanNames(ReactiveElasticsearchTemplate.class).hasSize(1) .contains("reactiveElasticsearchTemplate")); } @@ -131,14 +120,14 @@ class ElasticsearchDataAutoConfigurationTests { static class CustomRestTemplate { @Bean - ElasticsearchRestTemplate elasticsearchTemplate() { - return mock(ElasticsearchRestTemplate.class); + ElasticsearchTemplate elasticsearchTemplate() { + return mock(ElasticsearchTemplate.class); } } @Configuration(proxyBeanMethods = false) - static class CustomReactiveRestTemplate { + static class CustomReactiveElasticsearchTemplate { @Bean ReactiveElasticsearchTemplate reactiveElasticsearchTemplate() { @@ -153,10 +142,10 @@ class ElasticsearchDataAutoConfigurationTests { } - static class MyConverter implements Converter { + static class MyConverter implements Converter { @Override - public Boolean convert(ElasticsearchRestTemplate source) { + public Boolean convert(ElasticsearchTemplate source) { return null; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchRepositoriesAutoConfigurationTests.java index 48a43075ae..8b5f407388 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchRepositoriesAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchRepositoriesAutoConfigurationTests.java @@ -29,11 +29,12 @@ import org.springframework.boot.autoconfigure.data.alt.elasticsearch.CityElastic import org.springframework.boot.autoconfigure.data.elasticsearch.city.City; import org.springframework.boot.autoconfigure.data.elasticsearch.city.CityRepository; import org.springframework.boot.autoconfigure.data.empty.EmptyDataPackage; +import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchClientAutoConfiguration; import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.context.annotation.Configuration; -import org.springframework.data.elasticsearch.client.erhlc.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate; import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories; import static org.assertj.core.api.Assertions.assertThat; @@ -46,7 +47,6 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Brian Clozel */ @Testcontainers(disabledWithoutDocker = true) -@SuppressWarnings("deprecation") class ElasticsearchRepositoriesAutoConfigurationTests { @Container @@ -55,19 +55,20 @@ class ElasticsearchRepositoriesAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(ElasticsearchRestClientAutoConfiguration.class, - ElasticsearchRepositoriesAutoConfiguration.class, ElasticsearchDataAutoConfiguration.class)) + ElasticsearchClientAutoConfiguration.class, ElasticsearchRepositoriesAutoConfiguration.class, + ElasticsearchDataAutoConfiguration.class)) .withPropertyValues("spring.elasticsearch.uris=" + elasticsearch.getHttpHostAddress()); @Test void testDefaultRepositoryConfiguration() { this.contextRunner.withUserConfiguration(TestConfiguration.class).run((context) -> assertThat(context) - .hasSingleBean(CityRepository.class).hasSingleBean(ElasticsearchRestTemplate.class)); + .hasSingleBean(CityRepository.class).hasSingleBean(ElasticsearchTemplate.class)); } @Test void testNoRepositoryConfiguration() { this.contextRunner.withUserConfiguration(EmptyConfiguration.class) - .run((context) -> assertThat(context).hasSingleBean(ElasticsearchRestTemplate.class)); + .run((context) -> assertThat(context).hasSingleBean(ElasticsearchTemplate.class)); } @Test diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveElasticsearchRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveElasticsearchRepositoriesAutoConfigurationTests.java index c9b5ab278c..dda40980ed 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveElasticsearchRepositoriesAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveElasticsearchRepositoriesAutoConfigurationTests.java @@ -29,11 +29,12 @@ import org.springframework.boot.autoconfigure.data.alt.elasticsearch.CityReactiv import org.springframework.boot.autoconfigure.data.elasticsearch.city.City; import org.springframework.boot.autoconfigure.data.elasticsearch.city.ReactiveCityRepository; import org.springframework.boot.autoconfigure.data.empty.EmptyDataPackage; -import org.springframework.boot.autoconfigure.elasticsearch.ReactiveElasticsearchClientAutoConfiguration; +import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchClientAutoConfiguration; +import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.context.annotation.Configuration; -import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchTemplate; +import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchTemplate; import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories; import static org.assertj.core.api.Assertions.assertThat; @@ -46,7 +47,6 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Brian Clozel */ @Testcontainers(disabledWithoutDocker = true) -@SuppressWarnings("deprecation") class ReactiveElasticsearchRepositoriesAutoConfigurationTests { @Container @@ -54,7 +54,8 @@ class ReactiveElasticsearchRepositoriesAutoConfigurationTests { .withStartupAttempts(5).withStartupTimeout(Duration.ofMinutes(10)); private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(ReactiveElasticsearchClientAutoConfiguration.class, + .withConfiguration(AutoConfigurations.of(ElasticsearchClientAutoConfiguration.class, + ElasticsearchRestClientAutoConfiguration.class, ReactiveElasticsearchRepositoriesAutoConfiguration.class, ElasticsearchDataAutoConfiguration.class)) .withPropertyValues( "spring.elasticsearch.uris=" + elasticsearch.getHost() + ":" + elasticsearch.getFirstMappedPort(), diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchClientAutoConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchClientAutoConfigurationIntegrationTests.java new file mode 100644 index 0000000000..2684f3cf05 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchClientAutoConfigurationIntegrationTests.java @@ -0,0 +1,66 @@ +/* + * Copyright 2012-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.elasticsearch; + +import java.time.Duration; +import java.util.Map; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch.core.GetResponse; +import org.junit.jupiter.api.Test; +import org.testcontainers.elasticsearch.ElasticsearchContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.boot.testsupport.testcontainers.DockerImageNames; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link ElasticsearchClientAutoConfiguration}. + * + * @author Andy Wilkinson + */ +@Testcontainers(disabledWithoutDocker = true) +class ElasticsearchClientAutoConfigurationIntegrationTests { + + @Container + static ElasticsearchContainer elasticsearch = new ElasticsearchContainer(DockerImageNames.elasticsearch()) + .withStartupAttempts(5).withStartupTimeout(Duration.ofMinutes(10)); + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class, + ElasticsearchRestClientAutoConfiguration.class, ElasticsearchClientAutoConfiguration.class)); + + @Test + void reactiveClientCanQueryElasticsearchNode() { + this.contextRunner + .withPropertyValues("spring.elasticsearch.uris=" + elasticsearch.getHttpHostAddress(), + "spring.elasticsearch.connection-timeout=120s", "spring.elasticsearch.socket-timeout=120s") + .run((context) -> { + ElasticsearchClient client = context.getBean(ElasticsearchClient.class); + client.index((b) -> b.index("foo").id("1").document(Map.of("a", "alpha", "b", "bravo"))); + GetResponse response = client.get((b) -> b.index("foo").id("1"), Object.class); + assertThat(response).isNotNull(); + assertThat(response.found()).isTrue(); + }); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchClientAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchClientAutoConfigurationTests.java new file mode 100644 index 0000000000..c15a90c6e2 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchClientAutoConfigurationTests.java @@ -0,0 +1,141 @@ +/* + * Copyright 2012-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.elasticsearch; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.json.JsonpMapper; +import co.elastic.clients.json.SimpleJsonpMapper; +import co.elastic.clients.json.jackson.JacksonJsonpMapper; +import co.elastic.clients.json.jsonb.JsonbJsonpMapper; +import co.elastic.clients.transport.ElasticsearchTransport; +import co.elastic.clients.transport.rest_client.RestClientTransport; +import org.elasticsearch.client.RestClient; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; +import org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link ElasticsearchClientAutoConfiguration}. + * + * @author Andy Wilkinson + */ +public class ElasticsearchClientAutoConfigurationTests { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(ElasticsearchClientAutoConfiguration.class)); + + @Test + void withoutRestClientThenAutoConfigurationShouldBackOff() { + this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(ElasticsearchTransport.class) + .doesNotHaveBean(JsonpMapper.class).doesNotHaveBean(ElasticsearchClient.class)); + } + + @Test + void withRestClientAutoConfigurationShouldDefineClientAndSupportingBeans() { + this.contextRunner.withUserConfiguration(RestClientConfiguration.class) + .run((context) -> assertThat(context).hasSingleBean(JsonpMapper.class) + .hasSingleBean(RestClientTransport.class).hasSingleBean(ElasticsearchClient.class)); + } + + @Test + void withoutJsonbOrJacksonShouldDefineSimpleMapper() { + this.contextRunner.withUserConfiguration(RestClientConfiguration.class).run((context) -> assertThat(context) + .hasSingleBean(JsonpMapper.class).hasSingleBean(SimpleJsonpMapper.class)); + } + + @Test + void withJsonbShouldDefineJsonbMapper() { + this.contextRunner.withConfiguration(AutoConfigurations.of(JsonbAutoConfiguration.class)) + .withUserConfiguration(RestClientConfiguration.class).run((context) -> assertThat(context) + .hasSingleBean(JsonpMapper.class).hasSingleBean(JsonbJsonpMapper.class)); + } + + @Test + void withJacksonShouldDefineJacksonMapper() { + this.contextRunner.withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class)) + .withUserConfiguration(RestClientConfiguration.class).run((context) -> assertThat(context) + .hasSingleBean(JsonpMapper.class).hasSingleBean(JacksonJsonpMapper.class)); + } + + @Test + void withJacksonAndJsonbShouldDefineJacksonMapper() { + this.contextRunner + .withConfiguration(AutoConfigurations.of(JsonbAutoConfiguration.class, JacksonAutoConfiguration.class)) + .withUserConfiguration(RestClientConfiguration.class).run((context) -> assertThat(context) + .hasSingleBean(JsonpMapper.class).hasSingleBean(JacksonJsonpMapper.class)); + } + + @Test + void withCustomMapperTransportShouldUseIt() { + this.contextRunner.withUserConfiguration(JsonpMapperConfiguration.class) + .withUserConfiguration(RestClientConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(JsonpMapper.class).hasBean("customJsonpMapper"); + JsonpMapper mapper = context.getBean(JsonpMapper.class); + assertThat(context.getBean(ElasticsearchTransport.class).jsonpMapper()).isSameAs(mapper); + }); + } + + @Test + void withCustomTransportClientShouldUseIt() { + this.contextRunner.withUserConfiguration(TransportConfiguration.class) + .withUserConfiguration(RestClientConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(ElasticsearchTransport.class) + .hasBean("customElasticsearchTransport"); + ElasticsearchTransport transport = context.getBean(ElasticsearchTransport.class); + assertThat(context.getBean(ElasticsearchClient.class)._transport()).isSameAs(transport); + }); + } + + @Configuration(proxyBeanMethods = false) + static class RestClientConfiguration { + + @Bean + RestClient restClient() { + return mock(RestClient.class); + } + + } + + @Configuration(proxyBeanMethods = false) + static class JsonpMapperConfiguration { + + @Bean + JsonpMapper customJsonpMapper() { + return mock(JsonpMapper.class); + } + + } + + @Configuration(proxyBeanMethods = false) + static class TransportConfiguration { + + @Bean + ElasticsearchTransport customElasticsearchTransport() { + return mock(ElasticsearchTransport.class); + } + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientAutoConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientAutoConfigurationIntegrationTests.java index e979c4d1d8..31e5fc62b6 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientAutoConfigurationIntegrationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientAutoConfigurationIntegrationTests.java @@ -18,15 +18,10 @@ package org.springframework.boot.autoconfigure.elasticsearch; import java.io.InputStream; import java.time.Duration; -import java.util.HashMap; -import java.util.Map; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import org.elasticsearch.action.get.GetRequest; -import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.client.Request; -import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.Response; import org.elasticsearch.client.RestClient; import org.junit.jupiter.api.Test; @@ -58,25 +53,6 @@ class ElasticsearchRestClientAutoConfigurationIntegrationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(ElasticsearchRestClientAutoConfiguration.class)); - @Test - @SuppressWarnings("deprecation") - void restHighLevelClientCanQueryElasticsearchNode() { - this.contextRunner - .withPropertyValues("spring.elasticsearch.uris=" + elasticsearch.getHttpHostAddress(), - "spring.elasticsearch.connection-timeout=120s", "spring.elasticsearch.socket-timeout=120s") - .run((context) -> { - org.elasticsearch.client.RestHighLevelClient client = context - .getBean(org.elasticsearch.client.RestHighLevelClient.class); - Map source = new HashMap<>(); - source.put("a", "alpha"); - source.put("b", "bravo"); - IndexRequest index = new IndexRequest("test").id("1").source(source); - client.index(index, RequestOptions.DEFAULT); - GetRequest getRequest = new GetRequest("test").id("1"); - assertThat(client.get(getRequest, RequestOptions.DEFAULT).isExists()).isTrue(); - }); - } - @Test void restClientCanQueryElasticsearchNode() { this.contextRunner diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientAutoConfigurationTests.java index 3a8f1723a0..5343e11953 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientAutoConfigurationTests.java @@ -50,74 +50,32 @@ import static org.mockito.Mockito.mock; * @author Filip Hrisafov * @author Andy Wilkinson */ -@SuppressWarnings("deprecation") class ElasticsearchRestClientAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(ElasticsearchRestClientAutoConfiguration.class)); @Test - void configureShouldCreateHighLevelAndLowLevelRestClients() { - this.contextRunner.run((context) -> { - assertThat(context).hasSingleBean(RestClient.class) - .hasSingleBean(org.elasticsearch.client.RestHighLevelClient.class) - .hasSingleBean(RestClientBuilder.class); - assertThat(context.getBean(RestClient.class)) - .isEqualTo(context.getBean(org.elasticsearch.client.RestHighLevelClient.class).getLowLevelClient()); - }); - } - - @Test - void configureWithoutRestHighLevelClientShouldOnlyCreateRestClientBuilderAndRestClient() { - this.contextRunner.withClassLoader(new FilteredClassLoader(org.elasticsearch.client.RestHighLevelClient.class)) - .run((context) -> assertThat(context).hasSingleBean(RestClient.class) - .hasSingleBean(RestClientBuilder.class) - .doesNotHaveBean(org.elasticsearch.client.RestHighLevelClient.class)); + void configureShouldCreateRestClientBuilderAndRestClient() { + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(RestClient.class) + .hasSingleBean(RestClientBuilder.class)); } @Test void configureWhenCustomRestClientShouldBackOff() { this.contextRunner.withUserConfiguration(CustomRestClientConfiguration.class) - .run((context) -> assertThat(context) - .doesNotHaveBean(org.elasticsearch.client.RestHighLevelClient.class) - .hasSingleBean(RestClientBuilder.class).hasSingleBean(RestClient.class) - .hasBean("customRestClient")); - } - - @Test - void configureWhenCustomRestHighLevelClientShouldDefineRestClientFromCustomHighLevelClient() { - this.contextRunner.withUserConfiguration(CustomRestHighLevelClientConfiguration.class) - .run((context) -> assertThat(context).hasSingleBean(org.elasticsearch.client.RestHighLevelClient.class) - .hasSingleBean(RestClient.class).hasBean("elasticsearchRestClient").getBean(RestClient.class) - .isEqualTo(context.getBean(org.elasticsearch.client.RestHighLevelClient.class) - .getLowLevelClient())); - } - - @Test - void configureWhenCustomRestHighLevelClientAndRestClientShouldBackOff() { - this.contextRunner.withUserConfiguration(CustomRestHighLevelClientWithRestClientConfiguration.class) - .run((context) -> assertThat(context).hasSingleBean(org.elasticsearch.client.RestHighLevelClient.class) - .hasBean("customRestHighLevelClient").hasSingleBean(RestClient.class) - .hasBean("customRestClient")); - } - - @Test - void configureWhenNoUniqueRestHighLevelClientShouldNotDefineRestClient() { - this.contextRunner.withUserConfiguration(TwoCustomRestHighLevelClientsConfiguration.class) - .run((context) -> assertThat(context).doesNotHaveBean(RestClient.class)); + .run((context) -> assertThat(context).hasSingleBean(RestClientBuilder.class) + .hasSingleBean(RestClient.class).hasBean("customRestClient")); } @Test void configureWhenBuilderCustomizerShouldApply() { this.contextRunner.withUserConfiguration(BuilderCustomizerConfiguration.class).run((context) -> { - assertThat(context).hasSingleBean(org.elasticsearch.client.RestHighLevelClient.class) - .hasSingleBean(RestClient.class); - org.elasticsearch.client.RestHighLevelClient restClient = context - .getBean(org.elasticsearch.client.RestHighLevelClient.class); - RestClient lowLevelClient = restClient.getLowLevelClient(); - assertThat(lowLevelClient).hasFieldOrPropertyWithValue("pathPrefix", "/test"); - assertThat(lowLevelClient).extracting("client.connmgr.pool.maxTotal").isEqualTo(100); - assertThat(lowLevelClient).extracting("client.defaultConfig.cookieSpec").isEqualTo("rfc6265-lax"); + assertThat(context).hasSingleBean(RestClient.class); + RestClient restClient = context.getBean(RestClient.class); + assertThat(restClient).hasFieldOrPropertyWithValue("pathPrefix", "/test"); + assertThat(restClient).extracting("client.connmgr.pool.maxTotal").isEqualTo(100); + assertThat(restClient).extracting("client.defaultConfig.cookieSpec").isEqualTo("rfc6265-lax"); }); } @@ -228,22 +186,20 @@ class ElasticsearchRestClientAutoConfigurationTests { @Test void configureWithoutSnifferLibraryShouldNotCreateSniffer() { this.contextRunner.withClassLoader(new FilteredClassLoader("org.elasticsearch.client.sniff")) - .run((context) -> assertThat(context).hasSingleBean(org.elasticsearch.client.RestHighLevelClient.class) - .hasSingleBean(RestClient.class).doesNotHaveBean(Sniffer.class)); + .run((context) -> assertThat(context).hasSingleBean(RestClient.class).doesNotHaveBean(Sniffer.class)); } @Test void configureShouldCreateSnifferUsingRestClient() { - this.contextRunner.withClassLoader(new FilteredClassLoader(org.elasticsearch.client.RestHighLevelClient.class)) - .run((context) -> { - assertThat(context).hasSingleBean(Sniffer.class); - assertThat(context.getBean(Sniffer.class)).hasFieldOrPropertyWithValue("restClient", - context.getBean(RestClient.class)); - // Validate shutdown order as the sniffer must be shutdown before the - // client - assertThat(context.getBeanFactory().getDependentBeans("elasticsearchRestClient")) - .contains("elasticsearchSniffer"); - }); + this.contextRunner.run((context) -> { + assertThat(context).hasSingleBean(Sniffer.class); + assertThat(context.getBean(Sniffer.class)).hasFieldOrPropertyWithValue("restClient", + context.getBean(RestClient.class)); + // Validate shutdown order as the sniffer must be shutdown before the + // client + assertThat(context.getBeanFactory().getDependentBeans("elasticsearchRestClient")) + .contains("elasticsearchSniffer"); + }); } @Test @@ -297,46 +253,6 @@ class ElasticsearchRestClientAutoConfigurationTests { } - @Configuration(proxyBeanMethods = false) - static class CustomRestHighLevelClientConfiguration { - - @Bean - org.elasticsearch.client.RestHighLevelClient customRestHighLevelClient(RestClientBuilder builder) { - return new org.elasticsearch.client.RestHighLevelClient(builder); - } - - } - - @Configuration(proxyBeanMethods = false) - static class CustomRestHighLevelClientWithRestClientConfiguration { - - @Bean - org.elasticsearch.client.RestHighLevelClient customRestHighLevelClient(RestClientBuilder builder) { - return new org.elasticsearch.client.RestHighLevelClient(builder); - } - - @Bean - RestClient customRestClient(org.elasticsearch.client.RestHighLevelClient restHighLevelClient) { - return restHighLevelClient.getLowLevelClient(); - } - - } - - @Configuration(proxyBeanMethods = false) - static class TwoCustomRestHighLevelClientsConfiguration { - - @Bean - org.elasticsearch.client.RestHighLevelClient customRestHighLevelClient(RestClientBuilder builder) { - return new org.elasticsearch.client.RestHighLevelClient(builder); - } - - @Bean - org.elasticsearch.client.RestHighLevelClient anotherCustomRestHighLevelClient(RestClientBuilder builder) { - return new org.elasticsearch.client.RestHighLevelClient(builder); - } - - } - @Configuration(proxyBeanMethods = false) static class CustomRestClientConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ReactiveElasticsearchRestClientAutoConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ReactiveElasticsearchClientAutoConfigurationIntegrationTests.java similarity index 65% rename from spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ReactiveElasticsearchRestClientAutoConfigurationIntegrationTests.java rename to spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ReactiveElasticsearchClientAutoConfigurationIntegrationTests.java index 969d68cbb2..26a64cba64 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ReactiveElasticsearchRestClientAutoConfigurationIntegrationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ReactiveElasticsearchClientAutoConfigurationIntegrationTests.java @@ -17,21 +17,21 @@ package org.springframework.boot.autoconfigure.elasticsearch; import java.time.Duration; -import java.util.HashMap; import java.util.Map; -import org.elasticsearch.action.get.GetRequest; -import org.elasticsearch.action.index.IndexRequest; -import org.elasticsearch.index.get.GetResult; +import co.elastic.clients.elasticsearch.core.GetResponse; +import co.elastic.clients.elasticsearch.core.IndexResponse; import org.junit.jupiter.api.Test; import org.testcontainers.elasticsearch.ElasticsearchContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import reactor.core.publisher.Mono; import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; -import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient; +import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient; import static org.assertj.core.api.Assertions.assertThat; @@ -39,32 +39,33 @@ import static org.assertj.core.api.Assertions.assertThat; * Integration tests for {@link ReactiveElasticsearchClientAutoConfiguration}. * * @author Brian Clozel + * @author Andy Wilkinson */ @Testcontainers(disabledWithoutDocker = true) -class ReactiveElasticsearchRestClientAutoConfigurationIntegrationTests { +class ReactiveElasticsearchClientAutoConfigurationIntegrationTests { @Container static ElasticsearchContainer elasticsearch = new ElasticsearchContainer(DockerImageNames.elasticsearch()) .withStartupAttempts(5).withStartupTimeout(Duration.ofMinutes(10)); - private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(ReactiveElasticsearchClientAutoConfiguration.class)); + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration( + AutoConfigurations.of(JacksonAutoConfiguration.class, ElasticsearchRestClientAutoConfiguration.class, + ReactiveElasticsearchClientAutoConfiguration.class)); @Test - void restClientCanQueryElasticsearchNode() { + void reactiveClientCanQueryElasticsearchNode() { this.contextRunner .withPropertyValues("spring.elasticsearch.uris=" + elasticsearch.getHttpHostAddress(), "spring.elasticsearch.connection-timeout=120s", "spring.elasticsearch.socket-timeout=120s") .run((context) -> { ReactiveElasticsearchClient client = context.getBean(ReactiveElasticsearchClient.class); - Map source = new HashMap<>(); - source.put("a", "alpha"); - source.put("b", "bravo"); - IndexRequest indexRequest = new IndexRequest("foo").id("1").source(source); - GetRequest getRequest = new GetRequest("foo").id("1"); - GetResult getResult = client.index(indexRequest).then(client.get(getRequest)).block(); - assertThat(getResult).isNotNull(); - assertThat(getResult.isExists()).isTrue(); + Mono index = client + .index((b) -> b.index("foo").id("1").document(Map.of("a", "alpha", "b", "bravo"))); + index.block(); + Mono> get = client.get((b) -> b.index("foo").id("1"), Object.class); + GetResponse response = get.block(); + assertThat(response).isNotNull(); + assertThat(response.found()).isTrue(); }); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ReactiveElasticsearchClientAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ReactiveElasticsearchClientAutoConfigurationTests.java index 4df9e6c9eb..cb5f89d8e3 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ReactiveElasticsearchClientAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/ReactiveElasticsearchClientAutoConfigurationTests.java @@ -16,25 +16,14 @@ package org.springframework.boot.autoconfigure.elasticsearch; -import java.net.InetSocketAddress; -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.util.Base64; -import java.util.List; - -import org.assertj.core.api.InstanceOfAssertFactories; +import org.elasticsearch.client.RestClient; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.data.elasticsearch.client.ClientConfiguration; -import org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback; -import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient; -import org.springframework.http.HttpHeaders; -import org.springframework.http.codec.CodecConfigurer.DefaultCodecConfig; -import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -43,6 +32,7 @@ import static org.mockito.Mockito.mock; * Tests for {@link ReactiveElasticsearchClientAutoConfiguration}. * * @author Brian Clozel + * @author Andy Wilkinson */ class ReactiveElasticsearchClientAutoConfigurationTests { @@ -50,206 +40,31 @@ class ReactiveElasticsearchClientAutoConfigurationTests { .withConfiguration(AutoConfigurations.of(ReactiveElasticsearchClientAutoConfiguration.class)); @Test - void configureShouldCreateDefaultBeans() { - this.contextRunner.run((context) -> { - assertThat(context).hasSingleBean(ClientConfiguration.class) - .hasSingleBean(ReactiveElasticsearchClient.class); - List endpoints = context.getBean(ClientConfiguration.class).getEndpoints(); - assertThat(endpoints).hasSize(1); - assertThat(endpoints.get(0).getHostString()).isEqualTo("localhost"); - assertThat(endpoints.get(0).getPort()).isEqualTo(9200); - }); + void configureWithoutRestClientShouldBackOff() { + this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(ReactiveElasticsearchClient.class)); + } + + @Test + void configureWithRestClientShouldCreateTransportAndClient() { + this.contextRunner.withUserConfiguration(RestClientConfiguration.class) + .run((context) -> assertThat(context).hasSingleBean(ReactiveElasticsearchClient.class)); } @Test void configureWhenCustomClientShouldBackOff() { - this.contextRunner.withUserConfiguration(CustomClientConfiguration.class).run((context) -> assertThat(context) - .hasSingleBean(ReactiveElasticsearchClient.class).hasBean("customClient")); - } - - @Test - void configureWhenCustomClientConfig() { - this.contextRunner.withUserConfiguration(CustomClientConfigConfiguration.class) + this.contextRunner.withUserConfiguration(RestClientConfiguration.class, CustomClientConfiguration.class) .run((context) -> assertThat(context).hasSingleBean(ReactiveElasticsearchClient.class) - .hasSingleBean(ClientConfiguration.class).hasBean("customClientConfiguration")); + .hasBean("customClient")); } - @Test - void whenUriIsCustomizedThenClientConfigurationHasCustomEndpoint() { - this.contextRunner.withPropertyValues("spring.elasticsearch.uris=http://localhost:9876").run((context) -> { - List endpoints = context.getBean(ClientConfiguration.class).getEndpoints(); - assertThat(endpoints).hasSize(1); - assertThat(endpoints.get(0).getHostString()).isEqualTo("localhost"); - assertThat(endpoints.get(0).getPort()).isEqualTo(9876); - }); - } + @Configuration(proxyBeanMethods = false) + static class RestClientConfiguration { - @Test - void whenUriHasHttpsSchemeThenClientConfigurationUsesSsl() { - this.contextRunner.withPropertyValues("spring.elasticsearch.uris=https://localhost:9876").run((context) -> { - ClientConfiguration clientConfiguration = context.getBean(ClientConfiguration.class); - List endpoints = clientConfiguration.getEndpoints(); - assertThat(endpoints).hasSize(1); - assertThat(endpoints.get(0).getHostString()).isEqualTo("localhost"); - assertThat(endpoints.get(0).getPort()).isEqualTo(9876); - assertThat(clientConfiguration.useSsl()).isTrue(); - }); - } - - @Test - void whenMultipleUrisAreConfiguredThenClientConfigurationHasMultipleEndpoints() { - this.contextRunner.withPropertyValues("spring.elasticsearch.uris=http://localhost:9876,http://localhost:8765") - .run((context) -> { - List endpoints = context.getBean(ClientConfiguration.class).getEndpoints(); - assertThat(endpoints).hasSize(2); - assertThat(endpoints.get(0).getHostString()).isEqualTo("localhost"); - assertThat(endpoints.get(0).getPort()).isEqualTo(9876); - assertThat(endpoints.get(1).getHostString()).isEqualTo("localhost"); - assertThat(endpoints.get(1).getPort()).isEqualTo(8765); - }); - } - - @Test - void whenMultipleUrisHaveHttpsSchemeThenClientConfigurationUsesSsl() { - this.contextRunner.withPropertyValues("spring.elasticsearch.uris=https://localhost:9876,https://localhost:8765") - .run((context) -> { - ClientConfiguration clientConfiguration = context.getBean(ClientConfiguration.class); - List endpoints = clientConfiguration.getEndpoints(); - assertThat(endpoints).hasSize(2); - assertThat(endpoints.get(0).getHostString()).isEqualTo("localhost"); - assertThat(endpoints.get(0).getPort()).isEqualTo(9876); - assertThat(endpoints.get(1).getHostString()).isEqualTo("localhost"); - assertThat(endpoints.get(1).getPort()).isEqualTo(8765); - assertThat(clientConfiguration.useSsl()).isTrue(); - }); - } - - @Test - void whenMultipleUrisHaveVaryingSchemesThenRunFails() { - this.contextRunner.withPropertyValues("spring.elasticsearch.uris=https://localhost:9876,http://localhost:8765") - .run((context) -> { - assertThat(context).hasFailed(); - assertThat(context).getFailure().hasRootCauseInstanceOf(IllegalArgumentException.class) - .hasRootCauseMessage("Configured Elasticsearch URIs have varying schemes"); - }); - } - - @Test - void whenUriHasUsernameOnlyThenDefaultAuthorizationHeaderHasUsernameAndEmptyPassword() { - this.contextRunner.withPropertyValues("spring.elasticsearch.uris=http://user@localhost:9200").run((context) -> { - ClientConfiguration clientConfiguration = context.getBean(ClientConfiguration.class); - assertThat(clientConfiguration.getDefaultHeaders().get(HttpHeaders.AUTHORIZATION)).containsExactly( - "Basic " + Base64.getEncoder().encodeToString("user:".getBytes(StandardCharsets.UTF_8))); - }); - } - - @Test - void whenUriHasUsernameAndPasswordThenDefaultAuthorizationHeaderHasUsernameAndPassword() { - this.contextRunner.withPropertyValues("spring.elasticsearch.uris=http://user:secret@localhost:9200") - .run((context) -> { - ClientConfiguration clientConfiguration = context.getBean(ClientConfiguration.class); - assertThat(clientConfiguration.getDefaultHeaders().get(HttpHeaders.AUTHORIZATION)) - .containsExactly("Basic " + Base64.getEncoder() - .encodeToString("user:secret".getBytes(StandardCharsets.UTF_8))); - }); - } - - @Test - void whenMultipleUrisHaveVaryingUserInfosThenRunFails() { - this.contextRunner - .withPropertyValues("spring.elasticsearch.uris=http://user:secret@localhost:9876,http://localhost:8765") - .run((context) -> { - assertThat(context).hasFailed(); - assertThat(context).getFailure().hasRootCauseInstanceOf(IllegalArgumentException.class) - .hasRootCauseMessage("Configured Elasticsearch URIs have varying user infos"); - }); - } - - @Test - void whenUriUserInfoMatchesUsernameAndPasswordPropertiesThenDefaultAuthorizationHeaderIsConfigured() { - this.contextRunner.withPropertyValues("spring.elasticsearch.uris=http://user:secret@localhost:9876", - "spring.elasticsearch.username=user", "spring.elasticsearch.password=secret").run((context) -> { - ClientConfiguration clientConfiguration = context.getBean(ClientConfiguration.class); - assertThat(clientConfiguration.getDefaultHeaders().get(HttpHeaders.AUTHORIZATION)) - .containsExactly("Basic " + Base64.getEncoder() - .encodeToString("user:secret".getBytes(StandardCharsets.UTF_8))); - }); - } - - @Test - void whenUriUserInfoAndUsernameAndPasswordPropertiesDoNotMatchThenRunFails() { - this.contextRunner - .withPropertyValues("spring.elasticsearch.uris=http://user:secret@localhost:9876", - "spring.elasticsearch.username=alice", "spring.elasticsearch.password=confidential") - .run((context) -> { - assertThat(context).hasFailed(); - assertThat(context).getFailure().hasRootCauseInstanceOf(IllegalArgumentException.class) - .hasRootCauseMessage("Credentials from URI user info do not match those from " - + "spring.elasticsearch.username and spring.elasticsearch.password"); - }); - } - - @Test - void whenSocketTimeoutIsNotConfiguredThenClientConfigurationUsesDefault() { - this.contextRunner.run((context) -> assertThat(context.getBean(ClientConfiguration.class).getSocketTimeout()) - .isEqualTo(Duration.ofSeconds(30))); - } - - @Test - void whenConnectionTimeoutIsNotConfiguredThenClientConfigurationUsesDefault() { - this.contextRunner.run((context) -> assertThat(context.getBean(ClientConfiguration.class).getConnectTimeout()) - .isEqualTo(Duration.ofSeconds(1))); - } - - @Test - void whenSocketTimeoutIsConfiguredThenClientConfigurationHasCustomSocketTimeout() { - this.contextRunner.withPropertyValues("spring.elasticsearch.socket-timeout=2s") - .run((context) -> assertThat(context.getBean(ClientConfiguration.class).getSocketTimeout()) - .isEqualTo(Duration.ofSeconds(2))); - } - - @Test - void whenConnectionTimeoutIsConfiguredThenClientConfigurationHasCustomConnectTimeout() { - this.contextRunner.withPropertyValues("spring.elasticsearch.connection-timeout=2s") - .run((context) -> assertThat(context.getBean(ClientConfiguration.class).getConnectTimeout()) - .isEqualTo(Duration.ofSeconds(2))); - } - - @Test - void whenPathPrefixIsConfiguredThenClientConfigurationHasPathPrefix() { - this.contextRunner.withPropertyValues("spring.elasticsearch.path-prefix=/some/prefix") - .run((context) -> assertThat(context.getBean(ClientConfiguration.class).getPathPrefix()) - .isEqualTo("/some/prefix")); - } - - @Test - void whenCredentialsAreConfiguredThenClientConfigurationHasDefaultAuthorizationHeader() { - this.contextRunner - .withPropertyValues("spring.elasticsearch.username=alice", "spring.elasticsearch.password=secret") - .run((context) -> assertThat( - context.getBean(ClientConfiguration.class).getDefaultHeaders().get(HttpHeaders.AUTHORIZATION)) - .containsExactly("Basic YWxpY2U6c2VjcmV0")); - } - - @Test - void whenMaxInMemorySizeIsConfiguredThenUnderlyingWebClientHasCustomMaxInMemorySize() { - this.contextRunner.withPropertyValues("spring.elasticsearch.webclient.max-in-memory-size=1MB") - .run((context) -> { - WebClient client = configureWebClient( - context.getBean(ClientConfiguration.class).getClientConfigurers()); - assertThat(client).extracting("exchangeFunction.strategies.codecConfigurer.defaultCodecs") - .asInstanceOf(InstanceOfAssertFactories.type(DefaultCodecConfig.class)) - .extracting(DefaultCodecConfig::maxInMemorySize).isEqualTo(1024 * 1024); - }); - } - - @SuppressWarnings("unchecked") - private WebClient configureWebClient(List> callbacks) { - WebClient webClient = WebClient.create(); - for (ClientConfigurationCallback callback : callbacks) { - webClient = ((ClientConfiguration.ClientConfigurationCallback) callback).configure(webClient); + @Bean + RestClient restClient() { + return mock(RestClient.class); } - return webClient; + } @Configuration(proxyBeanMethods = false) @@ -262,14 +77,4 @@ class ReactiveElasticsearchClientAutoConfigurationTests { } - @Configuration(proxyBeanMethods = false) - static class CustomClientConfigConfiguration { - - @Bean - ClientConfiguration customClientConfiguration() { - return ClientConfiguration.localhost(); - } - - } - } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/HttpMessageConvertersTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/HttpMessageConvertersTests.java index 669e2c7e76..103bbf3962 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/HttpMessageConvertersTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/HttpMessageConvertersTests.java @@ -31,7 +31,6 @@ import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.cbor.MappingJackson2CborHttpMessageConverter; import org.springframework.http.converter.json.GsonHttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.http.converter.smile.MappingJackson2SmileHttpMessageConverter; import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter; import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter; import org.springframework.http.converter.xml.SourceHttpMessageConverter; @@ -58,8 +57,7 @@ class HttpMessageConvertersTests { StringHttpMessageConverter.class, ResourceHttpMessageConverter.class, ResourceRegionHttpMessageConverter.class, SourceHttpMessageConverter.class, AllEncompassingFormHttpMessageConverter.class, MappingJackson2HttpMessageConverter.class, - MappingJackson2SmileHttpMessageConverter.class, MappingJackson2CborHttpMessageConverter.class, - MappingJackson2XmlHttpMessageConverter.class); + MappingJackson2CborHttpMessageConverter.class, MappingJackson2XmlHttpMessageConverter.class); } @Test @@ -130,7 +128,7 @@ class HttpMessageConvertersTests { StringHttpMessageConverter.class, ResourceHttpMessageConverter.class, ResourceRegionHttpMessageConverter.class, SourceHttpMessageConverter.class, AllEncompassingFormHttpMessageConverter.class, MappingJackson2HttpMessageConverter.class, - MappingJackson2SmileHttpMessageConverter.class, MappingJackson2CborHttpMessageConverter.class); + MappingJackson2CborHttpMessageConverter.class); } @Test @@ -151,7 +149,7 @@ class HttpMessageConvertersTests { } assertThat(converterClasses).containsExactly(ByteArrayHttpMessageConverter.class, StringHttpMessageConverter.class, ResourceHttpMessageConverter.class, SourceHttpMessageConverter.class, - MappingJackson2HttpMessageConverter.class, MappingJackson2SmileHttpMessageConverter.class); + MappingJackson2HttpMessageConverter.class); } private List> extractFormPartConverters(List> converters) { diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 3acb542c5c..6f3faf7b9a 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -212,34 +212,20 @@ bom { ] } } - library("Elasticsearch", "7.17.5") { - group("org.elasticsearch") { - modules = [ - "elasticsearch" - ] - } + library("Elasticsearch Client", "8.3.2") { group("org.elasticsearch.client") { modules = [ - "transport", "elasticsearch-rest-client" { exclude group: "commons-logging", module: "commons-logging" }, "elasticsearch-rest-client-sniffer" { exclude group: "commons-logging", module: "commons-logging" }, - "elasticsearch-rest-high-level-client" ] } - group("org.elasticsearch.distribution.integ-test-zip") { + group("co.elastic.clients") { modules = [ - "elasticsearch" { - type = 'zip' - } - ] - } - group("org.elasticsearch.plugin") { - modules = [ - "transport-netty4-client" + "elasticsearch-java" ] } } diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/anchor-rewrite.properties b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/anchor-rewrite.properties index 990226259e..7f3437caef 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/anchor-rewrite.properties +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/anchor-rewrite.properties @@ -998,4 +998,7 @@ dependency-versions.coordinates=appendix.dependency-versions.coordinates dependency-versions.properties=appendix.dependency-versions.properties # gh-30405 -web.servlet.spring-mvc.json=features.json.jackson.custom-serializers-and-deserializers \ No newline at end of file +web.servlet.spring-mvc.json=features.json.jackson.custom-serializers-and-deserializers + +# gh-28597 +data.nosql.elasticsearch.connecting-using-rest.webclient=data.nosql.elasticsearch.connecting-using-rest.reactiveclient diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/nosql.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/nosql.adoc index 682b7f7b0b..3440b9b8cb 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/nosql.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/nosql.adoc @@ -203,7 +203,8 @@ Spring Boot offers basic auto-configuration for Elasticsearch clients. Spring Boot supports several clients: -* The official Java "Low Level" and "High Level" REST clients +* The official low-level REST client +* The official Java API client * The `ReactiveElasticsearchClient` provided by Spring Data Elasticsearch Spring Boot provides a dedicated "`Starter`", `spring-boot-starter-data-elasticsearch`. @@ -212,8 +213,8 @@ Spring Boot provides a dedicated "`Starter`", `spring-boot-starter-data-elastics [[data.nosql.elasticsearch.connecting-using-rest]] ==== Connecting to Elasticsearch using REST clients -Elasticsearch ships https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/index.html[two different REST clients] that you can use to query a cluster: the low-level client from the `org.elasticsearch.client:elasticsearch-rest-client` module and the high-level client from the `org.elasticsearch.client:elasticsearch-high-level-client` module. -Additionally, Spring Boot provides support for a reactive client, based on Spring Framework's `WebClient`, from the `org.springframework.data:spring-data-elasticsearch` module. +Elasticsearch ships two different REST clients] that you can use to query a cluster: the https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/java-rest-low.html[low-level client] from the `org.elasticsearch.client:elasticsearch-rest-client` module and the https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/index.html[Java API client] from the `co.elastic.clients:elasticsearch-java` module. +Additionally, Spring Boot provides support for a reactive client from the `org.springframework.data:spring-data-elasticsearch` module. By default, the clients will target `http://localhost:9200`. You can use `spring.elasticsearch.*` properties to further tune how the clients are configured, as shown in the following example: @@ -230,9 +231,7 @@ You can use `spring.elasticsearch.*` properties to further tune how the clients [[data.nosql.elasticsearch.connecting-using-rest.restclient]] ===== Connecting to Elasticsearch using RestClient If you have `elasticsearch-rest-client` on the classpath, Spring Boot will auto-configure and register a `RestClient` bean. -If you have `elasticsearch-rest-high-level-client` on the classpath a `RestHighLevelClient` bean will be auto-configured as well. -Following Elasticsearch's deprecation of `RestHighLevelClient`, its auto-configuration is deprecated and will be removed in a future release. -In addition to the properties described previously, to fine-tune the `RestClient` and `RestHighLevelClient`, you can register an arbitrary number of beans that implement `RestClientBuilderCustomizer` for more advanced customizations. +In addition to the properties described previously, to fine-tune the `RestClient` you can register an arbitrary number of beans that implement `RestClientBuilderCustomizer` for more advanced customizations. To take full control over the clients' configuration, define a `RestClientBuilder` bean. @@ -251,38 +250,38 @@ You can further tune how `Sniffer` is configured, as shown in the following exam ---- +[[data.nosql.elasticsearch.connecting-using-rest.javaapiclient]] +===== Connecting to Elastixsearch using ElasticsearchClient +If you have `co.elastic.clients:elasticsearch-java` on the classpath, Spring Boot will auto-configure and register an `ElasticsearchClient` bean. -[[data.nosql.elasticsearch.connecting-using-rest.webclient]] +The `ElasticsearchClient` uses a transport that depends upon the previously described `RestClient`. +Therefore, the properties described previously can be used to configure the `ElasticsearchClient`. +Furthermore, you can define a `TransportOptions` bean to take further control of the behavior of the transport. + + + +[[data.nosql.elasticsearch.connecting-using-rest.reactiveclient]] ===== Connecting to Elasticsearch using ReactiveElasticsearchClient {spring-data-elasticsearch}[Spring Data Elasticsearch] ships `ReactiveElasticsearchClient` for querying Elasticsearch instances in a reactive fashion. -It is built on top of WebFlux's `WebClient`, so both `spring-boot-starter-elasticsearch` and `spring-boot-starter-webflux` dependencies are useful to enable this support. +If you have Spring Data Elasticsearch and Reactor on the classpath, Spring Boot will auto-configure and register a `ReactiveElasticsearchClient`. -By default, Spring Boot will auto-configure and register a `ReactiveElasticsearchClient`. -In addition to the properties described previously, the `spring.elasticsearch.webclient.*` properties can be used to configure reactive-specific settings, as shown in the following example: - -[source,yaml,indent=0,subs="verbatim",configprops,configblocks] ----- - spring: - elasticsearch: - webclient: - max-in-memory-size: "1MB" ----- - -If the `spring.elasticsearch.*` and `spring.elasticsearch.webclient.*` configuration properties are not enough and you'd like to fully control the client configuration, you can register a custom `ClientConfiguration` bean. +The `ReactiveElasticsearchclient` uses a transport that depends upon the previously described `RestClient`. +Therefore, the properties described previously can be used to configure the `ReactiveElasticsearchClient`. +Furthermore, you can define a `TransportOptions` bean to take further control of the behavior of the transport. [[data.nosql.elasticsearch.connecting-using-spring-data]] ==== Connecting to Elasticsearch by Using Spring Data -To connect to Elasticsearch, a `RestHighLevelClient` bean must be defined, +To connect to Elasticsearch, an `ElasticsearchClient` bean must be defined, auto-configured by Spring Boot or manually provided by the application (see previous sections). With this configuration in place, an -`ElasticsearchRestTemplate` can be injected like any other Spring bean, +`ElasticsearchTemplate` can be injected like any other Spring bean, as shown in the following example: include::code:MyBean[] -In the presence of `spring-data-elasticsearch` and the required dependencies for using a `WebClient` (typically `spring-boot-starter-webflux`), Spring Boot can also auto-configure a <> and a `ReactiveElasticsearchTemplate` as beans. +In the presence of `spring-data-elasticsearch` and Reactor, Spring Boot can also auto-configure a <> and a `ReactiveElasticsearchTemplate` as beans. They are the reactive equivalent of the other REST clients. diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/data/nosql/elasticsearch/connectingusingspringdata/MyBean.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/data/nosql/elasticsearch/connectingusingspringdata/MyBean.java index 7a54d1cd24..95fd932409 100644 --- a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/data/nosql/elasticsearch/connectingusingspringdata/MyBean.java +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/data/nosql/elasticsearch/connectingusingspringdata/MyBean.java @@ -16,15 +16,15 @@ package org.springframework.boot.docs.data.nosql.elasticsearch.connectingusingspringdata; +import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate; import org.springframework.stereotype.Component; @Component -@SuppressWarnings("deprecation") public class MyBean { - private final org.springframework.data.elasticsearch.client.erhlc.ElasticsearchRestTemplate template; + private final ElasticsearchTemplate template; - public MyBean(org.springframework.data.elasticsearch.client.erhlc.ElasticsearchRestTemplate template) { + public MyBean(ElasticsearchTemplate template) { this.template = template; } diff --git a/spring-boot-project/spring-boot-parent/build.gradle b/spring-boot-project/spring-boot-parent/build.gradle index e62b98650c..40955f6f4a 100644 --- a/spring-boot-project/spring-boot-parent/build.gradle +++ b/spring-boot-project/spring-boot-parent/build.gradle @@ -134,7 +134,7 @@ bom { modules = [ "native-gradle-plugin" ] - } + } } library("Plexus Build API", "0.0.7") { group("org.sonatype.plexus") { diff --git a/spring-boot-project/spring-boot-test-autoconfigure/build.gradle b/spring-boot-project/spring-boot-test-autoconfigure/build.gradle index d398fef2c0..cd58756e58 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-test-autoconfigure/build.gradle @@ -101,11 +101,6 @@ dependencies { testImplementation("org.testcontainers:neo4j") testImplementation("org.testcontainers:testcontainers") testImplementation("org.thymeleaf:thymeleaf") - - testRuntimeOnly("org.elasticsearch:elasticsearch") - testRuntimeOnly("org.elasticsearch.client:elasticsearch-rest-high-level-client") { - exclude group: "commons-logging", module: "commons-logging" - } } configurations { diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.data.elasticsearch.AutoConfigureDataElasticsearch.imports b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.data.elasticsearch.AutoConfigureDataElasticsearch.imports index 24274c9ff9..9e23ddfa32 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.data.elasticsearch.AutoConfigureDataElasticsearch.imports +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.data.elasticsearch.AutoConfigureDataElasticsearch.imports @@ -2,5 +2,8 @@ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration +org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchClientAutoConfiguration org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration org.springframework.boot.autoconfigure.elasticsearch.ReactiveElasticsearchClientAutoConfiguration +org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration +org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestIntegrationTests.java index 9ad2f73c23..059b7061b3 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestIntegrationTests.java @@ -28,6 +28,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.context.ApplicationContext; +import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; @@ -53,8 +54,7 @@ class DataElasticsearchTestIntegrationTests { } @Autowired - @SuppressWarnings("deprecation") - private org.springframework.data.elasticsearch.client.erhlc.ElasticsearchRestTemplate elasticsearchRestTemplate; + private ElasticsearchTemplate elasticsearchTemplate; @Autowired private ExampleRepository exampleRepository; @@ -75,7 +75,7 @@ class DataElasticsearchTestIntegrationTests { String id = UUID.randomUUID().toString(); document.setId(id); ExampleDocument savedDocument = this.exampleRepository.save(document); - ExampleDocument getDocument = this.elasticsearchRestTemplate.get(id, ExampleDocument.class); + ExampleDocument getDocument = this.elasticsearchTemplate.get(id, ExampleDocument.class); assertThat(getDocument).isNotNull(); assertThat(getDocument.getId()).isNotNull(); assertThat(getDocument.getId()).isEqualTo(savedDocument.getId()); diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestReactiveIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestReactiveIntegrationTests.java index 5c180fc16a..794b42a2ce 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestReactiveIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestReactiveIntegrationTests.java @@ -25,6 +25,7 @@ import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; +import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchTemplate; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; @@ -50,8 +51,7 @@ class DataElasticsearchTestReactiveIntegrationTests { } @Autowired - @SuppressWarnings("deprecation") - private org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchTemplate elasticsearchTemplate; + private ReactiveElasticsearchTemplate elasticsearchTemplate; @Autowired private ExampleReactiveRepository exampleReactiveRepository; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/ExampleService.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/ExampleService.java index 58ee668b06..778af7cf0e 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/ExampleService.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/ExampleService.java @@ -16,6 +16,7 @@ package org.springframework.boot.test.autoconfigure.data.elasticsearch; +import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate; import org.springframework.stereotype.Service; /** @@ -24,18 +25,16 @@ import org.springframework.stereotype.Service; * @author EddĂș MelĂ©ndez */ @Service -@SuppressWarnings("deprecation") public class ExampleService { - private final org.springframework.data.elasticsearch.client.erhlc.ElasticsearchRestTemplate elasticsearchRestTemplate; + private final ElasticsearchTemplate elasticsearchTemplate; - public ExampleService( - org.springframework.data.elasticsearch.client.erhlc.ElasticsearchRestTemplate elasticsearchRestTemplate) { - this.elasticsearchRestTemplate = elasticsearchRestTemplate; + public ExampleService(ElasticsearchTemplate elasticsearchRestTemplate) { + this.elasticsearchTemplate = elasticsearchRestTemplate; } public ExampleDocument findById(String id) { - return this.elasticsearchRestTemplate.get(id, ExampleDocument.class); + return this.elasticsearchTemplate.get(id, ExampleDocument.class); } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/build.gradle b/spring-boot-project/spring-boot-tools/spring-boot-test-support/build.gradle index a185547f10..1a6ff9d08c 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support/build.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/build.gradle @@ -13,7 +13,6 @@ dependencies { } compileOnly("jakarta.servlet:jakarta.servlet-api") compileOnly("junit:junit") - compileOnly("org.elasticsearch:elasticsearch") compileOnly("org.junit.jupiter:junit-jupiter") compileOnly("org.junit.platform:junit-platform-engine") compileOnly("org.junit.platform:junit-platform-launcher") diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java index 36803822c3..bea2bda637 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,8 @@ public final class DockerImageNames { private static final String COUCHBASE_VERSION = "6.5.1"; + private static final String ELASTICSEARCH_VERSION = "7.17.5"; + private static final String MONGO_VERSION = "4.0.23"; private static final String NEO4J_VERSION = "4.0"; @@ -60,13 +62,11 @@ public final class DockerImageNames { } /** - * Return a {@link DockerImageName} suitable for running Elasticsearch according to - * the version available on the classpath. + * Return a {@link DockerImageName} suitable for running Elasticsearch. * @return a docker image name for running elasticsearch */ public static DockerImageName elasticsearch() { - String version = org.elasticsearch.Version.CURRENT.toString(); - return DockerImageName.parse("docker.elastic.co/elasticsearch/elasticsearch").withTag(version); + return DockerImageName.parse("docker.elastic.co/elasticsearch/elasticsearch").withTag(ELASTICSEARCH_VERSION); } /**