Commit 11c1980c authored by Brian Clozel's avatar Brian Clozel

Merge pull request #20994 from vpavic

* improve-es-config:
  Polish
  Improve Elasticsearch RestClient customization capabilities

Closes gh-20994
parents 8de00277 f1039667
...@@ -23,7 +23,9 @@ import org.apache.http.auth.AuthScope; ...@@ -23,7 +23,9 @@ import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials; import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider; import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder; import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.client.RestHighLevelClient;
...@@ -40,32 +42,30 @@ import org.springframework.context.annotation.Configuration; ...@@ -40,32 +42,30 @@ import org.springframework.context.annotation.Configuration;
* *
* @author Brian Clozel * @author Brian Clozel
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Vedran Pavic
*/ */
class ElasticsearchRestClientConfigurations { class ElasticsearchRestClientConfigurations {
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(RestClientBuilder.class)
static class RestClientBuilderConfiguration { static class RestClientBuilderConfiguration {
@Bean @Bean
@ConditionalOnMissingBean RestClientBuilderCustomizer defaultRestClientBuilderCustomizer(ElasticsearchRestClientProperties properties) {
return new DefaultRestClientBuilderCustomizer(properties);
}
@Bean
RestClientBuilder elasticsearchRestClientBuilder(ElasticsearchRestClientProperties properties, RestClientBuilder elasticsearchRestClientBuilder(ElasticsearchRestClientProperties properties,
ObjectProvider<RestClientBuilderCustomizer> builderCustomizers) { ObjectProvider<RestClientBuilderCustomizer> builderCustomizers) {
HttpHost[] hosts = properties.getUris().stream().map(HttpHost::create).toArray(HttpHost[]::new); HttpHost[] hosts = properties.getUris().stream().map(HttpHost::create).toArray(HttpHost[]::new);
RestClientBuilder builder = RestClient.builder(hosts); RestClientBuilder builder = RestClient.builder(hosts);
PropertyMapper map = PropertyMapper.get(); builder.setHttpClientConfigCallback((httpClientBuilder) -> {
map.from(properties::getUsername).whenHasText().to((username) -> { builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(httpClientBuilder));
CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); return httpClientBuilder;
Credentials credentials = new UsernamePasswordCredentials(properties.getUsername(),
properties.getPassword());
credentialsProvider.setCredentials(AuthScope.ANY, credentials);
builder.setHttpClientConfigCallback(
(httpClientBuilder) -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));
}); });
builder.setRequestConfigCallback((requestConfigBuilder) -> { builder.setRequestConfigCallback((requestConfigBuilder) -> {
map.from(properties::getConnectionTimeout).whenNonNull().asInt(Duration::toMillis) builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(requestConfigBuilder));
.to(requestConfigBuilder::setConnectTimeout);
map.from(properties::getReadTimeout).whenNonNull().asInt(Duration::toMillis)
.to(requestConfigBuilder::setSocketTimeout);
return requestConfigBuilder; return requestConfigBuilder;
}); });
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
...@@ -108,4 +108,39 @@ class ElasticsearchRestClientConfigurations { ...@@ -108,4 +108,39 @@ class ElasticsearchRestClientConfigurations {
} }
static class DefaultRestClientBuilderCustomizer implements RestClientBuilderCustomizer {
private static final PropertyMapper map = PropertyMapper.get();
private final ElasticsearchRestClientProperties properties;
DefaultRestClientBuilderCustomizer(ElasticsearchRestClientProperties properties) {
this.properties = properties;
}
@Override
public void customize(RestClientBuilder builder) {
}
@Override
public void customize(HttpAsyncClientBuilder builder) {
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.setDefaultCredentialsProvider(credentialsProvider);
});
}
@Override
public void customize(RequestConfig.Builder builder) {
map.from(this.properties::getConnectionTimeout).whenNonNull().asInt(Duration::toMillis)
.to(builder::setConnectTimeout);
map.from(this.properties::getReadTimeout).whenNonNull().asInt(Duration::toMillis)
.to(builder::setSocketTimeout);
}
}
} }
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
package org.springframework.boot.autoconfigure.elasticsearch; package org.springframework.boot.autoconfigure.elasticsearch;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.client.RestClientBuilder; import org.elasticsearch.client.RestClientBuilder;
/** /**
...@@ -24,6 +26,7 @@ import org.elasticsearch.client.RestClientBuilder; ...@@ -24,6 +26,7 @@ import org.elasticsearch.client.RestClientBuilder;
* retaining default auto-configuration. * retaining default auto-configuration.
* *
* @author Brian Clozel * @author Brian Clozel
* @author Vedran Pavic
* @since 2.1.0 * @since 2.1.0
*/ */
@FunctionalInterface @FunctionalInterface
...@@ -31,8 +34,29 @@ public interface RestClientBuilderCustomizer { ...@@ -31,8 +34,29 @@ public interface RestClientBuilderCustomizer {
/** /**
* Customize the {@link RestClientBuilder}. * Customize the {@link RestClientBuilder}.
* <p>
* Possibly overrides customizations made with the {@code "spring.elasticsearch.rest"}
* configuration properties namespace. For more targeted changes, see
* {@link #customize(HttpAsyncClientBuilder)} and
* {@link #customize(RequestConfig.Builder)}.
* @param builder the builder to customize * @param builder the builder to customize
*/ */
void customize(RestClientBuilder builder); void customize(RestClientBuilder builder);
/**
* Customize the {@link HttpAsyncClientBuilder}.
* @param builder the builder
* @since 2.3.0
*/
default void customize(HttpAsyncClientBuilder builder) {
}
/**
* Customize the {@link RequestConfig.Builder}.
* @param builder the builder
* @since 2.3.0
*/
default void customize(RequestConfig.Builder builder) {
}
} }
...@@ -20,6 +20,8 @@ import java.time.Duration; ...@@ -20,6 +20,8 @@ import java.time.Duration;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RequestOptions;
...@@ -36,7 +38,6 @@ import org.springframework.boot.test.context.FilteredClassLoader; ...@@ -36,7 +38,6 @@ import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
...@@ -45,6 +46,7 @@ import static org.mockito.Mockito.mock; ...@@ -45,6 +46,7 @@ import static org.mockito.Mockito.mock;
* Tests for {@link ElasticsearchRestClientAutoConfiguration}. * Tests for {@link ElasticsearchRestClientAutoConfiguration}.
* *
* @author Brian Clozel * @author Brian Clozel
* @author Vedran Pavic
*/ */
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
class ElasticsearchRestClientAutoConfigurationTests { class ElasticsearchRestClientAutoConfigurationTests {
...@@ -53,7 +55,7 @@ class ElasticsearchRestClientAutoConfigurationTests { ...@@ -53,7 +55,7 @@ class ElasticsearchRestClientAutoConfigurationTests {
static final ElasticsearchContainer elasticsearch = new ElasticsearchContainer().withStartupAttempts(5) static final ElasticsearchContainer elasticsearch = new ElasticsearchContainer().withStartupAttempts(5)
.withStartupTimeout(Duration.ofMinutes(10)); .withStartupTimeout(Duration.ofMinutes(10));
private ApplicationContextRunner contextRunner = new ApplicationContextRunner() private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(ElasticsearchRestClientAutoConfiguration.class)); .withConfiguration(AutoConfigurations.of(ElasticsearchRestClientAutoConfiguration.class));
@Test @Test
...@@ -106,6 +108,8 @@ class ElasticsearchRestClientAutoConfigurationTests { ...@@ -106,6 +108,8 @@ class ElasticsearchRestClientAutoConfigurationTests {
assertThat(context).hasSingleBean(RestClient.class); assertThat(context).hasSingleBean(RestClient.class);
RestClient restClient = context.getBean(RestClient.class); RestClient restClient = context.getBean(RestClient.class);
assertThat(restClient).hasFieldOrPropertyWithValue("pathPrefix", "/test"); assertThat(restClient).hasFieldOrPropertyWithValue("pathPrefix", "/test");
assertThat(restClient).extracting("client.connmgr.pool.maxTotal").isEqualTo(100);
assertThat(restClient).extracting("client.defaultConfig.cookieSpec").isEqualTo("rfc6265-lax");
}); });
} }
...@@ -130,10 +134,10 @@ class ElasticsearchRestClientAutoConfigurationTests { ...@@ -130,10 +134,10 @@ class ElasticsearchRestClientAutoConfigurationTests {
} }
private static void assertTimeouts(RestClient restClient, Duration connectTimeout, Duration readTimeout) { private static void assertTimeouts(RestClient restClient, Duration connectTimeout, Duration readTimeout) {
Object client = ReflectionTestUtils.getField(restClient, "client"); assertThat(restClient).extracting("client.defaultConfig.socketTimeout")
Object config = ReflectionTestUtils.getField(client, "defaultConfig"); .isEqualTo(Math.toIntExact(readTimeout.toMillis()));
assertThat(config).hasFieldOrPropertyWithValue("socketTimeout", Math.toIntExact(readTimeout.toMillis())); assertThat(restClient).extracting("client.defaultConfig.connectTimeout")
assertThat(config).hasFieldOrPropertyWithValue("connectTimeout", Math.toIntExact(connectTimeout.toMillis())); .isEqualTo(Math.toIntExact(connectTimeout.toMillis()));
} }
@Test @Test
...@@ -167,7 +171,24 @@ class ElasticsearchRestClientAutoConfigurationTests { ...@@ -167,7 +171,24 @@ class ElasticsearchRestClientAutoConfigurationTests {
@Bean @Bean
RestClientBuilderCustomizer myCustomizer() { RestClientBuilderCustomizer myCustomizer() {
return (builder) -> builder.setPathPrefix("/test"); return new RestClientBuilderCustomizer() {
@Override
public void customize(RestClientBuilder builder) {
builder.setPathPrefix("/test");
}
@Override
public void customize(HttpAsyncClientBuilder builder) {
builder.setMaxConnTotal(100);
}
@Override
public void customize(RequestConfig.Builder builder) {
builder.setCookieSpec("rfc6265-lax");
}
};
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment