Bring back Elasticsearch RestClient auto-configuration
Prior to this commit, Spring Boot would only auto-configure the
`RestHighLevelClient` and `RestClientBuilder` if the
`RestHighLevelClient` was present. This was done in 1d73d4ed.
This commit brings back the exposing of the `RestClient` bean in when
exposing the `RestHighLevelClient` or when the `RestHighLevelClient`
is not present. It allows for using the auto-configuration and its
customizers of the `RestClientBuilder` in a similar way as it is done
for the `RestTemplateBuilder` and the `WebClient.Builder`.
The presence of the `elasticsearch-rest-high-level-client` module is
now optional. This opens the door for potentially adding support for
the new Elasticsearch Java Client[1] that is based on the same
`RestClient`.
The health contributor and its configuration has also been updated to
only depend on the low-level RestClient.
See gh-28496
[1] https://github.com/elastic/elasticsearch-java
			
			
This commit is contained in:
		
							parent
							
								
									e0ae1d3501
								
							
						
					
					
						commit
						eb3bf40bdb
					
				| 
						 | 
					@ -44,13 +44,12 @@ import org.springframework.context.annotation.Bean;
 | 
				
			||||||
@ConditionalOnClass(org.elasticsearch.client.RestHighLevelClient.class)
 | 
					@ConditionalOnClass(org.elasticsearch.client.RestHighLevelClient.class)
 | 
				
			||||||
@ConditionalOnBean(org.elasticsearch.client.RestHighLevelClient.class)
 | 
					@ConditionalOnBean(org.elasticsearch.client.RestHighLevelClient.class)
 | 
				
			||||||
@ConditionalOnEnabledHealthIndicator("elasticsearch")
 | 
					@ConditionalOnEnabledHealthIndicator("elasticsearch")
 | 
				
			||||||
