Skip to content

Commit fb0fccf

Browse files
committed
Polish "Create RestClient from a RestHighLevelClient if available"
This significantly rework the auto-configuration to reflect the order in which things are expected. Rather than keeping a conceptual cycle between the builder and the two inner classes that are processed first, the configuration is now split in three parts: * The builder that is required and common * The configuration when the HighLevelClient is available * The RestClient configuration when that's not the case See gh-17488
1 parent 0e87b9c commit fb0fccf

File tree

4 files changed

+122
-137
lines changed

4 files changed

+122
-137
lines changed

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

Lines changed: 5 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -16,105 +16,27 @@
1616

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

19-
import org.apache.http.HttpHost;
20-
import org.apache.http.auth.AuthScope;
21-
import org.apache.http.auth.Credentials;
22-
import org.apache.http.auth.UsernamePasswordCredentials;
23-
import org.apache.http.client.CredentialsProvider;
24-
import org.apache.http.impl.client.BasicCredentialsProvider;
2519
import org.elasticsearch.client.RestClient;
26-
import org.elasticsearch.client.RestClientBuilder;
27-
import org.elasticsearch.client.RestHighLevelClient;
2820

29-
import org.springframework.beans.factory.ObjectProvider;
3021
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
31-
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
3222
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
33-
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
3423
import org.springframework.boot.context.properties.EnableConfigurationProperties;
35-
import org.springframework.boot.context.properties.PropertyMapper;
36-
import org.springframework.context.annotation.Bean;
3724
import org.springframework.context.annotation.Configuration;
25+
import org.springframework.context.annotation.Import;
3826

