diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientAutoConfiguration.java index ab70515838f..ee0cc7f8eae 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientAutoConfiguration.java @@ -16,80 +16,27 @@ package org.springframework.boot.autoconfigure.elasticsearch.rest; -import org.apache.http.HttpHost; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.Credentials; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.CredentialsProvider; -import org.apache.http.impl.client.BasicCredentialsProvider; import org.elasticsearch.client.RestClient; -import org.elasticsearch.client.RestClientBuilder; -import org.elasticsearch.client.RestHighLevelClient; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.context.properties.PropertyMapper; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; /** * {@link EnableAutoConfiguration Auto-configuration} for Elasticsearch REST clients. * * @author Brian Clozel + * @author Stephane Nicoll * @since 2.1.0 */ @Configuration @ConditionalOnClass(RestClient.class) @EnableConfigurationProperties(RestClientProperties.class) +@Import({ RestClientConfigurations.RestClientBuilderConfiguration.class, + RestClientConfigurations.RestHighLevelClientConfiguration.class, + RestClientConfigurations.RestClientFallbackConfiguration.class }) public class RestClientAutoConfiguration { - private final RestClientProperties properties; - - private final ObjectProvider builderCustomizers; - - public RestClientAutoConfiguration(RestClientProperties properties, - ObjectProvider builderCustomizers) { - this.properties = properties; - this.builderCustomizers = builderCustomizers; - } - - @Bean - @ConditionalOnMissingBean - public RestClient restClient(RestClientBuilder builder) { - return builder.build(); - } - - @Bean - @ConditionalOnMissingBean - public RestClientBuilder restClientBuilder() { - HttpHost[] hosts = this.properties.getUris().stream().map(HttpHost::create).toArray(HttpHost[]::new); - RestClientBuilder builder = RestClient.builder(hosts); - PropertyMapper map = PropertyMapper.get(); - map.from(this.properties::getUsername).whenHasText().to((username) -> { - CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); - Credentials credentials = new UsernamePasswordCredentials(this.properties.getUsername(), - this.properties.getPassword()); - credentialsProvider.setCredentials(AuthScope.ANY, credentials); - builder.setHttpClientConfigCallback( - (httpClientBuilder) -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)); - }); - this.builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); - return builder; - } - - @Configuration - @ConditionalOnClass(RestHighLevelClient.class) - public static class RestHighLevelClientConfiguration { - - @Bean - @ConditionalOnMissingBean - public RestHighLevelClient restHighLevelClient(RestClientBuilder restClientBuilder) { - return new RestHighLevelClient(restClientBuilder); - } - - } - } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientConfigurations.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientConfigurations.java new file mode 100644 index 00000000000..735f27bd5ab --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientConfigurations.java @@ -0,0 +1,101 @@ +/* + * 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.autoconfigure.elasticsearch.rest; + +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.Credentials; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestClientBuilder; +import org.elasticsearch.client.RestHighLevelClient; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.PropertyMapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Elasticsearch rest client infrastructure configurations. + * + * @author Brian Clozel + * @author Stephane Nicoll + */ +class RestClientConfigurations { + + @Configuration + static class RestClientBuilderConfiguration { + + @Bean + @ConditionalOnMissingBean + RestClientBuilder restClientBuilder(RestClientProperties properties, + ObjectProvider builderCustomizers) { + HttpHost[] hosts = properties.getUris().stream().map(HttpHost::create).toArray(HttpHost[]::new); + RestClientBuilder builder = RestClient.builder(hosts); + PropertyMapper map = PropertyMapper.get(); + map.from(properties::getUsername).whenHasText().to((username) -> { + CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + Credentials credentials = new UsernamePasswordCredentials(properties.getUsername(), + properties.getPassword()); + credentialsProvider.setCredentials(AuthScope.ANY, credentials); + builder.setHttpClientConfigCallback( + (httpClientBuilder) -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)); + }); + builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); + return builder; + } + + } + + @Configuration + @ConditionalOnClass(RestHighLevelClient.class) + static class RestHighLevelClientConfiguration { + + @Bean + @ConditionalOnMissingBean + RestHighLevelClient restHighLevelClient(RestClientBuilder restClientBuilder) { + return new RestHighLevelClient(restClientBuilder); + } + + @Bean + @ConditionalOnMissingBean + RestClient restClient(RestClientBuilder builder, ObjectProvider restHighLevelClient) { + RestHighLevelClient client = restHighLevelClient.getIfUnique(); + if (client != null) { + return client.getLowLevelClient(); + } + return builder.build(); + } + + } + + @Configuration + static class RestClientFallbackConfiguration { + + @Bean + @ConditionalOnMissingBean + RestClient restClient(RestClientBuilder builder) { + return builder.build(); + } + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientAutoConfigurationTests.java index 0eff7db2636..233dcaea674 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientAutoConfigurationTests.java @@ -24,11 +24,13 @@ import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestClientBuilder; import org.elasticsearch.client.RestHighLevelClient; import org.junit.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchNodeTemplate; +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; @@ -49,14 +51,46 @@ public class RestClientAutoConfigurationTests { @Test public void configureShouldCreateBothRestClientVariants() { - this.contextRunner.run((context) -> assertThat(context).hasSingleBean(RestClient.class) - .hasSingleBean(RestHighLevelClient.class)); + this.contextRunner.run((context) -> { + assertThat(context).hasSingleBean(RestClient.class).hasSingleBean(RestHighLevelClient.class); + assertThat(context.getBean(RestClient.class)) + .isSameAs(context.getBean(RestHighLevelClient.class).getLowLevelClient()); + }); } @Test public void configureWhenCustomClientShouldBackOff() { this.contextRunner.withUserConfiguration(CustomRestClientConfiguration.class) - .run((context) -> assertThat(context).hasSingleBean(RestClient.class).hasBean("customRestClient")); + .run((context) -> assertThat(context).getBeanNames(RestClient.class).containsOnly("customRestClient")); + } + + @Test + public void configureWhenCustomRestHighLevelClientShouldBackOff() { + this.contextRunner.withUserConfiguration(CustomRestHighLevelClientConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(RestClient.class).hasSingleBean(RestHighLevelClient.class); + assertThat(context.getBean(RestClient.class)) + .isSameAs(context.getBean(RestHighLevelClient.class).getLowLevelClient()); + }); + } + + @Test + public void configureWhenDefaultRestClientShouldCreateWhenNoUniqueRestHighLevelClient() { + this.contextRunner.withUserConfiguration(TwoCustomRestHighLevelClientConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(RestClient.class); + RestClient restClient = context.getBean(RestClient.class); + Map restHighLevelClients = context.getBeansOfType(RestHighLevelClient.class); + assertThat(restHighLevelClients).hasSize(2); + for (RestHighLevelClient restHighLevelClient : restHighLevelClients.values()) { + assertThat(restHighLevelClient.getLowLevelClient()).isNotSameAs(restClient); + } + }); + } + + @Test + public void configureWhenHighLevelClientIsNotAvailableShouldCreateRestClientOnly() { + this.contextRunner.withClassLoader(new FilteredClassLoader(RestHighLevelClient.class)) + .run((context) -> assertThat(context).hasSingleBean(RestClient.class) + .doesNotHaveBean(RestHighLevelClient.class)); } @Test @@ -106,4 +140,29 @@ public class RestClientAutoConfigurationTests { } + @Configuration + static class CustomRestHighLevelClientConfiguration { + + @Bean + RestHighLevelClient customRestHighLevelClient(RestClientBuilder builder) { + return new RestHighLevelClient(builder); + } + + } + + @Configuration + static class TwoCustomRestHighLevelClientConfiguration { + + @Bean + RestHighLevelClient customRestHighLevelClient(RestClientBuilder builder) { + return new RestHighLevelClient(builder); + } + + @Bean + RestHighLevelClient customRestHighLevelClient1(RestClientBuilder builder) { + return new RestHighLevelClient(builder); + } + + } + }