diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataAutoConfiguration.java index bea89b82fc6..8cf642bfdd8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataAutoConfiguration.java @@ -24,27 +24,30 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories; +import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories; /** * {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Elasticsearch * support. *

- * Registers an {@link ElasticsearchTemplate} if no other bean of the same type is - * configured. + * Registers an {@link ElasticsearchTemplate} if no other bean of the same type and the + * same name {@code "elasticsearchTemplate"} is configured. * * @author Brian Clozel * @author Artur Konczak * @author Mohsin Husen * @see EnableElasticsearchRepositories + * @see EnableReactiveElasticsearchRepositories * @since 1.1.0 */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ ElasticsearchTemplate.class }) @AutoConfigureAfter({ ElasticsearchAutoConfiguration.class, - RestClientAutoConfiguration.class }) + RestClientAutoConfiguration.class, ReactiveRestClientAutoConfiguration.class }) @Import({ ElasticsearchDataConfiguration.BaseConfiguration.class, ElasticsearchDataConfiguration.TransportClientConfiguration.class, - ElasticsearchDataConfiguration.RestHighLevelClientConfiguration.class }) + ElasticsearchDataConfiguration.RestClientConfiguration.class, + ElasticsearchDataConfiguration.ReactiveRestClientConfiguration.class }) public class ElasticsearchDataAutoConfiguration { } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataConfiguration.java index e9dda9b14b9..4848eb74e1c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataConfiguration.java @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.data.elasticsearch; +import org.elasticsearch.action.support.IndicesOptions; +import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.client.Client; import org.elasticsearch.client.RestHighLevelClient; @@ -24,12 +26,20 @@ 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; +import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient; +import org.springframework.data.elasticsearch.core.DefaultEntityMapper; +import org.springframework.data.elasticsearch.core.DefaultResultMapper; import org.springframework.data.elasticsearch.core.ElasticsearchOperations; import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; +import org.springframework.data.elasticsearch.core.EntityMapper; +import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations; +import org.springframework.data.elasticsearch.core.ReactiveElasticsearchTemplate; +import org.springframework.data.elasticsearch.core.ResultsMapper; import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter; import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext; +import org.springframework.web.reactive.function.client.WebClient; /** * Configuration classes for Spring Data for Elasticsearch @@ -57,19 +67,33 @@ abstract class ElasticsearchDataConfiguration { return new SimpleElasticsearchMappingContext(); } + @Bean + public EntityMapper entityMapper( + SimpleElasticsearchMappingContext mappingContext) { + return new DefaultEntityMapper(mappingContext); + } + + @Bean + @ConditionalOnMissingBean + public ResultsMapper resultsMapper( + SimpleElasticsearchMappingContext mappingContext, + EntityMapper entityMapper) { + return new DefaultResultMapper(mappingContext, entityMapper); + } + } @Configuration(proxyBeanMethods = false) @ConditionalOnClass(RestHighLevelClient.class) - static class RestHighLevelClientConfiguration { + static class RestClientConfiguration { @Bean @ConditionalOnMissingBean(value = ElasticsearchOperations.class, name = "elasticsearchTemplate") @ConditionalOnBean(RestHighLevelClient.class) public ElasticsearchRestTemplate elasticsearchTemplate(RestHighLevelClient client, - ElasticsearchConverter converter) { - return new ElasticsearchRestTemplate(client, converter); + ElasticsearchConverter converter, ResultsMapper resultsMapper) { + return new ElasticsearchRestTemplate(client, converter, resultsMapper); } } @@ -83,9 +107,9 @@ abstract class ElasticsearchDataConfiguration { name = "elasticsearchTemplate") @ConditionalOnBean(Client.class) public ElasticsearchTemplate elasticsearchTemplate(Client client, - ElasticsearchConverter converter) { + ElasticsearchConverter converter, ResultsMapper resultsMapper) { try { - return new ElasticsearchTemplate(client, converter); + return new ElasticsearchTemplate(client, converter, resultsMapper); } catch (Exception ex) { throw new IllegalStateException(ex); @@ -94,4 +118,24 @@ abstract class ElasticsearchDataConfiguration { } + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass({ WebClient.class, ReactiveElasticsearchOperations.class }) + static class ReactiveRestClientConfiguration { + + @Bean + @ConditionalOnMissingBean(value = ReactiveElasticsearchOperations.class, + name = "reactiveElasticsearchTemplate") + @ConditionalOnBean(ReactiveElasticsearchClient.class) + public ReactiveElasticsearchTemplate reactiveElasticsearchTemplate( + ReactiveElasticsearchClient client, ElasticsearchConverter converter, + ResultsMapper resultsMapper) { + ReactiveElasticsearchTemplate template = new ReactiveElasticsearchTemplate( + client, converter, resultsMapper); + template.setIndicesOptions(IndicesOptions.strictExpandOpenAndForbidClosed()); + template.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); + return template; + } + + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveElasticsearchRepositoriesAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveElasticsearchRepositoriesAutoConfiguration.java new file mode 100644 index 00000000000..811fc463f63 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveElasticsearchRepositoriesAutoConfiguration.java @@ -0,0 +1,47 @@ +/* + * 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.data.elasticsearch; + +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.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient; +import org.springframework.data.elasticsearch.repository.ReactiveElasticsearchRepository; +import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories; +import org.springframework.data.elasticsearch.repository.support.ReactiveElasticsearchRepositoryFactoryBean; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Elasticsearch + * Reactive Repositories. + * + * @author Brian Clozel + * @see EnableReactiveElasticsearchRepositories + * @since 2.2.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass({ ReactiveElasticsearchClient.class, + ReactiveElasticsearchRepository.class }) +@ConditionalOnProperty(prefix = "spring.data.elasticsearch.repositories", + name = "enabled", havingValue = "true", matchIfMissing = true) +@ConditionalOnMissingBean(ReactiveElasticsearchRepositoryFactoryBean.class) +@Import(ReactiveElasticsearchRepositoriesRegistrar.class) +public class ReactiveElasticsearchRepositoriesAutoConfiguration { + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveElasticsearchRepositoriesRegistrar.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveElasticsearchRepositoriesRegistrar.java new file mode 100644 index 00000000000..c7bcfa8b416 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveElasticsearchRepositoriesRegistrar.java @@ -0,0 +1,57 @@ +/* + * 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.data.elasticsearch; + +import java.lang.annotation.Annotation; + +import org.springframework.boot.autoconfigure.data.AbstractRepositoryConfigurationSourceSupport; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories; +import org.springframework.data.elasticsearch.repository.config.ReactiveElasticsearchRepositoryConfigurationExtension; +import org.springframework.data.repository.config.RepositoryConfigurationExtension; + +/** + * {@link ImportBeanDefinitionRegistrar} used to auto-configure Spring Data Elasticsearch + * Reactive Repositories. + * + * @author Brian Clozel + * @since 2.2.0 + */ +class ReactiveElasticsearchRepositoriesRegistrar + extends AbstractRepositoryConfigurationSourceSupport { + + @Override + protected Class getAnnotation() { + return EnableReactiveElasticsearchRepositories.class; + } + + @Override + protected Class getConfiguration() { + return EnableElasticsearchRepositoriesConfiguration.class; + } + + @Override + protected RepositoryConfigurationExtension getRepositoryConfigurationExtension() { + return new ReactiveElasticsearchRepositoryConfigurationExtension(); + } + + @EnableReactiveElasticsearchRepositories + private static class EnableElasticsearchRepositoriesConfiguration { + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveRestClientAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveRestClientAutoConfiguration.java new file mode 100644 index 00000000000..26a95f17ee8 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveRestClientAutoConfiguration.java @@ -0,0 +1,81 @@ +/* + * 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.data.elasticsearch; + +import reactor.netty.http.client.HttpClient; + +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.data.elasticsearch.client.ClientConfiguration; +import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient; +import org.springframework.data.elasticsearch.client.reactive.ReactiveRestClients; +import org.springframework.http.HttpHeaders; +import org.springframework.web.reactive.function.client.WebClient; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for Elasticsearch Reactive REST + * clients. + * + * @author Brian Clozel + * @since 2.2.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass({ ReactiveRestClients.class, WebClient.class, HttpClient.class }) +@EnableConfigurationProperties(ReactiveRestClientProperties.class) +public class ReactiveRestClientAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public ClientConfiguration clientConfiguration( + ReactiveRestClientProperties properties) { + ClientConfiguration.MaybeSecureClientConfigurationBuilder builder = ClientConfiguration + .builder().connectedTo(properties.getEndpoints().toArray(new String[0])); + if (properties.isUseSsl()) { + builder.usingSsl(); + } + configureTimeouts(builder, properties); + return builder.build(); + } + + private void configureTimeouts( + ClientConfiguration.TerminalClientConfigurationBuilder builder, + ReactiveRestClientProperties properties) { + PropertyMapper map = PropertyMapper.get(); + map.from(properties.getConnectionTimeout()).whenNonNull() + .to(builder::withConnectTimeout); + map.from(properties.getSocketTimeout()).whenNonNull() + .to(builder::withSocketTimeout); + map.from(properties.getUsername()).whenHasText().to((username) -> { + HttpHeaders headers = new HttpHeaders(); + headers.setBasicAuth(username, properties.getPassword()); + builder.withDefaultHeaders(headers); + }); + } + + @Bean + @ConditionalOnMissingBean + public ReactiveElasticsearchClient reactiveElasticsearchClient( + ClientConfiguration clientConfiguration) { + return ReactiveRestClients.create(clientConfiguration); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveRestClientProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveRestClientProperties.java new file mode 100644 index 00000000000..e322db33565 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveRestClientProperties.java @@ -0,0 +1,114 @@ +/* + * 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.data.elasticsearch; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Configuration properties for Elasticsearch Reactive REST clients. + * + * @author Brian Clozel + * @since 2.2.0 + */ +@ConfigurationProperties(prefix = "spring.data.elasticsearch.client.reactive") +public class ReactiveRestClientProperties { + + /** + * Comma-separated list of the Elasticsearch endpoints to connect to. + */ + private List endpoints = new ArrayList<>( + Collections.singletonList("localhost:9200")); + + /** + * Whether the client should use SSL to connect to the endpoints. + */ + private boolean useSsl = false; + + /** + * Credentials username. + */ + private String username; + + /** + * Credentials password. + */ + private String password; + + /** + * Connection timeout. + */ + private Duration connectionTimeout; + + /** + * Read and Write Socket timeout. + */ + private Duration socketTimeout; + + public List getEndpoints() { + return this.endpoints; + } + + public void setEndpoints(List endpoints) { + this.endpoints = endpoints; + } + + public boolean isUseSsl() { + return this.useSsl; + } + + public void setUseSsl(boolean useSsl) { + this.useSsl = useSsl; + } + + public String getUsername() { + return this.username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return this.password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Duration getConnectionTimeout() { + return this.connectionTimeout; + } + + public void setConnectionTimeout(Duration connectionTimeout) { + this.connectionTimeout = connectionTimeout; + } + + public Duration getSocketTimeout() { + return this.socketTimeout; + } + + public void setSocketTimeout(Duration socketTimeout) { + this.socketTimeout = socketTimeout; + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/package-info.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/package-info.java index c27a1ccdb46..0796afa2183 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/package-info.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * 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. diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index 55165252981..91f0a509656 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -42,6 +42,8 @@ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoC org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\ +org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\ +org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveRestClientAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\ diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/alt/elasticsearch/CityReactiveElasticsearchDbRepository.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/alt/elasticsearch/CityReactiveElasticsearchDbRepository.java new file mode 100644 index 00000000000..a216b15e851 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/alt/elasticsearch/CityReactiveElasticsearchDbRepository.java @@ -0,0 +1,25 @@ +/* + * 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.data.alt.elasticsearch; + +import org.springframework.boot.autoconfigure.data.elasticsearch.city.City; +import org.springframework.data.elasticsearch.repository.ReactiveElasticsearchRepository; + +public interface CityReactiveElasticsearchDbRepository + extends ReactiveElasticsearchRepository { + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchAutoConfigurationTests.java index ac8f9f0ece1..9e61ac08b9d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchAutoConfigurationTests.java @@ -41,7 +41,6 @@ import static org.mockito.Mockito.mock; * @author Phillip Webb * @author Andy Wilkinson */ -@Deprecated @Testcontainers public class ElasticsearchAutoConfigurationTests { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataAutoConfigurationTests.java index 520c1b943a9..0c2ead20e8f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataAutoConfigurationTests.java @@ -28,6 +28,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; +import org.springframework.data.elasticsearch.core.EntityMapper; +import org.springframework.data.elasticsearch.core.ReactiveElasticsearchTemplate; import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext; @@ -50,6 +52,7 @@ public class ElasticsearchDataAutoConfigurationTests { private ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(ElasticsearchAutoConfiguration.class, RestClientAutoConfiguration.class, + ReactiveRestClientAutoConfiguration.class, ElasticsearchDataAutoConfiguration.class)); @Test @@ -75,6 +78,10 @@ public class ElasticsearchDataAutoConfigurationTests { public void defaultRestBeansRegistered() { this.contextRunner.run((context) -> assertThat(context) .hasSingleBean(ElasticsearchRestTemplate.class) + .hasSingleBean(ReactiveElasticsearchTemplate.class) + .hasSingleBean(ElasticsearchConverter.class) + .hasSingleBean(SimpleElasticsearchMappingContext.class) + .hasSingleBean(EntityMapper.class) .hasSingleBean(ElasticsearchConverter.class)); } @@ -94,6 +101,14 @@ public class ElasticsearchDataAutoConfigurationTests { .contains("elasticsearchTemplate")); } + @Test + public void customReactiveRestTemplateShouldBeUsed() { + this.contextRunner.withUserConfiguration(CustomReactiveRestTemplate.class) + .run((context) -> assertThat(context) + .getBeanNames(ReactiveElasticsearchTemplate.class).hasSize(1) + .contains("reactiveElasticsearchTemplate")); + } + @Configuration static class CustomTransportTemplate { @@ -114,4 +129,14 @@ public class ElasticsearchDataAutoConfigurationTests { } + @Configuration + static class CustomReactiveRestTemplate { + + @Bean + ReactiveElasticsearchTemplate reactiveElasticsearchTemplate() { + return mock(ReactiveElasticsearchTemplate.class); + } + + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveElasticsearchRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveElasticsearchRepositoriesAutoConfigurationTests.java new file mode 100644 index 00000000000..b2183d75110 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveElasticsearchRepositoriesAutoConfigurationTests.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.data.elasticsearch; + +import org.junit.jupiter.api.Test; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage; +import org.springframework.boot.autoconfigure.data.alt.elasticsearch.CityReactiveElasticsearchDbRepository; +import org.springframework.boot.autoconfigure.data.elasticsearch.city.City; +import org.springframework.boot.autoconfigure.data.elasticsearch.city.ReactiveCityRepository; +import org.springframework.boot.autoconfigure.data.empty.EmptyDataPackage; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.boot.testsupport.testcontainers.ElasticsearchContainer; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.elasticsearch.core.ReactiveElasticsearchTemplate; +import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ReactiveElasticsearchRepositoriesAutoConfiguration}. + * + * @author Phillip Webb + * @author Andy Wilkinson + * @author Brian Clozel + */ +@Testcontainers +public class ReactiveElasticsearchRepositoriesAutoConfigurationTests { + + @Container + public static ElasticsearchContainer elasticsearch = new ElasticsearchContainer(); + + private ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration( + AutoConfigurations.of(ReactiveRestClientAutoConfiguration.class, + ReactiveElasticsearchRepositoriesAutoConfiguration.class, + ElasticsearchDataAutoConfiguration.class)) + .withPropertyValues( + "spring.data.elasticsearch.client.reactive.endpoints=localhost:" + + elasticsearch.getMappedHttpPort()); + + @Test + public void testDefaultRepositoryConfiguration() { + this.contextRunner.withUserConfiguration(TestConfiguration.class) + .run((context) -> assertThat(context) + .hasSingleBean(ReactiveCityRepository.class) + .hasSingleBean(ReactiveElasticsearchTemplate.class)); + } + + @Test + public void testNoRepositoryConfiguration() { + this.contextRunner.withUserConfiguration(EmptyConfiguration.class) + .run((context) -> assertThat(context) + .hasSingleBean(ReactiveElasticsearchTemplate.class)); + } + + @Test + public void doesNotTriggerDefaultRepositoryDetectionIfCustomized() { + this.contextRunner.withUserConfiguration(CustomizedConfiguration.class) + .run((context) -> assertThat(context) + .hasSingleBean(CityReactiveElasticsearchDbRepository.class)); + } + + @Configuration(proxyBeanMethods = false) + @TestAutoConfigurationPackage(City.class) + static class TestConfiguration { + + } + + @Configuration(proxyBeanMethods = false) + @TestAutoConfigurationPackage(EmptyDataPackage.class) + static class EmptyConfiguration { + + } + + @Configuration(proxyBeanMethods = false) + @TestAutoConfigurationPackage(ReactiveElasticsearchRepositoriesAutoConfigurationTests.class) + @EnableReactiveElasticsearchRepositories( + basePackageClasses = CityReactiveElasticsearchDbRepository.class) + static class CustomizedConfiguration { + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveRestClientAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveRestClientAutoConfigurationTests.java new file mode 100644 index 00000000000..b11e5343a1e --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveRestClientAutoConfigurationTests.java @@ -0,0 +1,119 @@ +/* + * 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.data.elasticsearch; + +import java.util.HashMap; +import java.util.Map; + +import org.elasticsearch.action.get.GetRequest; +import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.index.get.GetResult; +import org.junit.jupiter.api.Test; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.boot.testsupport.testcontainers.ElasticsearchContainer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.elasticsearch.client.ClientConfiguration; +import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link ReactiveRestClientAutoConfiguration} + * + * @author Brian Clozel + */ +@Testcontainers +public class ReactiveRestClientAutoConfigurationTests { + + @Container + public static ElasticsearchContainer elasticsearch = new ElasticsearchContainer(); + + private ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration( + AutoConfigurations.of(ReactiveRestClientAutoConfiguration.class)); + + @Test + public void configureShouldCreateDefaultBeans() { + this.contextRunner.run( + (context) -> assertThat(context).hasSingleBean(ClientConfiguration.class) + .hasSingleBean(ReactiveElasticsearchClient.class)); + } + + @Test + public void configureWhenCustomClientShouldBackOff() { + this.contextRunner.withUserConfiguration(CustomClientConfiguration.class) + .run((context) -> assertThat(context) + .hasSingleBean(ReactiveElasticsearchClient.class) + .hasBean("customClient")); + } + + @Test + public void configureWhenCustomClientConfig() { + this.contextRunner.withUserConfiguration(CustomClientConfigConfiguration.class) + .run((context) -> assertThat(context) + .hasSingleBean(ReactiveElasticsearchClient.class) + .hasSingleBean(ClientConfiguration.class) + .hasBean("customClientConfiguration")); + } + + @Test + public void restClientCanQueryElasticsearchNode() { + this.contextRunner.withPropertyValues( + "spring.data.elasticsearch.client.reactive.endpoints=localhost:" + + elasticsearch.getMappedPort()) + .run((context) -> { + ReactiveElasticsearchClient client = context + .getBean(ReactiveElasticsearchClient.class); + Map source = new HashMap<>(); + source.put("a", "alpha"); + source.put("b", "bravo"); + IndexRequest index = new IndexRequest("foo", "bar", "1") + .source(source); + GetRequest getRequest = new GetRequest("foo", "bar", "1"); + GetResult getResult = client.index(index).then(client.get(getRequest)) + .block(); + assertThat(getResult.isExists()).isTrue(); + }); + } + + @Configuration(proxyBeanMethods = false) + static class CustomClientConfiguration { + + @Bean + public ReactiveElasticsearchClient customClient() { + return mock(ReactiveElasticsearchClient.class); + } + + } + + @Configuration(proxyBeanMethods = false) + static class CustomClientConfigConfiguration { + + @Bean + public ClientConfiguration customClientConfiguration() { + return ClientConfiguration.localhost(); + } + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/city/ReactiveCityRepository.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/city/ReactiveCityRepository.java new file mode 100644 index 00000000000..444e0502c81 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/city/ReactiveCityRepository.java @@ -0,0 +1,26 @@ +/* + * 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.data.elasticsearch.city; + +import org.springframework.data.repository.reactive.ReactiveCrudRepository; + +/** + * @author Brian Clozel + */ +public interface ReactiveCityRepository extends ReactiveCrudRepository { + +} diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/attributes.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/attributes.adoc index e06bb64e3ce..6d58eb62e09 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/attributes.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/attributes.adoc @@ -52,6 +52,8 @@ :spring-session: https://projects.spring.io/spring-session/ :spring-framework: https://projects.spring.io/spring-framework/ :spring-security: https://projects.spring.io/spring-security/ +:spring-data-elasticsearch: https://spring.io/projects/spring-data-elasticsearch +:spring-data-elasticsearch-reference: https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/ :spring-data-jpa: https://projects.spring.io/spring-data-jpa/ :spring-security-reference: https://docs.spring.io/spring-security/site/docs/{spring-security-docs-version}/reference/htmlsingle :spring-security-oauth2-reference: https://projects.spring.io/spring-security-oauth/docs/oauth2.html diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index dba67be2588..47e890ab26f 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -4865,12 +4865,15 @@ auto-configuration for Elasticsearch. Spring Boot supports several clients: * The official Java "Low Level" and "High Level" REST clients -* https://github.com/searchbox-io/Jest[Jest] +* The `ReactiveElasticsearchClient` provided by Spring Data Elasticsearch The transport client is still available but its support has been deprecated in https://github.com/spring-projects/spring-data-elasticsearch[Spring Data Elasticsearch] -and will be removed in a future release. Spring Boot provides a dedicated "`Starter`", -`spring-boot-starter-data-elasticsearch`. +and Elasticsearch itself. It will be removed in a future release. +Spring Boot provides a dedicated "`Starter`", `spring-boot-starter-data-elasticsearch`. + +The https://github.com/searchbox-io/Jest[Jest] client has been deprecated as well, since +both Elasticsearch and Spring Data Elasticsearch provide official support for REST clients. [[boot-features-connecting-to-elasticsearch-rest]] ==== Connecting to Elasticsearch using REST clients @@ -4899,6 +4902,28 @@ If you have the `org.elasticsearch.client:elasticsearch-rest-high-level-client` on the classpath, Spring Boot will auto-configure a `RestHighLevelClient`, which wraps any existing `RestClient` bean, reusing its HTTP configuration. +[[boot-features-connecting-to-elasticsearch-reactive-rest]] +==== Connecting to Elasticsearch using Reactive REST clients +{spring-data-elasticsearch}[Spring Data Elasticsearch] ships `ReactiveElasticsearchClient` +for querying Elasticsearch instances in a reactive fashion. It is built on top of WebFlux's +`WebClient`, so both `spring-boot-starter-elasticsearch` and `spring-boot-starter-webflux` +dependencies are useful to enable this support. + +By default, Spring Boot will auto-configure and register a `ReactiveElasticsearchClient` +bean that targets `http://localhost:9200`. +You can further tune how it is configured, as shown in the following example: + +[source,properties,indent=0] +---- + spring.elasticsearch.reactive.endpoints=search.example.com:9200 + spring.elasticsearch.reactive.use-ssl=true + spring.elasticsearch.reactive.socket-timeout=10s + spring.elasticsearch.reactive.username=user + spring.elasticsearch.reactive.password=secret +---- + +If the configuration properties are not enough and you'd like to fully control the client +configuration, you can register a custom `ClientConfiguration` bean. [[boot-features-connecting-to-elasticsearch-jest]] ==== Connecting to Elasticsearch using Jest @@ -4933,17 +4958,12 @@ To take full control over the registration, define a `JestClient` bean. [[boot-features-connecting-to-elasticsearch-spring-data]] ==== Connecting to Elasticsearch by Using Spring Data -To connect to Elasticsearch, you must provide the address of one or more Elasticsearch -instances. The address can be specified by setting the `spring.elasticsearch.rest.uris` -property to a comma-separated `host:port` list. With this configuration in place, an -`ElasticsearchRestTemplate` or `RestHighLevelClient` can be injected like any other Spring bean, +To connect to Elasticsearch, a `RestHighLevelClient` bean must be defined, +auto-configured by Spring Boot or manually provided by the application (see previous sections). +With this configuration in place, an +`ElasticsearchRestTemplate` can be injected like any other Spring bean, as shown in the following example: -[source,properties,indent=0] ----- - spring.elasticsearch.rest.uris=localhost:9200 ----- - [source,java,indent=0] ---- @Component @@ -4960,9 +4980,12 @@ as shown in the following example: } ---- -If you add your own `ElasticsearchRestTemplate` or `ElasticsearchOperations` `@Bean`, -it replaces the default given it is named `"elasticsearchTemplate""`. - +In the presence of `spring-data-elasticsearch` and the required dependencies +for using a `WebClient` (typically `spring-boot-starter-webflux`), Spring Boot can also +auto-configure a +<> +and a `ReactiveElasticsearchTemplate` as beans. They are the reactive equivalent of the +other REST clients. [[boot-features-spring-data-elasticsearch-repositories]] @@ -4977,8 +5000,24 @@ now an Elasticsearch `@Document` class rather than a JPA `@Entity`, it works in way. TIP: For complete details of Spring Data Elasticsearch, refer to the -https://docs.spring.io/spring-data/elasticsearch/docs/[reference documentation]. +{spring-data-elasticsearch-reference}[reference documentation]. +Spring Boot supports both classic and reactive Elasticsearch repositories, using the +`ElasticsearchRestTemplate` or `ReactiveElasticsearchTemplate` beans. Most likely those +beans are auto-configured by Spring Boot given the required dependencies are present. + +If you wish to use your own template for backing the Elasticsearch repositories, you can +add your own `ElasticsearchRestTemplate` or `ElasticsearchOperations` `@Bean`, +as long as it is named `"elasticsearchTemplate"`. Same applies to +`ReactiveElasticsearchTemplate` and `ReactiveElasticsearchOperations`, with the bean +name `"reactiveElasticsearchTemplate"`. + +You can choose to disable the repositories support with the following property: + +[source,properties,indent=0] +---- + spring.data.elasticsearch.repositories.enabled=false +---- [[boot-features-cassandra]]