3927
/**
4028
* {@link EnableAutoConfiguration Auto-configuration} for Elasticsearch REST clients.
4129
*
4230
* @author Brian Clozel
31+
* @author Stephane Nicoll
4332
* @since 2.1.0
4433
*/
4534
@Configuration
4635
@ConditionalOnClass(RestClient.class)
4736
@EnableConfigurationProperties(RestClientProperties.class)
37+
@Import({ RestClientConfigurations.RestClientBuilderConfiguration.class,
38+
RestClientConfigurations.RestHighLevelClientConfiguration.class,
39+
RestClientConfigurations.RestClientFallbackConfiguration.class })
4840
public class RestClientAutoConfiguration {
4941

50-
private final RestClientProperties properties;
51-
52-
private final ObjectProvider<RestClientBuilderCustomizer> builderCustomizers;
53-
54-
public RestClientAutoConfiguration(RestClientProperties properties,
55-
ObjectProvider<RestClientBuilderCustomizer> builderCustomizers) {
56-
this.properties = properties;
57-
this.builderCustomizers = builderCustomizers;
58-
}
59-
60-
@Bean
61-
@ConditionalOnMissingBean
62-
public RestClient restClient(RestClientBuilder builder) {
63-
return builder.build();
64-
}
65-
66-
@Bean
67-
@ConditionalOnMissingBean
68-
public RestClientBuilder restClientBuilder() {
69-
HttpHost[] hosts = this.properties.getUris().stream().map(HttpHost::create).toArray(HttpHost[]::new);
70-
RestClientBuilder builder = RestClient.builder(hosts);
71-
PropertyMapper map = PropertyMapper.get();
72-
map.from(this.properties::getUsername).whenHasText().to((username) -> {
73-
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
74-
Credentials credentials = new UsernamePasswordCredentials(this.properties.getUsername(),
75-
this.properties.getPassword());
76-
credentialsProvider.setCredentials(AuthScope.ANY, credentials);
77-
builder.setHttpClientConfigCallback(
78-
(httpClientBuilder) -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));
79-
});
80-
this.builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
81-
return builder;
82-
}
83-
84-
@Configuration
85-
@ConditionalOnClass(RestHighLevelClient.class)
86-
public static class RestHighLevelClientConfiguration {
87-
88-
@Bean
89-
@ConditionalOnMissingBean
90-
public RestHighLevelClient restHighLevelClient(RestClientBuilder restClientBuilder) {
91-
return new RestHighLevelClient(restClientBuilder);
92-
}
93-
94-
}
95-
96-
/**
97-
* Configuration to configure a {@link RestClient} bean from a
98-
* {@link RestHighLevelClient} if such a bean has been registered by the application.
99-
* If {@link RestHighLevelClient} is not unique or does not exist then
100-
* {@link RestClientBuilder#build()} will be used.
101-
*/
102-
@Configuration(proxyBeanMethods = false)
103-
@ConditionalOnClass(RestHighLevelClient.class)
104-
@ConditionalOnBean(RestHighLevelClient.class)
105-
public static class RestClientConfiguration {
106-
107-
@Bean
108-
@ConditionalOnMissingBean
109-
public RestClient restClient(ObjectProvider<RestHighLevelClient> restHighLevelClient,
110-
RestClientBuilder builder) {
111-
RestHighLevelClient client = restHighLevelClient.getIfUnique();
112-
if (client != null) {
113-
return client.getLowLevelClient();
114-
}
115-
return builder.build();
116-
}
117-
118-
}
119-
12042
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright 2012-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.elasticsearch.rest;
18+
19+
import org.apache.http.HttpHost;
20+
import org.apache.http.auth.AuthScope;
21+
import org.apache.http.auth.Credentials;
22+
import org.apache.http.auth.UsernamePasswordCredentials;
23+
import org.apache.http.client.CredentialsProvider;
24+
import org.apache.http.impl.client.BasicCredentialsProvider;
25+
import org.elasticsearch.client.RestClient;
26+
import org.elasticsearch.client.RestClientBuilder;
27+
import org.elasticsearch.client.RestHighLevelClient;
28+
29+
import org.springframework.beans.factory.ObjectProvider;
30+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
31+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
32+
import org.springframework.boot.context.properties.PropertyMapper;
33+
import org.springframework.context.annotation.Bean;
34+
import org.springframework.context.annotation.Configuration;
35+
36+
/**
37+
* Elasticsearch rest client infrastructure configurations.
38+
*
39+
* @author Brian Clozel
40+
* @author Stephane Nicoll
41+
*/
42+
class RestClientConfigurations {
43+
44+
@Configuration
45+
static class RestClientBuilderConfiguration {
46+
47+
@Bean
48+
@ConditionalOnMissingBean
49+
RestClientBuilder restClientBuilder(RestClientProperties properties,
50+
ObjectProvider<RestClientBuilderCustomizer> builderCustomizers) {
51+
HttpHost[] hosts = properties.getUris().stream().map(HttpHost::create).toArray(HttpHost[]::new);
52+
RestClientBuilder builder = RestClient.builder(hosts);
53+
PropertyMapper map = PropertyMapper.get();
54+
map.from(properties::getUsername).whenHasText().to((username) -> {
55+
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
56+
Credentials credentials = new UsernamePasswordCredentials(properties.getUsername(),
57+
properties.getPassword());
58+
credentialsProvider.setCredentials(AuthScope.ANY, credentials);
59+
builder.setHttpClientConfigCallback(
60+
(httpClientBuilder) -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));
61+
});
62+
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
63+
return builder;
64+
}
65+
66+
}
67+
68+
@Configuration
69+
@ConditionalOnClass(RestHighLevelClient.class)
70+
static class RestHighLevelClientConfiguration {
71+
72+
@Bean
73+
@ConditionalOnMissingBean
74+
RestHighLevelClient restHighLevelClient(RestClientBuilder restClientBuilder) {
75+
return new RestHighLevelClient(restClientBuilder);
76+
}
77+
78+
@Bean
79+
@ConditionalOnMissingBean
80+
RestClient restClient(RestClientBuilder builder, ObjectProvider<RestHighLevelClient> restHighLevelClient) {
81+
RestHighLevelClient client = restHighLevelClient.getIfUnique();
82+
if (client != null) {
83+
return client.getLowLevelClient();
84+
}
85+
return builder.build();
86+
}
87+
88+
}
89+
90+
@Configuration
91+
static class RestClientFallbackConfiguration {
92+
93+
@Bean
94+
@ConditionalOnMissingBean
95+
RestClient restClient(RestClientBuilder builder) {
96+
return builder.build();
97+
}
98+
99+
}
100+
101+
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientAutoConfigurationTests.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
import org.springframework.boot.autoconfigure.AutoConfigurations;
3232
import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchNodeTemplate;
33+
import org.springframework.boot.test.context.FilteredClassLoader;
3334
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
3435
import org.springframework.context.annotation.Bean;
3536
import org.springframework.context.annotation.Configuration;
@@ -52,38 +53,46 @@ public class RestClientAutoConfigurationTests {
5253
public void configureShouldCreateBothRestClientVariants() {
5354
this.contextRunner.run((context) -> {
5455
assertThat(context).hasSingleBean(RestClient.class).hasSingleBean(RestHighLevelClient.class);
55-
RestHighLevelClient restHighLevelClient = context.getBean(RestHighLevelClient.class);
56-
assertThat(restHighLevelClient.getLowLevelClient()).isNotSameAs(context.getBean(RestClient.class));
56+
assertThat(context.getBean(RestClient.class))
57+
.isSameAs(context.getBean(RestHighLevelClient.class).getLowLevelClient());
5758
});
5859
}
5960

6061
@Test
6162
public void configureWhenCustomClientShouldBackOff() {
6263
this.contextRunner.withUserConfiguration(CustomRestClientConfiguration.class)
63-
.run((context) -> assertThat(context).hasSingleBean(RestClient.class).hasBean("customRestClient"));
64+
.run((context) -> assertThat(context).getBeanNames(RestClient.class).containsOnly("customRestClient"));
6465
}
6566

6667
@Test
6768
public void configureWhenCustomRestHighLevelClientShouldBackOff() {
6869
this.contextRunner.withUserConfiguration(CustomRestHighLevelClientConfiguration.class).run((context) -> {
6970
assertThat(context).hasSingleBean(RestClient.class).hasSingleBean(RestHighLevelClient.class);
70-
RestHighLevelClient restHighLevelClient = context.getBean(RestHighLevelClient.class);
71-
assertThat(restHighLevelClient.getLowLevelClient()).isSameAs(context.getBean(RestClient.class));
71+
assertThat(context.getBean(RestClient.class))
72+
.isSameAs(context.getBean(RestHighLevelClient.class).getLowLevelClient());
7273
});
7374
}
7475

7576
@Test
7677
public void configureWhenDefaultRestClientShouldCreateWhenNoUniqueRestHighLevelClient() {
7778
this.contextRunner.withUserConfiguration(TwoCustomRestHighLevelClientConfiguration.class).run((context) -> {
7879
assertThat(context).hasSingleBean(RestClient.class);
80+
RestClient restClient = context.getBean(RestClient.class);
7981
Map<String, RestHighLevelClient> restHighLevelClients = context.getBeansOfType(RestHighLevelClient.class);
80-
assertThat(restHighLevelClients).isNotEmpty();
82+
assertThat(restHighLevelClients).hasSize(2);
8183
for (RestHighLevelClient restHighLevelClient : restHighLevelClients.values()) {
82-
assertThat(restHighLevelClient.getLowLevelClient()).isNotSameAs(context.getBean(RestClient.class));
84+
assertThat(restHighLevelClient.getLowLevelClient()).isNotSameAs(restClient);
8385
}
8486
});
8587
}
8688

89+
@Test
90+
public void configureWhenHighLevelClientIsNotAvailableShouldCreateRestClientOnly() {
91+
this.contextRunner.withClassLoader(new FilteredClassLoader(RestHighLevelClient.class))
92+
.run((context) -> assertThat(context).hasSingleBean(RestClient.class)
93+
.doesNotHaveBean(RestHighLevelClient.class));
94+
}
95+
8796
@Test
8897
public void configureWhenBuilderCustomizerShouldApply() {
8998
this.contextRunner.withUserConfiguration(BuilderCustomizerConfiguration.class).run((context) -> {

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientAutoConfigurationWithoutRestHighLevelClientTests.java

Lines changed: 0 additions & 47 deletions
This file was deleted.

0 commit comments

Comments
 (0)