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 index 51e2b1e83f..095c8e5f3e 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 @@ -39,18 +39,16 @@ import org.springframework.context.annotation.Bean; * @author Artsiom Yudovin * @since 2.1.1 */ -@SuppressWarnings("deprecation") @AutoConfiguration(after = ElasticsearchRestClientAutoConfiguration.class) -@ConditionalOnClass(org.elasticsearch.client.RestHighLevelClient.class) -@ConditionalOnBean(org.elasticsearch.client.RestHighLevelClient.class) +@ConditionalOnClass(RestClient.class) +@ConditionalOnBean(RestClient.class) @ConditionalOnEnabledHealthIndicator("elasticsearch") -public class ElasticSearchRestHealthContributorAutoConfiguration extends - CompositeHealthContributorConfiguration { +public class ElasticSearchRestHealthContributorAutoConfiguration + extends CompositeHealthContributorConfiguration { @Bean @ConditionalOnMissingBean(name = { "elasticsearchHealthIndicator", "elasticsearchHealthContributor" }) - public HealthContributor elasticsearchHealthContributor( - Map clients) { + public HealthContributor elasticsearchHealthContributor(Map clients) { return createContributor(clients); } 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 new file mode 100644 index 0000000000..56972fc4ba --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticSearchRestHealthContributorAutoConfigurationTests.java @@ -0,0 +1,111 @@ +/* + * 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.actuate.autoconfigure.elasticsearch; + +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestClientBuilder; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration; +import org.springframework.boot.actuate.elasticsearch.ElasticsearchRestHealthIndicator; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration; +import org.springframework.boot.test.context.FilteredClassLoader; +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; + +/** + * Tests for {@link ElasticSearchRestHealthContributorAutoConfiguration}. + * + * @author Filip Hrisafov + * @author Andy Wilkinson + */ +class ElasticSearchRestHealthContributorAutoConfigurationTests { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(ElasticsearchRestClientAutoConfiguration.class, + ElasticSearchRestHealthContributorAutoConfiguration.class, + HealthContributorAutoConfiguration.class)); + + @Test + void runShouldCreateIndicator() { + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(ElasticsearchRestHealthIndicator.class) + .hasBean("elasticsearchHealthContributor")); + } + + @Test + @SuppressWarnings("deprecation") + void runWithoutRestHighLevelClientAndWithoutRestClientShouldNotCreateIndicator() { + this.contextRunner + .withClassLoader( + new FilteredClassLoader(org.elasticsearch.client.RestHighLevelClient.class, RestClient.class)) + .run((context) -> assertThat(context).doesNotHaveBean(ElasticsearchRestHealthIndicator.class) + .doesNotHaveBean("elasticsearchHealthContributor")); + } + + @Test + void runWithoutRestHighLevelClientAndWithRestClientShouldCreateIndicator() { + this.contextRunner.withUserConfiguration(CustomRestClientConfiguration.class) + .run((context) -> assertThat(context).hasSingleBean(ElasticsearchRestHealthIndicator.class) + .hasSingleBean(ElasticsearchRestHealthIndicator.class) + .hasBean("elasticsearchHealthContributor")); + } + + @Test + void runWithRestHighLevelClientAndWithRestClientShouldCreateIndicator() { + this.contextRunner.withUserConfiguration(CustomRestHighClientConfiguration.class) + .run((context) -> assertThat(context).hasSingleBean(ElasticsearchRestHealthIndicator.class) + .hasBean("elasticsearchHealthContributor")); + } + + @Test + void runWhenDisabledShouldNotCreateIndicator() { + this.contextRunner.withPropertyValues("management.health.elasticsearch.enabled:false") + .run((context) -> assertThat(context).doesNotHaveBean(ElasticsearchRestHealthIndicator.class) + .doesNotHaveBean("elasticsearchHealthContributor")); + } + + @Configuration(proxyBeanMethods = false) + static class CustomRestClientConfiguration { + + @Bean + RestClient customRestClient(RestClientBuilder builder) { + return builder.build(); + } + + } + + @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-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 d495db352f..db67b1f0d3 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 @@ -22,6 +22,8 @@ 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.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; @@ -40,6 +42,7 @@ import org.springframework.context.annotation.Import; @EnableConfigurationProperties({ ElasticsearchProperties.class, ElasticsearchRestClientProperties.class, DeprecatedElasticsearchRestClientProperties.class }) @Import({ RestClientBuilderConfiguration.class, RestHighLevelClientConfiguration.class, + RestClientFromRestHighLevelClientConfiguration.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 ceea098c85..8e50a3da5f 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 @@ -36,6 +36,7 @@ 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; @@ -46,6 +47,7 @@ import org.springframework.util.StringUtils; * Elasticsearch rest client configurations. * * @author Stephane Nicoll + * @author Filip Hrisafov */ class ElasticsearchRestClientConfigurations { @@ -126,16 +128,41 @@ class ElasticsearchRestClientConfigurations { @SuppressWarnings("deprecation") @Configuration(proxyBeanMethods = false) - @ConditionalOnClass(Sniffer.class) + @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 { + + @Bean + RestClient elasticsearchRestClient(RestClientBuilder restClientBuilder) { + return restClientBuilder.build(); + } + + } + + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(Sniffer.class) + @ConditionalOnSingleCandidate(RestClient.class) static class RestClientSnifferConfiguration { @Bean @ConditionalOnMissingBean - Sniffer elasticsearchSniffer(org.elasticsearch.client.RestHighLevelClient client, - ElasticsearchRestClientProperties properties, + @SuppressWarnings("deprecation") + Sniffer elasticsearchSniffer(RestClient client, ElasticsearchRestClientProperties properties, DeprecatedElasticsearchRestClientProperties deprecatedProperties) { - SnifferBuilder builder = Sniffer.builder(client.getLowLevelClient()); + SnifferBuilder builder = Sniffer.builder(client); PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); Duration interval = deprecatedProperties.isCustomized() ? deprecatedProperties.getSniffer().getInterval() : properties.getSniffer().getInterval(); 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 24be99bfdc..e979c4d1d8 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 @@ -16,13 +16,19 @@ 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; import org.testcontainers.elasticsearch.ElasticsearchContainer; import org.testcontainers.junit.jupiter.Container; @@ -40,6 +46,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Brian Clozel * @author Vedran Pavic * @author Evgeniy Cheban + * @author Filip Hrisafov */ @Testcontainers(disabledWithoutDocker = true) class ElasticsearchRestClientAutoConfigurationIntegrationTests { @@ -53,7 +60,7 @@ class ElasticsearchRestClientAutoConfigurationIntegrationTests { @Test @SuppressWarnings("deprecation") - void restClientCanQueryElasticsearchNode() { + void restHighLevelClientCanQueryElasticsearchNode() { this.contextRunner .withPropertyValues("spring.elasticsearch.uris=" + elasticsearch.getHttpHostAddress(), "spring.elasticsearch.connection-timeout=120s", "spring.elasticsearch.socket-timeout=120s") @@ -70,4 +77,23 @@ class ElasticsearchRestClientAutoConfigurationIntegrationTests { }); } + @Test + void restClientCanQueryElasticsearchNode() { + this.contextRunner + .withPropertyValues("spring.elasticsearch.uris=" + elasticsearch.getHttpHostAddress(), + "spring.elasticsearch.connection-timeout=120s", "spring.elasticsearch.socket-timeout=120s") + .run((context) -> { + RestClient client = context.getBean(RestClient.class); + Request index = new Request("PUT", "/test/_doc/2"); + index.setJsonEntity("{" + " \"a\": \"alpha\"," + " \"b\": \"bravo\"" + "}"); + client.performRequest(index); + Request getRequest = new Request("GET", "/test/_doc/2"); + Response response = client.performRequest(getRequest); + try (InputStream input = response.getEntity().getContent()) { + JsonNode result = new ObjectMapper().readTree(input); + assertThat(result.path("found").asBoolean()).isTrue(); + } + }); + } + } 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 d1f3049ebc..11a8ee9f58 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 @@ -21,7 +21,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.time.Duration; -import java.util.Map; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; @@ -55,6 +54,7 @@ import static org.mockito.Mockito.mock; * @author Vedran Pavic * @author Evgeniy Cheban * @author Filip Hrisafov + * @author Andy Wilkinson */ @SuppressWarnings("deprecation") class ElasticsearchRestClientAutoConfigurationTests { @@ -63,19 +63,22 @@ class ElasticsearchRestClientAutoConfigurationTests { .withConfiguration(AutoConfigurations.of(ElasticsearchRestClientAutoConfiguration.class)); @Test - void configureShouldOnlyCreateHighLevelRestClient() { - this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(RestClient.class) - .hasSingleBean(RestClientBuilder.class) - .hasSingleBean(org.elasticsearch.client.RestHighLevelClient.class)); - + 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 configureWithoutRestHighLevelClientShouldOnlyCreateRestClientBuilder() { + void configureWithoutRestHighLevelClientShouldOnlyCreateRestClientBuilderAndRestClient() { this.contextRunner.withClassLoader(new FilteredClassLoader(org.elasticsearch.client.RestHighLevelClient.class)) - .run((context) -> assertThat(context).doesNotHaveBean(RestClient.class) - .doesNotHaveBean(org.elasticsearch.client.RestHighLevelClient.class) - .hasSingleBean(RestClientBuilder.class)); + .run((context) -> assertThat(context).hasSingleBean(RestClient.class) + .hasSingleBean(RestClientBuilder.class) + .doesNotHaveBean(org.elasticsearch.client.RestHighLevelClient.class)); } @Test @@ -88,24 +91,33 @@ class ElasticsearchRestClientAutoConfigurationTests { } @Test - void configureWhenCustomRestHighLevelClientShouldBackOff() { - this.contextRunner.withUserConfiguration(CustomRestHighLevelClientConfiguration.class).run( - (context) -> assertThat(context).hasSingleBean(org.elasticsearch.client.RestHighLevelClient.class)); + 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 configureWhenDefaultRestClientShouldCreateWhenNoUniqueRestHighLevelClient() { - this.contextRunner.withUserConfiguration(TwoCustomRestHighLevelClientConfiguration.class).run((context) -> { - Map restHighLevelClients = context - .getBeansOfType(org.elasticsearch.client.RestHighLevelClient.class); - assertThat(restHighLevelClients).hasSize(2); - }); + 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)); } @Test void configureWhenBuilderCustomizerShouldApply() { this.contextRunner.withUserConfiguration(BuilderCustomizerConfiguration.class).run((context) -> { - assertThat(context).hasSingleBean(org.elasticsearch.client.RestHighLevelClient.class); + 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(); @@ -118,9 +130,8 @@ class ElasticsearchRestClientAutoConfigurationTests { @Test void configureWithNoTimeoutsApplyDefaults() { this.contextRunner.run((context) -> { - assertThat(context).hasSingleBean(org.elasticsearch.client.RestHighLevelClient.class); - org.elasticsearch.client.RestHighLevelClient restClient = context - .getBean(org.elasticsearch.client.RestHighLevelClient.class); + assertThat(context).hasSingleBean(RestClient.class); + RestClient restClient = context.getBean(RestClient.class); assertTimeouts(restClient, Duration.ofMillis(RestClientBuilder.DEFAULT_CONNECT_TIMEOUT_MILLIS), Duration.ofMillis(RestClientBuilder.DEFAULT_SOCKET_TIMEOUT_MILLIS)); }); @@ -130,9 +141,8 @@ class ElasticsearchRestClientAutoConfigurationTests { void configureWithLegacyCustomTimeouts() { this.contextRunner.withPropertyValues("spring.elasticsearch.rest.connection-timeout=15s", "spring.elasticsearch.rest.read-timeout=1m").run((context) -> { - assertThat(context).hasSingleBean(org.elasticsearch.client.RestHighLevelClient.class); - org.elasticsearch.client.RestHighLevelClient restClient = context - .getBean(org.elasticsearch.client.RestHighLevelClient.class); + assertThat(context).hasSingleBean(RestClient.class); + RestClient restClient = context.getBean(RestClient.class); assertTimeouts(restClient, Duration.ofSeconds(15), Duration.ofMinutes(1)); }); } @@ -141,25 +151,23 @@ class ElasticsearchRestClientAutoConfigurationTests { void configureWithCustomTimeouts() { this.contextRunner.withPropertyValues("spring.elasticsearch.connection-timeout=15s", "spring.elasticsearch.socket-timeout=1m").run((context) -> { - assertThat(context).hasSingleBean(org.elasticsearch.client.RestHighLevelClient.class); - org.elasticsearch.client.RestHighLevelClient restClient = context - .getBean(org.elasticsearch.client.RestHighLevelClient.class); + assertThat(context).hasSingleBean(RestClient.class); + RestClient restClient = context.getBean(RestClient.class); assertTimeouts(restClient, Duration.ofSeconds(15), Duration.ofMinutes(1)); }); } - private static void assertTimeouts(org.elasticsearch.client.RestHighLevelClient restClient, Duration connectTimeout, - Duration readTimeout) { - assertThat(restClient.getLowLevelClient()).extracting("client.defaultConfig.socketTimeout") + private static void assertTimeouts(RestClient restClient, Duration connectTimeout, Duration readTimeout) { + assertThat(restClient).extracting("client.defaultConfig.socketTimeout") .isEqualTo(Math.toIntExact(readTimeout.toMillis())); - assertThat(restClient.getLowLevelClient()).extracting("client.defaultConfig.connectTimeout") + assertThat(restClient).extracting("client.defaultConfig.connectTimeout") .isEqualTo(Math.toIntExact(connectTimeout.toMillis())); } @ParameterizedPropertyPrefixTest void configureUriWithNoScheme(String prefix) { this.contextRunner.withPropertyValues(prefix + "uris=localhost:9876").run((context) -> { - RestClient client = context.getBean(org.elasticsearch.client.RestHighLevelClient.class).getLowLevelClient(); + RestClient client = context.getBean(RestClient.class); assertThat(client.getNodes().stream().map(Node::getHost).map(HttpHost::toString)) .containsExactly("http://localhost:9876"); }); @@ -168,7 +176,7 @@ class ElasticsearchRestClientAutoConfigurationTests { @ParameterizedPropertyPrefixTest void configureUriWithUsernameOnly(String prefix) { this.contextRunner.withPropertyValues(prefix + "uris=http://user@localhost:9200").run((context) -> { - RestClient client = context.getBean(org.elasticsearch.client.RestHighLevelClient.class).getLowLevelClient(); + RestClient client = context.getBean(RestClient.class); assertThat(client.getNodes().stream().map(Node::getHost).map(HttpHost::toString)) .containsExactly("http://localhost:9200"); assertThat(client) @@ -184,7 +192,7 @@ class ElasticsearchRestClientAutoConfigurationTests { @ParameterizedPropertyPrefixTest void configureUriWithUsernameAndEmptyPassword(String prefix) { this.contextRunner.withPropertyValues(prefix + "uris=http://user:@localhost:9200").run((context) -> { - RestClient client = context.getBean(org.elasticsearch.client.RestHighLevelClient.class).getLowLevelClient(); + RestClient client = context.getBean(RestClient.class); assertThat(client.getNodes().stream().map(Node::getHost).map(HttpHost::toString)) .containsExactly("http://localhost:9200"); assertThat(client) @@ -201,8 +209,7 @@ class ElasticsearchRestClientAutoConfigurationTests { void configureUriWithUsernameAndPasswordWhenUsernameAndPasswordPropertiesSet(String prefix) { this.contextRunner.withPropertyValues(prefix + "uris=http://user:password@localhost:9200,localhost:9201", prefix + "username=admin", prefix + "password=admin").run((context) -> { - RestClient client = context.getBean(org.elasticsearch.client.RestHighLevelClient.class) - .getLowLevelClient(); + RestClient client = context.getBean(RestClient.class); assertThat(client.getNodes().stream().map(Node::getHost).map(HttpHost::toString)) .containsExactly("http://localhost:9200", "http://localhost:9201"); assertThat(client) @@ -224,7 +231,7 @@ class ElasticsearchRestClientAutoConfigurationTests { @Test void configureWithCustomPathPrefix() { this.contextRunner.withPropertyValues("spring.elasticsearch.path-prefix=/some/prefix").run((context) -> { - RestClient client = context.getBean(org.elasticsearch.client.RestHighLevelClient.class).getLowLevelClient(); + RestClient client = context.getBean(RestClient.class); assertThat(client).extracting("pathPrefix").isEqualTo("/some/prefix"); }); } @@ -233,19 +240,21 @@ class ElasticsearchRestClientAutoConfigurationTests { void configureWithoutSnifferLibraryShouldNotCreateSniffer() { this.contextRunner.withClassLoader(new FilteredClassLoader("org.elasticsearch.client.sniff")) .run((context) -> assertThat(context).hasSingleBean(org.elasticsearch.client.RestHighLevelClient.class) - .doesNotHaveBean(Sniffer.class)); + .hasSingleBean(RestClient.class).doesNotHaveBean(Sniffer.class)); } @Test - void configureShouldCreateSnifferUsingRestHighLevelClient() { - this.contextRunner.run((context) -> { - assertThat(context).hasSingleBean(Sniffer.class); - assertThat(context.getBean(Sniffer.class)).hasFieldOrPropertyWithValue("restClient", - context.getBean(org.elasticsearch.client.RestHighLevelClient.class).getLowLevelClient()); - // Validate shutdown order as the sniffer must be shutdown before the client - assertThat(context.getBeanFactory().getDependentBeans("elasticsearchRestHighLevelClient")) - .contains("elasticsearchSniffer"); - }); + 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"); + }); } @ParameterizedSnifferPropertyPrefixTest @@ -310,7 +319,7 @@ class ElasticsearchRestClientAutoConfigurationTests { } @Configuration(proxyBeanMethods = false) - static class TwoCustomRestHighLevelClientConfiguration { + static class CustomRestHighLevelClientWithRestClientConfiguration { @Bean org.elasticsearch.client.RestHighLevelClient customRestHighLevelClient(RestClientBuilder builder) { @@ -318,7 +327,22 @@ class ElasticsearchRestClientAutoConfigurationTests { } @Bean - org.elasticsearch.client.RestHighLevelClient customoRestHighLevelClient1(RestClientBuilder builder) { + 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); } @@ -334,6 +358,21 @@ class ElasticsearchRestClientAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class TwoCustomRestClientConfiguration { + + @Bean + RestClient customRestClient(RestClientBuilder builder) { + return builder.build(); + } + + @Bean + RestClient customRestClient1(RestClientBuilder builder) { + return builder.build(); + } + + } + @ParameterizedTest @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) 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 56823d5325..122a91afe9 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 @@ -252,9 +252,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 and the "High Level" client. -Spring Boot provides support for the "High Level" client, which ships with `org.elasticsearch.client:elasticsearch-rest-high-level-client`. -Additionally, Spring Boot provides support for a reactive client, based on Spring Framework's `WebClient`, that ships with `org.springframework.data:spring-data-elasticsearch`. +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. 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: @@ -269,14 +268,15 @@ You can use `spring.elasticsearch.*` properties to further tune how the clients ---- [[data.nosql.elasticsearch.connecting-using-rest.restclient]] -===== Connecting to Elasticsearch using RestHighLevelClient -If you have `elasticsearch-rest-high-level-client` on the classpath, Spring Boot will auto-configure and register a `RestHighLevelClient` bean. -In addition to the properties described previously, to fine-tune the `RestHighLevelClient`, you can register an arbitrary number of beans that implement `RestClientBuilderCustomizer` for more advanced customizations. -To take full control over its registration, define a `RestClientBuilder` bean. +===== 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 registered as well. +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. +To take full control over the clients' configuration, define a `RestClientBuilder` bean. -TIP: If your application needs access to a "Low Level" `RestClient`, you can get it by calling `client.getLowLevelClient()` on the auto-configured `RestHighLevelClient`. -Additionally, if `elasticsearch-rest-client-sniffer` is on the classpath, a `Sniffer` is auto-configured to automatically discover nodes from a running Elasticsearch cluster and set them on the `RestHighLevelClient` bean. + +Additionally, if `elasticsearch-rest-client-sniffer` is on the classpath, a `Sniffer` is auto-configured to automatically discover nodes from a running Elasticsearch cluster and set them on the `RestClient` bean. You can further tune how `Sniffer` is configured, as shown in the following example: [source,yaml,indent=0,subs="verbatim",configprops,configblocks]