public class ElasticSearchRestHealthContributorAutoConfiguration extends
 | 
					public class ElasticSearchRestHealthContributorAutoConfiguration
 | 
				
			||||||
		CompositeHealthContributorConfiguration<ElasticsearchRestHealthIndicator, org.elasticsearch.client.RestHighLevelClient> {
 | 
							extends CompositeHealthContributorConfiguration<ElasticsearchRestHealthIndicator, RestClient> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Bean
 | 
						@Bean
 | 
				
			||||||
	@ConditionalOnMissingBean(name = { "elasticsearchHealthIndicator", "elasticsearchHealthContributor" })
 | 
						@ConditionalOnMissingBean(name = { "elasticsearchHealthIndicator", "elasticsearchHealthContributor" })
 | 
				
			||||||
	public HealthContributor elasticsearchHealthContributor(
 | 
						public HealthContributor elasticsearchHealthContributor(Map<String, RestClient> clients) {
 | 
				
			||||||
			Map<String, org.elasticsearch.client.RestHighLevelClient> clients) {
 | 
					 | 
				
			||||||
		return createContributor(clients);
 | 
							return createContributor(clients);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,69 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.elasticsearch.client.RestClient;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthContributorConfiguration;
 | 
				
			||||||
 | 
					import org.springframework.boot.actuate.elasticsearch.ElasticsearchRestClientHealthIndicator;
 | 
				
			||||||
 | 
					import org.springframework.boot.actuate.health.HealthContributor;
 | 
				
			||||||
 | 
					import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
 | 
				
			||||||
 | 
					import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 | 
				
			||||||
 | 
					import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 | 
				
			||||||
 | 
					import org.springframework.context.annotation.Bean;
 | 
				
			||||||
 | 
					import org.springframework.context.annotation.Configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Elasticsearch rest client health contributor configurations.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Filip Hrisafov
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class ElasticSearchRestHealthContributorConfigurations {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Configuration(proxyBeanMethods = false)
 | 
				
			||||||
 | 
						@ConditionalOnClass(org.elasticsearch.client.RestHighLevelClient.class)
 | 
				
			||||||
 | 
						@ConditionalOnBean(org.elasticsearch.client.RestHighLevelClient.class)
 | 
				
			||||||
 | 
						@Deprecated
 | 
				
			||||||
 | 
						static class RestHighLevelClientHealthContributorConfiguration extends
 | 
				
			||||||
 | 
								CompositeHealthContributorConfiguration<org.springframework.boot.actuate.elasticsearch.ElasticsearchRestHealthIndicator, org.elasticsearch.client.RestHighLevelClient> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							@Bean
 | 
				
			||||||
 | 
							@ConditionalOnMissingBean(name = { "elasticsearchHealthIndicator", "elasticsearchHealthContributor" })
 | 
				
			||||||
 | 
							HealthContributor elasticsearchHealthContributor(
 | 
				
			||||||
 | 
									Map<String, org.elasticsearch.client.RestHighLevelClient> clients) {
 | 
				
			||||||
 | 
								return createContributor(clients);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Configuration(proxyBeanMethods = false)
 | 
				
			||||||
 | 
						@ConditionalOnBean(RestClient.class)
 | 
				
			||||||
 | 
						@ConditionalOnMissingBean(org.elasticsearch.client.RestHighLevelClient.class)
 | 
				
			||||||
 | 
						static class RestClientHealthContributorConfiguration
 | 
				
			||||||
 | 
								extends CompositeHealthContributorConfiguration<ElasticsearchRestClientHealthIndicator, RestClient> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							@Bean
 | 
				
			||||||
 | 
							@ConditionalOnMissingBean(name = { "elasticsearchHealthIndicator", "elasticsearchHealthContributor" })
 | 
				
			||||||
 | 
							HealthContributor elasticsearchHealthContributor(Map<String, RestClient> clients) {
 | 
				
			||||||
 | 
								return createContributor(clients);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -50,6 +50,7 @@ class ElasticsearchReactiveHealthContributorAutoConfigurationTests {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
 | 
						@SuppressWarnings("deprecation")
 | 
				
			||||||
	void runWithRegularIndicatorShouldOnlyCreateReactiveIndicator() {
 | 
						void runWithRegularIndicatorShouldOnlyCreateReactiveIndicator() {
 | 
				
			||||||
		this.contextRunner
 | 
							this.contextRunner
 | 
				
			||||||
				.withConfiguration(AutoConfigurations.of(ElasticSearchRestHealthContributorAutoConfiguration.class))
 | 
									.withConfiguration(AutoConfigurations.of(ElasticSearchRestHealthContributorAutoConfiguration.class))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,113 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2012-2020 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.ElasticsearchRestClientHealthIndicator;
 | 
				
			||||||
 | 
					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 ElasticsearchRestClientAutoConfiguration}.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Filip Hrisafov
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class ElasticsearchRestHealthContributorAutoConfigurationTests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
 | 
				
			||||||
 | 
								.withConfiguration(AutoConfigurations.of(ElasticsearchRestClientAutoConfiguration.class,
 | 
				
			||||||
 | 
										ElasticSearchRestHealthContributorAutoConfiguration.class,
 | 
				
			||||||
 | 
										HealthContributorAutoConfiguration.class));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						@SuppressWarnings("deprecation")
 | 
				
			||||||
 | 
						void runShouldCreateIndicator() {
 | 
				
			||||||
 | 
							this.contextRunner.run((context) -> assertThat(context).hasSingleBean(ElasticsearchRestHealthIndicator.class)
 | 
				
			||||||
 | 
									.hasBean("elasticsearchHealthContributor"));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						void runWithoutRestHighLevelClientAndWithoutRestClientShouldNotCreateIndicator() {
 | 
				
			||||||
 | 
							this.contextRunner
 | 
				
			||||||
 | 
									.withClassLoader(
 | 
				
			||||||
 | 
											new FilteredClassLoader(org.elasticsearch.client.RestHighLevelClient.class, RestClient.class))
 | 
				
			||||||
 | 
									.run((context) -> assertThat(context).doesNotHaveBean(ElasticsearchRestClientHealthIndicator.class)
 | 
				
			||||||
 | 
											.doesNotHaveBean("elasticsearchHealthContributor"));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						@SuppressWarnings("deprecation")
 | 
				
			||||||
 | 
						void runWithoutRestHighLevelClientAndWithRestClientShouldCreateIndicator() {
 | 
				
			||||||
 | 
							this.contextRunner.withUserConfiguration(CustomRestClientConfiguration.class)
 | 
				
			||||||
 | 
									.run((context) -> assertThat(context).hasSingleBean(ElasticsearchRestClientHealthIndicator.class)
 | 
				
			||||||
 | 
											.doesNotHaveBean(ElasticsearchRestHealthIndicator.class)
 | 
				
			||||||
 | 
											.hasBean("elasticsearchHealthContributor"));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						@SuppressWarnings("deprecation")
 | 
				
			||||||
 | 
						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")
 | 
				
			||||||
 | 
									.run((context) -> assertThat(context).doesNotHaveBean(ElasticsearchRestClientHealthIndicator.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();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,85 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2012-2020 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.elasticsearch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.InputStream;
 | 
				
			||||||
 | 
					import java.nio.charset.StandardCharsets;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.apache.http.HttpStatus;
 | 
				
			||||||
 | 
					import org.apache.http.StatusLine;
 | 
				
			||||||
 | 
					import org.elasticsearch.client.Request;
 | 
				
			||||||
 | 
					import org.elasticsearch.client.Response;
 | 
				
			||||||
 | 
					import org.elasticsearch.client.RestClient;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.springframework.boot.actuate.health.AbstractHealthIndicator;
 | 
				
			||||||
 | 
					import org.springframework.boot.actuate.health.Health;
 | 
				
			||||||
 | 
					import org.springframework.boot.actuate.health.HealthIndicator;
 | 
				
			||||||
 | 
					import org.springframework.boot.json.JsonParser;
 | 
				
			||||||
 | 
					import org.springframework.boot.json.JsonParserFactory;
 | 
				
			||||||
 | 
					import org.springframework.util.StreamUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * {@link HealthIndicator} for an Elasticsearch cluster using a {@link RestClient}.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Artsiom Yudovin
 | 
				
			||||||
 | 
					 * @author Brian Clozel
 | 
				
			||||||
 | 
					 * @author Filip Hrisafov
 | 
				
			||||||
 | 
					 * @since 2.7
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ElasticsearchRestClientHealthIndicator extends AbstractHealthIndicator {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static final String RED_STATUS = "red";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private final RestClient client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private final JsonParser jsonParser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public ElasticsearchRestClientHealthIndicator(RestClient client) {
 | 
				
			||||||
 | 
							super("Elasticsearch health check failed");
 | 
				
			||||||
 | 
							this.client = client;
 | 
				
			||||||
 | 
							this.jsonParser = JsonParserFactory.getJsonParser();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						protected void doHealthCheck(Health.Builder builder) throws Exception {
 | 
				
			||||||
 | 
							Response response = this.client.performRequest(new Request("GET", "/_cluster/health/"));
 | 
				
			||||||
 | 
							StatusLine statusLine = response.getStatusLine();
 | 
				
			||||||
 | 
							if (statusLine.getStatusCode() != HttpStatus.SC_OK) {
 | 
				
			||||||
 | 
								builder.down();
 | 
				
			||||||
 | 
								builder.withDetail("statusCode", statusLine.getStatusCode());
 | 
				
			||||||
 | 
								builder.withDetail("reasonPhrase", statusLine.getReasonPhrase());
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							try (InputStream inputStream = response.getEntity().getContent()) {
 | 
				
			||||||
 | 
								doHealthCheck(builder, StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private void doHealthCheck(Health.Builder builder, String json) {
 | 
				
			||||||
 | 
							Map<String, Object> response = this.jsonParser.parseMap(json);
 | 
				
			||||||
 | 
							String status = (String) response.get("status");
 | 
				
			||||||
 | 
							if (RED_STATUS.equals(status)) {
 | 
				
			||||||
 | 
								builder.outOfService();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else {
 | 
				
			||||||
 | 
								builder.up();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							builder.withDetails(response);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -16,22 +16,9 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package org.springframework.boot.actuate.elasticsearch;
 | 
					package org.springframework.boot.actuate.elasticsearch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.io.InputStream;
 | 
					 | 
				
			||||||
import java.nio.charset.StandardCharsets;
 | 
					 | 
				
			||||||
import java.util.Map;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import org.apache.http.HttpStatus;
 | 
					 | 
				
			||||||
import org.apache.http.StatusLine;
 | 
					 | 
				
			||||||
import org.elasticsearch.client.Request;
 | 
					 | 
				
			||||||
import org.elasticsearch.client.Response;
 | 
					 | 
				
			||||||
import org.elasticsearch.client.RestClient;
 | 
					import org.elasticsearch.client.RestClient;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
 | 
					 | 
				
			||||||
import org.springframework.boot.actuate.health.Health;
 | 
					 | 
				
			||||||
import org.springframework.boot.actuate.health.HealthIndicator;
 | 
					import org.springframework.boot.actuate.health.HealthIndicator;
 | 
				
			||||||
import org.springframework.boot.json.JsonParser;
 | 
					 | 
				
			||||||
import org.springframework.boot.json.JsonParserFactory;
 | 
					 | 
				
			||||||
import org.springframework.util.StreamUtils;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * {@link HealthIndicator} for an Elasticsearch cluster using a {@link RestClient}.
 | 
					 * {@link HealthIndicator} for an Elasticsearch cluster using a {@link RestClient}.
 | 
				
			||||||
| 
						 | 
					@ -40,14 +27,11 @@ import org.springframework.util.StreamUtils;
 | 
				
			||||||
 * @author Brian Clozel
 | 
					 * @author Brian Clozel
 | 
				
			||||||
 * @author Filip Hrisafov
 | 
					 * @author Filip Hrisafov
 | 
				
			||||||
 * @since 2.1.1
 | 
					 * @since 2.1.1
 | 
				
			||||||
 | 
					 * @deprecated since 2.7.0 for removal in 2.9.0 in favor of
 | 
				
			||||||
 | 
					 * {@link ElasticsearchRestClientHealthIndicator}
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class ElasticsearchRestHealthIndicator extends AbstractHealthIndicator {
 | 
					@Deprecated
 | 
				
			||||||
 | 
					public class ElasticsearchRestHealthIndicator extends ElasticsearchRestClientHealthIndicator {
 | 
				
			||||||
	private static final String RED_STATUS = "red";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	private final RestClient client;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	private final JsonParser jsonParser;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@SuppressWarnings("deprecation")
 | 
						@SuppressWarnings("deprecation")
 | 
				
			||||||
	public ElasticsearchRestHealthIndicator(org.elasticsearch.client.RestHighLevelClient client) {
 | 
						public ElasticsearchRestHealthIndicator(org.elasticsearch.client.RestHighLevelClient client) {
 | 
				
			||||||
| 
						 | 
					@ -55,36 +39,7 @@ public class ElasticsearchRestHealthIndicator extends AbstractHealthIndicator {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public ElasticsearchRestHealthIndicator(RestClient client) {
 | 
						public ElasticsearchRestHealthIndicator(RestClient client) {
 | 
				
			||||||
		super("Elasticsearch health check failed");
 | 
							super(client);
 | 
				
			||||||
		this.client = client;
 | 
					 | 
				
			||||||
		this.jsonParser = JsonParserFactory.getJsonParser();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	protected void doHealthCheck(Health.Builder builder) throws Exception {
 | 
					 | 
				
			||||||
		Response response = this.client.performRequest(new Request("GET", "/_cluster/health/"));
 | 
					 | 
				
			||||||
		StatusLine statusLine = response.getStatusLine();
 | 
					 | 
				
			||||||
		if (statusLine.getStatusCode() != HttpStatus.SC_OK) {
 | 
					 | 
				
			||||||
			builder.down();
 | 
					 | 
				
			||||||
			builder.withDetail("statusCode", statusLine.getStatusCode());
 | 
					 | 
				
			||||||
			builder.withDetail("reasonPhrase", statusLine.getReasonPhrase());
 | 
					 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		try (InputStream inputStream = response.getEntity().getContent()) {
 | 
					 | 
				
			||||||
			doHealthCheck(builder, StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	private void doHealthCheck(Health.Builder builder, String json) {
 | 
					 | 
				
			||||||
		Map<String, Object> response = this.jsonParser.parseMap(json);
 | 
					 | 
				
			||||||
		String status = (String) response.get("status");
 | 
					 | 
				
			||||||
		if (RED_STATUS.equals(status)) {
 | 
					 | 
				
			||||||
			builder.outOfService();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		else {
 | 
					 | 
				
			||||||
			builder.up();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		builder.withDetails(response);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,143 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2012-2019 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.elasticsearch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.ByteArrayInputStream;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.apache.http.StatusLine;
 | 
				
			||||||
 | 
					import org.apache.http.entity.BasicHttpEntity;
 | 
				
			||||||
 | 
					import org.elasticsearch.client.Request;
 | 
				
			||||||
 | 
					import org.elasticsearch.client.Response;
 | 
				
			||||||
 | 
					import org.elasticsearch.client.RestClient;
 | 
				
			||||||
 | 
					import org.junit.jupiter.api.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.springframework.boot.actuate.health.Health;
 | 
				
			||||||
 | 
					import org.springframework.boot.actuate.health.Status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static org.assertj.core.api.Assertions.assertThat;
 | 
				
			||||||
 | 
					import static org.assertj.core.api.Assertions.entry;
 | 
				
			||||||
 | 
					import static org.mockito.ArgumentMatchers.any;
 | 
				
			||||||
 | 
					import static org.mockito.BDDMockito.given;
 | 
				
			||||||
 | 
					import static org.mockito.Mockito.mock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Tests for {@link ElasticsearchRestClientHealthIndicator}.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Artsiom Yudovin
 | 
				
			||||||
 | 
					 * @author Filip Hrisafov
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class ElasticsearchRestClientHealthIndicatorTests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private final RestClient restClient = mock(RestClient.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private final ElasticsearchRestClientHealthIndicator elasticsearchRestHealthIndicator = new ElasticsearchRestClientHealthIndicator(
 | 
				
			||||||
 | 
								this.restClient);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						void elasticsearchIsUp() throws IOException {
 | 
				
			||||||
 | 
							BasicHttpEntity httpEntity = new BasicHttpEntity();
 | 
				
			||||||
 | 
							httpEntity.setContent(new ByteArrayInputStream(createJsonResult(200, "green").getBytes()));
 | 
				
			||||||
 | 
							Response response = mock(Response.class);
 | 
				
			||||||
 | 
							StatusLine statusLine = mock(StatusLine.class);
 | 
				
			||||||
 | 
							given(statusLine.getStatusCode()).willReturn(200);
 | 
				
			||||||
 | 
							given(response.getStatusLine()).willReturn(statusLine);
 | 
				
			||||||
 | 
							given(response.getEntity()).willReturn(httpEntity);
 | 
				
			||||||
 | 
							given(this.restClient.performRequest(any(Request.class))).willReturn(response);
 | 
				
			||||||
 | 
							Health health = this.elasticsearchRestHealthIndicator.health();
 | 
				
			||||||
 | 
							assertThat(health.getStatus()).isEqualTo(Status.UP);
 | 
				
			||||||
 | 
							assertHealthDetailsWithStatus(health.getDetails(), "green");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						void elasticsearchWithYellowStatusIsUp() throws IOException {
 | 
				
			||||||
 | 
							BasicHttpEntity httpEntity = new BasicHttpEntity();
 | 
				
			||||||
 | 
							httpEntity.setContent(new ByteArrayInputStream(createJsonResult(200, "yellow").getBytes()));
 | 
				
			||||||
 | 
							Response response = mock(Response.class);
 | 
				
			||||||
 | 
							StatusLine statusLine = mock(StatusLine.class);
 | 
				
			||||||
 | 
							given(statusLine.getStatusCode()).willReturn(200);
 | 
				
			||||||
 | 
							given(response.getStatusLine()).willReturn(statusLine);
 | 
				
			||||||
 | 
							given(response.getEntity()).willReturn(httpEntity);
 | 
				
			||||||
 | 
							given(this.restClient.performRequest(any(Request.class))).willReturn(response);
 | 
				
			||||||
 | 
							Health health = this.elasticsearchRestHealthIndicator.health();
 | 
				
			||||||
 | 
							assertThat(health.getStatus()).isEqualTo(Status.UP);
 | 
				
			||||||
 | 
							assertHealthDetailsWithStatus(health.getDetails(), "yellow");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						void elasticsearchIsDown() throws IOException {
 | 
				
			||||||
 | 
							given(this.restClient.performRequest(any(Request.class))).willThrow(new IOException("Couldn't connect"));
 | 
				
			||||||
 | 
							Health health = this.elasticsearchRestHealthIndicator.health();
 | 
				
			||||||
 | 
							assertThat(health.getStatus()).isEqualTo(Status.DOWN);
 | 
				
			||||||
 | 
							assertThat(health.getDetails()).contains(entry("error", "java.io.IOException: Couldn't connect"));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						void elasticsearchIsDownByResponseCode() throws IOException {
 | 
				
			||||||
 | 
							Response response = mock(Response.class);
 | 
				
			||||||
 | 
							StatusLine statusLine = mock(StatusLine.class);
 | 
				
			||||||
 | 
							given(statusLine.getStatusCode()).willReturn(500);
 | 
				
			||||||
 | 
							given(statusLine.getReasonPhrase()).willReturn("Internal server error");
 | 
				
			||||||
 | 
							given(response.getStatusLine()).willReturn(statusLine);
 | 
				
			||||||
 | 
							given(this.restClient.performRequest(any(Request.class))).willReturn(response);
 | 
				
			||||||
 | 
							Health health = this.elasticsearchRestHealthIndicator.health();
 | 
				
			||||||
 | 
							assertThat(health.getStatus()).isEqualTo(Status.DOWN);
 | 
				
			||||||
 | 
							assertThat(health.getDetails()).contains(entry("statusCode", 500),
 | 
				
			||||||
 | 
									entry("reasonPhrase", "Internal server error"));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						void elasticsearchIsOutOfServiceByStatus() throws IOException {
 | 
				
			||||||
 | 
							BasicHttpEntity httpEntity = new BasicHttpEntity();
 | 
				
			||||||
 | 
							httpEntity.setContent(new ByteArrayInputStream(createJsonResult(200, "red").getBytes()));
 | 
				
			||||||
 | 
							Response response = mock(Response.class);
 | 
				
			||||||
 | 
							StatusLine statusLine = mock(StatusLine.class);
 | 
				
			||||||
 | 
							given(statusLine.getStatusCode()).willReturn(200);
 | 
				
			||||||
 | 
							given(response.getStatusLine()).willReturn(statusLine);
 | 
				
			||||||
 | 
							given(response.getEntity()).willReturn(httpEntity);
 | 
				
			||||||
 | 
							given(this.restClient.performRequest(any(Request.class))).willReturn(response);
 | 
				
			||||||
 | 
							Health health = this.elasticsearchRestHealthIndicator.health();
 | 
				
			||||||
 | 
							assertThat(health.getStatus()).isEqualTo(Status.OUT_OF_SERVICE);
 | 
				
			||||||
 | 
							assertHealthDetailsWithStatus(health.getDetails(), "red");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private void assertHealthDetailsWithStatus(Map<String, Object> details, String status) {
 | 
				
			||||||
 | 
							assertThat(details).contains(entry("cluster_name", "elasticsearch"), entry("status", status),
 | 
				
			||||||
 | 
									entry("timed_out", false), entry("number_of_nodes", 1), entry("number_of_data_nodes", 1),
 | 
				
			||||||
 | 
									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));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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}";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -42,6 +42,7 @@ import static org.mockito.Mockito.mock;
 | 
				
			||||||
 * @author Artsiom Yudovin
 | 
					 * @author Artsiom Yudovin
 | 
				
			||||||
 * @author Filip Hrisafov
 | 
					 * @author Filip Hrisafov
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					@Deprecated
 | 
				
			||||||
class ElasticsearchRestHealthIndicatorTests {
 | 
					class ElasticsearchRestHealthIndicatorTests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private final RestClient restClient = mock(RestClient.class);
 | 
						private final RestClient restClient = mock(RestClient.class);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,7 +22,9 @@ import org.springframework.boot.autoconfigure.AutoConfiguration;
 | 
				
			||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 | 
					import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 | 
				
			||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 | 
					import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 | 
				
			||||||
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientConfigurations.RestClientBuilderConfiguration;
 | 
					import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientConfigurations.RestClientBuilderConfiguration;
 | 
				
			||||||
 | 
					import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientConfigurations.RestClientConfiguration;
 | 
				
			||||||
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientConfigurations.RestClientSnifferConfiguration;
 | 
					import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientConfigurations.RestClientSnifferConfiguration;
 | 
				
			||||||
 | 
					import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientConfigurations.RestClientWithRestHighLevelClientConfiguration;
 | 
				
			||||||
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientConfigurations.RestHighLevelClientConfiguration;
 | 
					import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientConfigurations.RestHighLevelClientConfiguration;
 | 
				
			||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
 | 
					import org.springframework.boot.context.properties.EnableConfigurationProperties;
 | 
				
			||||||
import org.springframework.context.annotation.Import;
 | 
					import org.springframework.context.annotation.Import;
 | 
				
			||||||
| 
						 | 
					@ -40,6 +42,7 @@ import org.springframework.context.annotation.Import;
 | 
				
			||||||
@EnableConfigurationProperties({ ElasticsearchProperties.class, ElasticsearchRestClientProperties.class,
 | 
					@EnableConfigurationProperties({ ElasticsearchProperties.class, ElasticsearchRestClientProperties.class,
 | 
				
			||||||
		DeprecatedElasticsearchRestClientProperties.class })
 | 
							DeprecatedElasticsearchRestClientProperties.class })
 | 
				
			||||||
@Import({ RestClientBuilderConfiguration.class, RestHighLevelClientConfiguration.class,
 | 
					@Import({ RestClientBuilderConfiguration.class, RestHighLevelClientConfiguration.class,
 | 
				
			||||||
 | 
							RestClientWithRestHighLevelClientConfiguration.class, RestClientConfiguration.class,
 | 
				
			||||||
		RestClientSnifferConfiguration.class })
 | 
							RestClientSnifferConfiguration.class })
 | 
				
			||||||
public class ElasticsearchRestClientAutoConfiguration {
 | 
					public class ElasticsearchRestClientAutoConfiguration {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,6 +36,7 @@ import org.elasticsearch.client.sniff.SnifferBuilder;
 | 
				
			||||||
import org.springframework.beans.factory.ObjectProvider;
 | 
					import org.springframework.beans.factory.ObjectProvider;
 | 
				
			||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 | 
					import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 | 
				
			||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 | 
					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.autoconfigure.condition.ConditionalOnSingleCandidate;
 | 
				
			||||||
import org.springframework.boot.context.properties.PropertyMapper;
 | 
					import org.springframework.boot.context.properties.PropertyMapper;
 | 
				
			||||||
import org.springframework.context.annotation.Bean;
 | 
					import org.springframework.context.annotation.Bean;
 | 
				
			||||||
| 
						 | 
					@ -46,6 +47,7 @@ import org.springframework.util.StringUtils;
 | 
				
			||||||
 * Elasticsearch rest client configurations.
 | 
					 * Elasticsearch rest client configurations.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author Stephane Nicoll
 | 
					 * @author Stephane Nicoll
 | 
				
			||||||
 | 
					 * @author Filip Hrisafov
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class ElasticsearchRestClientConfigurations {
 | 
					class ElasticsearchRestClientConfigurations {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -126,16 +128,41 @@ class ElasticsearchRestClientConfigurations {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@SuppressWarnings("deprecation")
 | 
						@SuppressWarnings("deprecation")
 | 
				
			||||||
	@Configuration(proxyBeanMethods = false)
 | 
						@Configuration(proxyBeanMethods = false)
 | 
				
			||||||
	@ConditionalOnClass(Sniffer.class)
 | 
						@ConditionalOnClass(org.elasticsearch.client.RestHighLevelClient.class)
 | 
				
			||||||
	@ConditionalOnSingleCandidate(org.elasticsearch.client.RestHighLevelClient.class)
 | 
						@ConditionalOnSingleCandidate(org.elasticsearch.client.RestHighLevelClient.class)
 | 
				
			||||||
 | 
						@ConditionalOnMissingBean(RestClient.class)
 | 
				
			||||||
 | 
						static class RestClientWithRestHighLevelClientConfiguration {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							@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 {
 | 
						static class RestClientSnifferConfiguration {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		@Bean
 | 
							@Bean
 | 
				
			||||||
		@ConditionalOnMissingBean
 | 
							@ConditionalOnMissingBean
 | 
				
			||||||
		Sniffer elasticsearchSniffer(org.elasticsearch.client.RestHighLevelClient client,
 | 
							@SuppressWarnings("deprecation")
 | 
				
			||||||
				ElasticsearchRestClientProperties properties,
 | 
							Sniffer elasticsearchSniffer(RestClient client, ElasticsearchRestClientProperties properties,
 | 
				
			||||||
				DeprecatedElasticsearchRestClientProperties deprecatedProperties) {
 | 
									DeprecatedElasticsearchRestClientProperties deprecatedProperties) {
 | 
				
			||||||
			SnifferBuilder builder = Sniffer.builder(client.getLowLevelClient());
 | 
								SnifferBuilder builder = Sniffer.builder(client);
 | 
				
			||||||
			PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
 | 
								PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
 | 
				
			||||||
			Duration interval = deprecatedProperties.isCustomized() ? deprecatedProperties.getSniffer().getInterval()
 | 
								Duration interval = deprecatedProperties.isCustomized() ? deprecatedProperties.getSniffer().getInterval()
 | 
				
			||||||
					: properties.getSniffer().getInterval();
 | 
										: properties.getSniffer().getInterval();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,19 +16,26 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package org.springframework.boot.autoconfigure.elasticsearch;
 | 
					package org.springframework.boot.autoconfigure.elasticsearch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.InputStream;
 | 
				
			||||||
import java.time.Duration;
 | 
					import java.time.Duration;
 | 
				
			||||||
import java.util.HashMap;
 | 
					import java.util.HashMap;
 | 
				
			||||||
import java.util.Map;
 | 
					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.get.GetRequest;
 | 
				
			||||||
import org.elasticsearch.action.index.IndexRequest;
 | 
					import org.elasticsearch.action.index.IndexRequest;
 | 
				
			||||||
 | 
					import org.elasticsearch.client.Request;
 | 
				
			||||||
import org.elasticsearch.client.RequestOptions;
 | 
					import org.elasticsearch.client.RequestOptions;
 | 
				
			||||||
 | 
					import org.elasticsearch.client.Response;
 | 
				
			||||||
 | 
					import org.elasticsearch.client.RestClient;
 | 
				
			||||||
import org.junit.jupiter.api.Test;
 | 
					import org.junit.jupiter.api.Test;
 | 
				
			||||||
import org.testcontainers.elasticsearch.ElasticsearchContainer;
 | 
					import org.testcontainers.elasticsearch.ElasticsearchContainer;
 | 
				
			||||||
import org.testcontainers.junit.jupiter.Container;
 | 
					import org.testcontainers.junit.jupiter.Container;
 | 
				
			||||||
import org.testcontainers.junit.jupiter.Testcontainers;
 | 
					import org.testcontainers.junit.jupiter.Testcontainers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
 | 
					import org.springframework.boot.autoconfigure.AutoConfigurations;
 | 
				
			||||||
 | 
					import org.springframework.boot.test.context.FilteredClassLoader;
 | 
				
			||||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
 | 
					import org.springframework.boot.test.context.runner.ApplicationContextRunner;
 | 
				
			||||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
 | 
					import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,6 +47,7 @@ import static org.assertj.core.api.Assertions.assertThat;
 | 
				
			||||||
 * @author Brian Clozel
 | 
					 * @author Brian Clozel
 | 
				
			||||||
 * @author Vedran Pavic
 | 
					 * @author Vedran Pavic
 | 
				
			||||||
 * @author Evgeniy Cheban
 | 
					 * @author Evgeniy Cheban
 | 
				
			||||||
 | 
					 * @author Filip Hrisafov
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@Testcontainers(disabledWithoutDocker = true)
 | 
					@Testcontainers(disabledWithoutDocker = true)
 | 
				
			||||||
class ElasticsearchRestClientAutoConfigurationIntegrationTests {
 | 
					class ElasticsearchRestClientAutoConfigurationIntegrationTests {
 | 
				
			||||||
| 
						 | 
					@ -53,7 +61,7 @@ class ElasticsearchRestClientAutoConfigurationIntegrationTests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	@SuppressWarnings("deprecation")
 | 
						@SuppressWarnings("deprecation")
 | 
				
			||||||
	void restClientCanQueryElasticsearchNode() {
 | 
						void restHighLevelClientCanQueryElasticsearchNode() {
 | 
				
			||||||
		this.contextRunner
 | 
							this.contextRunner
 | 
				
			||||||
				.withPropertyValues("spring.elasticsearch.uris=" + elasticsearch.getHttpHostAddress(),
 | 
									.withPropertyValues("spring.elasticsearch.uris=" + elasticsearch.getHttpHostAddress(),
 | 
				
			||||||
						"spring.elasticsearch.connection-timeout=120s", "spring.elasticsearch.socket-timeout=120s")
 | 
											"spring.elasticsearch.connection-timeout=120s", "spring.elasticsearch.socket-timeout=120s")
 | 
				
			||||||
| 
						 | 
					@ -70,4 +78,43 @@ 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();
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						@SuppressWarnings("deprecation")
 | 
				
			||||||
 | 
						void restClientCanQueryElasticsearchNodeWithoutHighLevelClient() {
 | 
				
			||||||
 | 
							this.contextRunner.withClassLoader(new FilteredClassLoader(org.elasticsearch.client.RestHighLevelClient.class))
 | 
				
			||||||
 | 
									.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/3");
 | 
				
			||||||
 | 
										index.setJsonEntity("{" + "  \"a\": \"alpha\"," + "  \"b\": \"bravo\"" + "}");
 | 
				
			||||||
 | 
										client.performRequest(index);
 | 
				
			||||||
 | 
										Request getRequest = new Request("GET", "/test/_doc/3");
 | 
				
			||||||
 | 
										Response response = client.performRequest(getRequest);
 | 
				
			||||||
 | 
										try (InputStream input = response.getEntity().getContent()) {
 | 
				
			||||||
 | 
											JsonNode result = new ObjectMapper().readTree(input);
 | 
				
			||||||
 | 
											assertThat(result.path("found").asBoolean()).isTrue();
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -63,17 +63,20 @@ class ElasticsearchRestClientAutoConfigurationTests {
 | 
				
			||||||
			.withConfiguration(AutoConfigurations.of(ElasticsearchRestClientAutoConfiguration.class));
 | 
								.withConfiguration(AutoConfigurations.of(ElasticsearchRestClientAutoConfiguration.class));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	void configureShouldOnlyCreateHighLevelRestClient() {
 | 
						void configureShouldCreateHighLevelAndLowLevelRestClient() {
 | 
				
			||||||
		this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(RestClient.class)
 | 
							this.contextRunner.run((context) -> {
 | 
				
			||||||
				.hasSingleBean(RestClientBuilder.class)
 | 
								assertThat(context).hasSingleBean(RestClient.class)
 | 
				
			||||||
				.hasSingleBean(org.elasticsearch.client.RestHighLevelClient.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
 | 
						@Test
 | 
				
			||||||
	void configureWithoutRestHighLevelClientShouldOnlyCreateRestClientBuilder() {
 | 
						void configureWithoutRestHighLevelClientShouldOnlyCreateRestClientBuilderAndRestClient() {
 | 
				
			||||||
		this.contextRunner.withClassLoader(new FilteredClassLoader(org.elasticsearch.client.RestHighLevelClient.class))
 | 
							this.contextRunner.withClassLoader(new FilteredClassLoader(org.elasticsearch.client.RestHighLevelClient.class))
 | 
				
			||||||
				.run((context) -> assertThat(context).doesNotHaveBean(RestClient.class)
 | 
									.run((context) -> assertThat(context).hasSingleBean(RestClient.class)
 | 
				
			||||||
						.doesNotHaveBean(org.elasticsearch.client.RestHighLevelClient.class)
 | 
											.doesNotHaveBean(org.elasticsearch.client.RestHighLevelClient.class)
 | 
				
			||||||
						.hasSingleBean(RestClientBuilder.class));
 | 
											.hasSingleBean(RestClientBuilder.class));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -87,25 +90,52 @@ class ElasticsearchRestClientAutoConfigurationTests {
 | 
				
			||||||
						.hasBean("customRestClient"));
 | 
											.hasBean("customRestClient"));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						void configureWhenCustomRestHighLevelClientIsNotPresent() {
 | 
				
			||||||
 | 
							this.contextRunner.withClassLoader(new FilteredClassLoader(org.elasticsearch.client.RestHighLevelClient.class))
 | 
				
			||||||
 | 
									.run((context) -> assertThat(context)
 | 
				
			||||||
 | 
											.doesNotHaveBean(org.elasticsearch.client.RestHighLevelClient.class)
 | 
				
			||||||
 | 
											.hasSingleBean(RestClient.class).hasSingleBean(RestClientBuilder.class));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	void configureWhenCustomRestHighLevelClientShouldBackOff() {
 | 
						void configureWhenCustomRestHighLevelClientShouldBackOff() {
 | 
				
			||||||
		this.contextRunner.withUserConfiguration(CustomRestHighLevelClientConfiguration.class).run(
 | 
							this.contextRunner.withUserConfiguration(CustomRestHighLevelClientConfiguration.class)
 | 
				
			||||||
				(context) -> assertThat(context).hasSingleBean(org.elasticsearch.client.RestHighLevelClient.class));
 | 
									.run((context) -> assertThat(context).hasSingleBean(org.elasticsearch.client.RestHighLevelClient.class)
 | 
				
			||||||
 | 
											.hasSingleBean(RestClient.class).hasBean("elasticsearchRestClient"));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						void configureWhenCustomRestHighLevelClientAndRestClientWithRestHighLevelClientShouldBackOff() {
 | 
				
			||||||
 | 
							this.contextRunner.withUserConfiguration(CustomRestHighLevelClientWithRestClientConfiguration.class)
 | 
				
			||||||
 | 
									.run((context) -> assertThat(context).hasSingleBean(org.elasticsearch.client.RestHighLevelClient.class)
 | 
				
			||||||
 | 
											.hasSingleBean(RestClient.class).hasBean("customRestClient"));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	void configureWhenDefaultRestClientShouldCreateWhenNoUniqueRestHighLevelClient() {
 | 
						void configureWhenDefaultRestClientShouldCreateWhenNoUniqueRestHighLevelClient() {
 | 
				
			||||||
		this.contextRunner.withUserConfiguration(TwoCustomRestHighLevelClientConfiguration.class).run((context) -> {
 | 
							this.contextRunner.withUserConfiguration(TwoCustomRestHighLevelClientConfiguration.class).run((context) -> {
 | 
				
			||||||
 | 
								assertThat(context).doesNotHaveBean(RestClient.class);
 | 
				
			||||||
			Map<String, org.elasticsearch.client.RestHighLevelClient> restHighLevelClients = context
 | 
								Map<String, org.elasticsearch.client.RestHighLevelClient> restHighLevelClients = context
 | 
				
			||||||
					.getBeansOfType(org.elasticsearch.client.RestHighLevelClient.class);
 | 
										.getBeansOfType(org.elasticsearch.client.RestHighLevelClient.class);
 | 
				
			||||||
			assertThat(restHighLevelClients).hasSize(2);
 | 
								assertThat(restHighLevelClients).hasSize(2);
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						void configureWhenDefaultRestClientShouldCreateWhenNoUniqueRestClient() {
 | 
				
			||||||
 | 
							this.contextRunner.withClassLoader(new FilteredClassLoader(org.elasticsearch.client.RestHighLevelClient.class))
 | 
				
			||||||
 | 
									.withUserConfiguration(TwoCustomRestClientConfiguration.class).run((context) -> {
 | 
				
			||||||
 | 
										Map<String, RestClient> restClients = context.getBeansOfType(RestClient.class);
 | 
				
			||||||
 | 
										assertThat(restClients).hasSize(2);
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	void configureWhenBuilderCustomizerShouldApply() {
 | 
						void configureWhenBuilderCustomizerShouldApply() {
 | 
				
			||||||
		this.contextRunner.withUserConfiguration(BuilderCustomizerConfiguration.class).run((context) -> {
 | 
							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
 | 
								org.elasticsearch.client.RestHighLevelClient restClient = context
 | 
				
			||||||
					.getBean(org.elasticsearch.client.RestHighLevelClient.class);
 | 
										.getBean(org.elasticsearch.client.RestHighLevelClient.class);
 | 
				
			||||||
			RestClient lowLevelClient = restClient.getLowLevelClient();
 | 
								RestClient lowLevelClient = restClient.getLowLevelClient();
 | 
				
			||||||
| 
						 | 
					@ -118,9 +148,8 @@ class ElasticsearchRestClientAutoConfigurationTests {
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	void configureWithNoTimeoutsApplyDefaults() {
 | 
						void configureWithNoTimeoutsApplyDefaults() {
 | 
				
			||||||
		this.contextRunner.run((context) -> {
 | 
							this.contextRunner.run((context) -> {
 | 
				
			||||||
			assertThat(context).hasSingleBean(org.elasticsearch.client.RestHighLevelClient.class);
 | 
								assertThat(context).hasSingleBean(RestClient.class);
 | 
				
			||||||
			org.elasticsearch.client.RestHighLevelClient restClient = context
 | 
								RestClient restClient = context.getBean(RestClient.class);
 | 
				
			||||||
					.getBean(org.elasticsearch.client.RestHighLevelClient.class);
 | 
					 | 
				
			||||||
			assertTimeouts(restClient, Duration.ofMillis(RestClientBuilder.DEFAULT_CONNECT_TIMEOUT_MILLIS),
 | 
								assertTimeouts(restClient, Duration.ofMillis(RestClientBuilder.DEFAULT_CONNECT_TIMEOUT_MILLIS),
 | 
				
			||||||
					Duration.ofMillis(RestClientBuilder.DEFAULT_SOCKET_TIMEOUT_MILLIS));
 | 
										Duration.ofMillis(RestClientBuilder.DEFAULT_SOCKET_TIMEOUT_MILLIS));
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
| 
						 | 
					@ -130,9 +159,8 @@ class ElasticsearchRestClientAutoConfigurationTests {
 | 
				
			||||||
	void configureWithLegacyCustomTimeouts() {
 | 
						void configureWithLegacyCustomTimeouts() {
 | 
				
			||||||
		this.contextRunner.withPropertyValues("spring.elasticsearch.rest.connection-timeout=15s",
 | 
							this.contextRunner.withPropertyValues("spring.elasticsearch.rest.connection-timeout=15s",
 | 
				
			||||||
				"spring.elasticsearch.rest.read-timeout=1m").run((context) -> {
 | 
									"spring.elasticsearch.rest.read-timeout=1m").run((context) -> {
 | 
				
			||||||
					assertThat(context).hasSingleBean(org.elasticsearch.client.RestHighLevelClient.class);
 | 
										assertThat(context).hasSingleBean(RestClient.class);
 | 
				
			||||||
					org.elasticsearch.client.RestHighLevelClient restClient = context
 | 
										RestClient restClient = context.getBean(RestClient.class);
 | 
				
			||||||
							.getBean(org.elasticsearch.client.RestHighLevelClient.class);
 | 
					 | 
				
			||||||
					assertTimeouts(restClient, Duration.ofSeconds(15), Duration.ofMinutes(1));
 | 
										assertTimeouts(restClient, Duration.ofSeconds(15), Duration.ofMinutes(1));
 | 
				
			||||||
				});
 | 
									});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -141,25 +169,23 @@ class ElasticsearchRestClientAutoConfigurationTests {
 | 
				
			||||||
	void configureWithCustomTimeouts() {
 | 
						void configureWithCustomTimeouts() {
 | 
				
			||||||
		this.contextRunner.withPropertyValues("spring.elasticsearch.connection-timeout=15s",
 | 
							this.contextRunner.withPropertyValues("spring.elasticsearch.connection-timeout=15s",
 | 
				
			||||||
				"spring.elasticsearch.socket-timeout=1m").run((context) -> {
 | 
									"spring.elasticsearch.socket-timeout=1m").run((context) -> {
 | 
				
			||||||
					assertThat(context).hasSingleBean(org.elasticsearch.client.RestHighLevelClient.class);
 | 
										assertThat(context).hasSingleBean(RestClient.class);
 | 
				
			||||||
					org.elasticsearch.client.RestHighLevelClient restClient = context
 | 
										RestClient restClient = context.getBean(RestClient.class);
 | 
				
			||||||
							.getBean(org.elasticsearch.client.RestHighLevelClient.class);
 | 
					 | 
				
			||||||
					assertTimeouts(restClient, Duration.ofSeconds(15), Duration.ofMinutes(1));
 | 
										assertTimeouts(restClient, Duration.ofSeconds(15), Duration.ofMinutes(1));
 | 
				
			||||||
				});
 | 
									});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static void assertTimeouts(org.elasticsearch.client.RestHighLevelClient restClient, Duration connectTimeout,
 | 
						private static void assertTimeouts(RestClient restClient, Duration connectTimeout, Duration readTimeout) {
 | 
				
			||||||
			Duration readTimeout) {
 | 
							assertThat(restClient).extracting("client.defaultConfig.socketTimeout")
 | 
				
			||||||
		assertThat(restClient.getLowLevelClient()).extracting("client.defaultConfig.socketTimeout")
 | 
					 | 
				
			||||||
				.isEqualTo(Math.toIntExact(readTimeout.toMillis()));
 | 
									.isEqualTo(Math.toIntExact(readTimeout.toMillis()));
 | 
				
			||||||
		assertThat(restClient.getLowLevelClient()).extracting("client.defaultConfig.connectTimeout")
 | 
							assertThat(restClient).extracting("client.defaultConfig.connectTimeout")
 | 
				
			||||||
				.isEqualTo(Math.toIntExact(connectTimeout.toMillis()));
 | 
									.isEqualTo(Math.toIntExact(connectTimeout.toMillis()));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@ParameterizedPropertyPrefixTest
 | 
						@ParameterizedPropertyPrefixTest
 | 
				
			||||||
	void configureUriWithNoScheme(String prefix) {
 | 
						void configureUriWithNoScheme(String prefix) {
 | 
				
			||||||
		this.contextRunner.withPropertyValues(prefix + "uris=localhost:9876").run((context) -> {
 | 
							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))
 | 
								assertThat(client.getNodes().stream().map(Node::getHost).map(HttpHost::toString))
 | 
				
			||||||
					.containsExactly("http://localhost:9876");
 | 
										.containsExactly("http://localhost:9876");
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
| 
						 | 
					@ -168,7 +194,7 @@ class ElasticsearchRestClientAutoConfigurationTests {
 | 
				
			||||||
	@ParameterizedPropertyPrefixTest
 | 
						@ParameterizedPropertyPrefixTest
 | 
				
			||||||
	void configureUriWithUsernameOnly(String prefix) {
 | 
						void configureUriWithUsernameOnly(String prefix) {
 | 
				
			||||||
		this.contextRunner.withPropertyValues(prefix + "uris=http://user@localhost:9200").run((context) -> {
 | 
							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))
 | 
								assertThat(client.getNodes().stream().map(Node::getHost).map(HttpHost::toString))
 | 
				
			||||||
					.containsExactly("http://localhost:9200");
 | 
										.containsExactly("http://localhost:9200");
 | 
				
			||||||
			assertThat(client)
 | 
								assertThat(client)
 | 
				
			||||||
| 
						 | 
					@ -184,7 +210,7 @@ class ElasticsearchRestClientAutoConfigurationTests {
 | 
				
			||||||
	@ParameterizedPropertyPrefixTest
 | 
						@ParameterizedPropertyPrefixTest
 | 
				
			||||||
	void configureUriWithUsernameAndEmptyPassword(String prefix) {
 | 
						void configureUriWithUsernameAndEmptyPassword(String prefix) {
 | 
				
			||||||
		this.contextRunner.withPropertyValues(prefix + "uris=http://user:@localhost:9200").run((context) -> {
 | 
							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))
 | 
								assertThat(client.getNodes().stream().map(Node::getHost).map(HttpHost::toString))
 | 
				
			||||||
					.containsExactly("http://localhost:9200");
 | 
										.containsExactly("http://localhost:9200");
 | 
				
			||||||
			assertThat(client)
 | 
								assertThat(client)
 | 
				
			||||||
| 
						 | 
					@ -201,8 +227,7 @@ class ElasticsearchRestClientAutoConfigurationTests {
 | 
				
			||||||
	void configureUriWithUsernameAndPasswordWhenUsernameAndPasswordPropertiesSet(String prefix) {
 | 
						void configureUriWithUsernameAndPasswordWhenUsernameAndPasswordPropertiesSet(String prefix) {
 | 
				
			||||||
		this.contextRunner.withPropertyValues(prefix + "uris=http://user:password@localhost:9200,localhost:9201",
 | 
							this.contextRunner.withPropertyValues(prefix + "uris=http://user:password@localhost:9200,localhost:9201",
 | 
				
			||||||
				prefix + "username=admin", prefix + "password=admin").run((context) -> {
 | 
									prefix + "username=admin", prefix + "password=admin").run((context) -> {
 | 
				
			||||||
					RestClient client = context.getBean(org.elasticsearch.client.RestHighLevelClient.class)
 | 
										RestClient client = context.getBean(RestClient.class);
 | 
				
			||||||
							.getLowLevelClient();
 | 
					 | 
				
			||||||
					assertThat(client.getNodes().stream().map(Node::getHost).map(HttpHost::toString))
 | 
										assertThat(client.getNodes().stream().map(Node::getHost).map(HttpHost::toString))
 | 
				
			||||||
							.containsExactly("http://localhost:9200", "http://localhost:9201");
 | 
												.containsExactly("http://localhost:9200", "http://localhost:9201");
 | 
				
			||||||
					assertThat(client)
 | 
										assertThat(client)
 | 
				
			||||||
| 
						 | 
					@ -224,7 +249,7 @@ class ElasticsearchRestClientAutoConfigurationTests {
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	void configureWithCustomPathPrefix() {
 | 
						void configureWithCustomPathPrefix() {
 | 
				
			||||||
		this.contextRunner.withPropertyValues("spring.elasticsearch.path-prefix=/some/prefix").run((context) -> {
 | 
							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");
 | 
								assertThat(client).extracting("pathPrefix").isEqualTo("/some/prefix");
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -233,19 +258,21 @@ class ElasticsearchRestClientAutoConfigurationTests {
 | 
				
			||||||
	void configureWithoutSnifferLibraryShouldNotCreateSniffer() {
 | 
						void configureWithoutSnifferLibraryShouldNotCreateSniffer() {
 | 
				
			||||||
		this.contextRunner.withClassLoader(new FilteredClassLoader("org.elasticsearch.client.sniff"))
 | 
							this.contextRunner.withClassLoader(new FilteredClassLoader("org.elasticsearch.client.sniff"))
 | 
				
			||||||
				.run((context) -> assertThat(context).hasSingleBean(org.elasticsearch.client.RestHighLevelClient.class)
 | 
									.run((context) -> assertThat(context).hasSingleBean(org.elasticsearch.client.RestHighLevelClient.class)
 | 
				
			||||||
						.doesNotHaveBean(Sniffer.class));
 | 
											.hasSingleBean(RestClient.class).doesNotHaveBean(Sniffer.class));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	void configureShouldCreateSnifferUsingRestHighLevelClient() {
 | 
						void configureShouldCreateSnifferUsingRestClient() {
 | 
				
			||||||
		this.contextRunner.run((context) -> {
 | 
							this.contextRunner.withClassLoader(new FilteredClassLoader(org.elasticsearch.client.RestHighLevelClient.class))
 | 
				
			||||||
			assertThat(context).hasSingleBean(Sniffer.class);
 | 
									.run((context) -> {
 | 
				
			||||||
			assertThat(context.getBean(Sniffer.class)).hasFieldOrPropertyWithValue("restClient",
 | 
										assertThat(context).hasSingleBean(Sniffer.class);
 | 
				
			||||||
					context.getBean(org.elasticsearch.client.RestHighLevelClient.class).getLowLevelClient());
 | 
										assertThat(context.getBean(Sniffer.class)).hasFieldOrPropertyWithValue("restClient",
 | 
				
			||||||
			// Validate shutdown order as the sniffer must be shutdown before the client
 | 
												context.getBean(RestClient.class));
 | 
				
			||||||
			assertThat(context.getBeanFactory().getDependentBeans("elasticsearchRestHighLevelClient"))
 | 
										// Validate shutdown order as the sniffer must be shutdown before the
 | 
				
			||||||
					.contains("elasticsearchSniffer");
 | 
										// client
 | 
				
			||||||
		});
 | 
										assertThat(context.getBeanFactory().getDependentBeans("elasticsearchRestClient"))
 | 
				
			||||||
 | 
												.contains("elasticsearchSniffer");
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@ParameterizedSnifferPropertyPrefixTest
 | 
						@ParameterizedSnifferPropertyPrefixTest
 | 
				
			||||||
| 
						 | 
					@ -309,6 +336,21 @@ class ElasticsearchRestClientAutoConfigurationTests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@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)
 | 
						@Configuration(proxyBeanMethods = false)
 | 
				
			||||||
	static class TwoCustomRestHighLevelClientConfiguration {
 | 
						static class TwoCustomRestHighLevelClientConfiguration {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -334,6 +376,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
 | 
						@ParameterizedTest
 | 
				
			||||||
	@Target(ElementType.METHOD)
 | 
						@Target(ElementType.METHOD)
 | 
				
			||||||
	@Retention(RetentionPolicy.RUNTIME)
 | 
						@Retention(RetentionPolicy.RUNTIME)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -269,14 +269,14 @@ You can use `spring.elasticsearch.*` properties to further tune how the clients
 | 
				
			||||||
----
 | 
					----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[data.nosql.elasticsearch.connecting-using-rest.restclient]]
 | 
					[[data.nosql.elasticsearch.connecting-using-rest.restclient]]
 | 
				
			||||||
===== Connecting to Elasticsearch using RestHighLevelClient
 | 
					===== Connecting to Elasticsearch using RestClient
 | 
				
			||||||
If you have `elasticsearch-rest-high-level-client` on the classpath, Spring Boot will auto-configure and register a `RestHighLevelClient` bean.
 | 
					If you have `elasticsearch-rest-client` on the classpath, Spring Boot will auto-configure and register a `RestClient` 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.
 | 
					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 its registration, define a `RestClientBuilder` bean.
 | 
					To take full control over its registration, 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`.
 | 
					NOTE: If you have `elasticsearch-rest-high-level-client` on the classpath a `RestHighLevelClient` bean will be exposed as well
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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:
 | 
					You can further tune how `Sniffer` is configured, as shown in the following example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[source,yaml,indent=0,subs="verbatim",configprops,configblocks]
 | 
					[source,yaml,indent=0,subs="verbatim",configprops,configblocks]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue