Skip to content

Commit

Permalink
Bring back Elasticsearch RestClient auto-configuration
Browse files Browse the repository at this point in the history
Prior to this commit, Spring Boot would only auto-configure
the `RestHighLevelClient` and `RestClientBuilder` if the `RestHighLevelClient`
was present. This was done in 1d73d4e.

This commit brings back the exposing of the `RestClient` bean in
Spring Boot when exposing the `RestHighLevelClient` or
when the `RestHighLevelClient` is not present.
It allows for using the Spring Boot auto configuration and its customizers
of the `RestClientBuilder` in a similar way as it is done for the `RestTeamplateBuilder`
and the `WebClient.Builder`.
Now the presence of the `org.elasticsearch.client:elasticsearch-rest-high-level-client` is optional.
This opens the door for potentially adding support to the new
[Elasticsearch Java Client](https://github.com/elastic/elasticsearch-java) that is based on the same `RestClient`
  • Loading branch information
filiphr committed Nov 12, 2021
1 parent 83e4430 commit 3e160a4
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 45 deletions.
Expand Up @@ -19,7 +19,6 @@
import java.util.Map;

import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;

import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthContributorConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
Expand All @@ -42,16 +41,16 @@
* @since 2.1.1
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestHighLevelClient.class)
@ConditionalOnBean(RestHighLevelClient.class)
@ConditionalOnClass(RestClient.class)
@ConditionalOnBean(RestClient.class)
@ConditionalOnEnabledHealthIndicator("elasticsearch")
@AutoConfigureAfter(ElasticsearchRestClientAutoConfiguration.class)
public class ElasticSearchRestHealthContributorAutoConfiguration
extends CompositeHealthContributorConfiguration<ElasticsearchRestHealthIndicator, RestHighLevelClient> {
extends CompositeHealthContributorConfiguration<ElasticsearchRestHealthIndicator, RestClient> {

@Bean
@ConditionalOnMissingBean(name = { "elasticsearchHealthIndicator", "elasticsearchHealthContributor" })
public HealthContributor elasticsearchHealthContributor(Map<String, RestHighLevelClient> clients) {
public HealthContributor elasticsearchHealthContributor(Map<String, RestClient> clients) {
return createContributor(clients);
}

Expand Down
Expand Up @@ -50,6 +50,13 @@ public class ElasticsearchRestHealthIndicator extends AbstractHealthIndicator {

private final JsonParser jsonParser;

/**
* Create a health indicator using the RestHighLevelClient.
* @param client the RestHighLevelClient that used to get the RestClient
* @deprecated since 2.7.0 for removal in 2.9.0 in favor of
* {@link #ElasticsearchRestHealthIndicator(RestClient)}
*/
@Deprecated
public ElasticsearchRestHealthIndicator(RestHighLevelClient client) {
this(client.getLowLevelClient());
}
Expand Down
Expand Up @@ -17,12 +17,11 @@
package org.springframework.boot.autoconfigure.elasticsearch;

import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
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.RestHighLevelClientConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
Expand All @@ -38,11 +37,10 @@
*/
@SuppressWarnings("deprecation")
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestHighLevelClient.class)
@ConditionalOnMissingBean(RestClient.class)
@ConditionalOnClass(RestClient.class)
@EnableConfigurationProperties({ ElasticsearchProperties.class, ElasticsearchRestClientProperties.class,
DeprecatedElasticsearchRestClientProperties.class })
@Import({ RestClientBuilderConfiguration.class, RestHighLevelClientConfiguration.class,
@Import({ RestClientBuilderConfiguration.class, RestHighLevelClientConfiguration.class, RestClientConfiguration.class,
RestClientSnifferConfiguration.class })
public class ElasticsearchRestClientAutoConfiguration {

Expand Down
Expand Up @@ -37,6 +37,7 @@
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;
Expand Down Expand Up @@ -112,27 +113,45 @@ private HttpHost createHttpHost(URI uri) {
}

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(RestHighLevelClient.class)
@ConditionalOnClass(RestHighLevelClient.class)
@ConditionalOnMissingBean({ RestHighLevelClient.class, RestClient.class })
static class RestHighLevelClientConfiguration {

@Bean
RestHighLevelClient elasticsearchRestHighLevelClient(RestClientBuilder restClientBuilder) {
return new RestHighLevelClient(restClientBuilder);
}

@Bean
RestClient elasticsearchRestClient(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(RestHighLevelClient.class)
@ConditionalOnSingleCandidate(RestClient.class)
static class RestClientSnifferConfiguration {

@Bean
@ConditionalOnMissingBean
@SuppressWarnings("deprecation")
Sniffer elasticsearchSniffer(RestHighLevelClient client, ElasticsearchRestClientProperties properties,
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();
Expand Down
Expand Up @@ -16,20 +16,27 @@

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.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.Test;
import org.testcontainers.elasticsearch.ElasticsearchContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;

Expand All @@ -41,6 +48,7 @@
* @author Brian Clozel
* @author Vedran Pavic
* @author Evgeniy Cheban
* @author Filip Hrisafov
*/
@Testcontainers(disabledWithoutDocker = true)
class ElasticsearchRestClientAutoConfigurationIntegrationTests {
Expand All @@ -53,7 +61,7 @@ class ElasticsearchRestClientAutoConfigurationIntegrationTests {
.withConfiguration(AutoConfigurations.of(ElasticsearchRestClientAutoConfiguration.class));

@Test
void restClientCanQueryElasticsearchNode() {
void restHighLevelClientCanQueryElasticsearchNode() {
this.contextRunner
.withPropertyValues("spring.elasticsearch.uris=" + elasticsearch.getHttpHostAddress(),
"spring.elasticsearch.connection-timeout=120s", "spring.elasticsearch.socket-timeout=120s")
Expand All @@ -69,4 +77,42 @@ void restClientCanQueryElasticsearchNode() {
});
}

@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
void restClientCanQueryElasticsearchNodeWithoutHighLevelClient() {
this.contextRunner.withClassLoader(new FilteredClassLoader(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();
}
});
}

}

0 comments on commit 3e160a4

Please sign in to comment.