Skip to content

Commit 1d73d4e

Browse files
committed
Remove Elasticsearch RestClient auto-configuration
Prior to this commit, Spring Boot would auto-configure both Elasticsearch variants: `RestClient` ("Low Level" client) and `RestHighLevelClient` ("High Level" client). Since one can be derived from the other, this would create complex and unclear situations depending on what developers provided with their configuration. `RestHighLevelClient` is mostly for actual use of the Elasticsearch API, with support for specific methods and (de)serialization. On the other hand, `RestClient` is merely wrapping the Apache HTTP client for load-balancing support and low level HTTP features. This commit completely removes the support for `RestClient` in Spring Boot and now requires the presence of the `org.elasticsearch.client:elasticsearch-rest-high-level-client` dependency for REST client support with Elasticsearch. Closes gh-22358
1 parent 016c46c commit 1d73d4e

File tree

6 files changed

+191
-281
lines changed

6 files changed

+191
-281
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticSearchRestHealthContributorAutoConfiguration.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.Map;
2020

2121
import org.elasticsearch.client.RestClient;
22+
import org.elasticsearch.client.RestHighLevelClient;
2223

2324
import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthContributorConfiguration;
2425
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
@@ -41,16 +42,16 @@
4142
* @since 2.1.1
4243
*/
4344
@Configuration(proxyBeanMethods = false)
44-
@ConditionalOnClass(RestClient.class)
45-
@ConditionalOnBean(RestClient.class)
45+
@ConditionalOnClass(RestHighLevelClient.class)
46+
@ConditionalOnBean(RestHighLevelClient.class)
4647
@ConditionalOnEnabledHealthIndicator("elasticsearch")
4748
@AutoConfigureAfter(ElasticsearchRestClientAutoConfiguration.class)
4849
public class ElasticSearchRestHealthContributorAutoConfiguration
49-
extends CompositeHealthContributorConfiguration<ElasticsearchRestHealthIndicator, RestClient> {
50+
extends CompositeHealthContributorConfiguration<ElasticsearchRestHealthIndicator, RestHighLevelClient> {
5051

5152
@Bean
5253
@ConditionalOnMissingBean(name = { "elasticsearchHealthIndicator", "elasticsearchHealthContributor" })
53-
public HealthContributor elasticsearchHealthContributor(Map<String, RestClient> clients) {
54+
public HealthContributor elasticsearchHealthContributor(Map <String, RestHighLevelClient> clients) {
5455
return createContributor(clients);
5556
}
5657

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchRestHealthIndicator.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
2525
import org.elasticsearch.client.Request;
2626
import org.elasticsearch.client.Response;
2727
import org.elasticsearch.client.RestClient;
28+
import org.elasticsearch.client.RestHighLevelClient;
2829

2930
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
3031
import org.springframework.boot.actuate.health.Health;
@@ -49,6 +50,10 @@ public class ElasticsearchRestHealthIndicator extends AbstractHealthIndicator {
4950

5051
private final JsonParser jsonParser;
5152

53+
public ElasticsearchRestHealthIndicator(RestHighLevelClient client) {
54+
this(client.getLowLevelClient());
55+
}
56+
5257
public ElasticsearchRestHealthIndicator(RestClient client) {
5358
super("Elasticsearch health check failed");
5459
this.client = client;

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientAutoConfiguration.java

Lines changed: 152 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,30 @@
1616

1717
package org.springframework.boot.autoconfigure.elasticsearch;
1818

19+
import java.net.URI;
20+
import java.net.URISyntaxException;
21+
import java.time.Duration;
22+
23+
import org.apache.http.HttpHost;
24+
import org.apache.http.auth.AuthScope;
25+
import org.apache.http.auth.Credentials;
26+
import org.apache.http.auth.UsernamePasswordCredentials;
27+
import org.apache.http.client.config.RequestConfig;
28+
import org.apache.http.impl.client.BasicCredentialsProvider;
29+
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
1930
import org.elasticsearch.client.RestClient;
31+
import org.elasticsearch.client.RestClientBuilder;
32+
import org.elasticsearch.client.RestHighLevelClient;
2033

34+
import org.springframework.beans.factory.ObjectProvider;
2135
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
2236
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
37+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2338
import org.springframework.boot.context.properties.EnableConfigurationProperties;
39+
import org.springframework.boot.context.properties.PropertyMapper;
40+
import org.springframework.context.annotation.Bean;
2441
import org.springframework.context.annotation.Configuration;
25-
import org.springframework.context.annotation.Import;
42+
import org.springframework.util.StringUtils;
2643

2744
/**
2845
* {@link EnableAutoConfiguration Auto-configuration} for Elasticsearch REST clients.
@@ -32,11 +49,141 @@
3249
* @since 2.1.0
3350
*/
3451
@Configuration(proxyBeanMethods = false)
35-
@ConditionalOnClass(RestClient.class)
52+
@ConditionalOnClass(RestHighLevelClient.class)
3653
@EnableConfigurationProperties(ElasticsearchRestClientProperties.class)
37-
@Import({ ElasticsearchRestClientConfigurations.RestClientBuilderConfiguration.class,
38-
ElasticsearchRestClientConfigurations.RestHighLevelClientConfiguration.class,
39-
ElasticsearchRestClientConfigurations.RestClientFallbackConfiguration.class })
4054
public class ElasticsearchRestClientAutoConfiguration {
4155

56+
@Configuration(proxyBeanMethods = false)
57+
@ConditionalOnMissingBean(RestClientBuilder.class)
58+
static class RestClientBuilderConfiguration {
59+
60+
@Bean
61+
RestClientBuilderCustomizer defaultRestClientBuilderCustomizer(ElasticsearchRestClientProperties properties) {
62+
return new DefaultRestClientBuilderCustomizer(properties);
63+
}
64+
65+
@Bean
66+
RestClientBuilder elasticsearchRestClientBuilder(ElasticsearchRestClientProperties properties,
67+
ObjectProvider<RestClientBuilderCustomizer> builderCustomizers) {
68+
HttpHost[] hosts = properties.getUris().stream().map(this::createHttpHost).toArray(HttpHost[]::new);
69+
RestClientBuilder builder = RestClient.builder(hosts);
70+
builder.setHttpClientConfigCallback((httpClientBuilder) -> {
71+
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(httpClientBuilder));
72+
return httpClientBuilder;
73+
});
74+
builder.setRequestConfigCallback((requestConfigBuilder) -> {
75+
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(requestConfigBuilder));
76+
return requestConfigBuilder;
77+
});
78+
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
79+
return builder;
80+
}
81+
82+
private HttpHost createHttpHost(String uri) {
83+
try {
84+
return createHttpHost(URI.create(uri));
85+
}
86+
catch (IllegalArgumentException ex) {
87+
return HttpHost.create(uri);
88+
}
89+
}
90+
91+
private HttpHost createHttpHost(URI uri) {
92+
if (!StringUtils.hasLength(uri.getUserInfo())) {
93+
return HttpHost.create(uri.toString());
94+
}
95+
try {
96+
return HttpHost.create(new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(),
97+
uri.getQuery(), uri.getFragment()).toString());
98+
}
99+
catch (URISyntaxException ex) {
100+
throw new IllegalStateException(ex);
101+
}
102+
}
103+
104+
}
105+
106+
@Configuration(proxyBeanMethods = false)
107+
@ConditionalOnMissingBean(RestHighLevelClient.class)
108+
static class RestHighLevelClientConfiguration {
109+
110+
@Bean
111+
RestHighLevelClient elasticsearchRestHighLevelClient(RestClientBuilder restClientBuilder) {
112+
return new RestHighLevelClient(restClientBuilder);
113+
}
114+
115+
}
116+
117+
static class DefaultRestClientBuilderCustomizer implements RestClientBuilderCustomizer {
118+
119+
private static final PropertyMapper map = PropertyMapper.get();
120+
121+
private final ElasticsearchRestClientProperties properties;
122+
123+
DefaultRestClientBuilderCustomizer(ElasticsearchRestClientProperties properties) {
124+
this.properties = properties;
125+
}
126+
127+
@Override
128+
public void customize(RestClientBuilder builder) {
129+
}
130+
131+
@Override
132+
public void customize(HttpAsyncClientBuilder builder) {
133+
builder.setDefaultCredentialsProvider(new PropertiesCredentialsProvider(this.properties));
134+
}
135+
136+
@Override
137+
public void customize(RequestConfig.Builder builder) {
138+
map.from(this.properties::getConnectionTimeout).whenNonNull().asInt(Duration::toMillis)
139+
.to(builder::setConnectTimeout);
140+
map.from(this.properties::getReadTimeout).whenNonNull().asInt(Duration::toMillis)
141+
.to(builder::setSocketTimeout);
142+
}
143+
144+
}
145+
146+
private static class PropertiesCredentialsProvider extends BasicCredentialsProvider {
147+
148+
PropertiesCredentialsProvider(ElasticsearchRestClientProperties properties) {
149+
if (StringUtils.hasText(properties.getUsername())) {
150+
Credentials credentials = new UsernamePasswordCredentials(properties.getUsername(),
151+
properties.getPassword());
152+
setCredentials(AuthScope.ANY, credentials);
153+
}
154+
properties.getUris().stream().map(this::toUri).filter(this::hasUserInfo)
155+
.forEach(this::addUserInfoCredentials);
156+
}
157+
158+
private URI toUri(String uri) {
159+
try {
160+
return URI.create(uri);
161+
}
162+
catch (IllegalArgumentException ex) {
163+
return null;
164+
}
165+
}
166+
167+
private boolean hasUserInfo(URI uri) {
168+
return uri != null && StringUtils.hasLength(uri.getUserInfo());
169+
}
170+
171+
private void addUserInfoCredentials(URI uri) {
172+
AuthScope authScope = new AuthScope(uri.getHost(), uri.getPort());
173+
Credentials credentials = createUserInfoCredentials(uri.getUserInfo());
174+
setCredentials(authScope, credentials);
175+
}
176+
177+
private Credentials createUserInfoCredentials(String userInfo) {
178+
int delimiter = userInfo.indexOf(":");
179+
if (delimiter == -1) {
180+
return new UsernamePasswordCredentials(userInfo, null);
181+
}
182+
String username = userInfo.substring(0, delimiter);
183+
String password = userInfo.substring(delimiter + 1);
184+
return new UsernamePasswordCredentials(username, password);
185+
}
186+
187+
}
188+
42189
}

0 commit comments

Comments
 (0)