Skip to content

Commit 1fd17cf

Browse files
committed
Add support for Elasticsearch sniffer
Closes gh-24174
1 parent 1e8d5c3 commit 1fd17cf

File tree

6 files changed

+309
-149
lines changed

6 files changed

+309
-149
lines changed

spring-boot-project/spring-boot-autoconfigure/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ dependencies {
7676
optional("org.eclipse.jetty.websocket:javax-websocket-server-impl")
7777
optional("org.ehcache:ehcache")
7878
optional("org.elasticsearch.client:elasticsearch-rest-client")
79+
optional("org.elasticsearch.client:elasticsearch-rest-client-sniffer")
7980
optional("org.elasticsearch.client:elasticsearch-rest-high-level-client")
8081
optional("org.flywaydb:flyway-core")
8182
optional("org.freemarker:freemarker")

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

Lines changed: 6 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -16,30 +16,18 @@
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;
3019
import org.elasticsearch.client.RestClient;
31-
import org.elasticsearch.client.RestClientBuilder;
3220
import org.elasticsearch.client.RestHighLevelClient;
3321

34-
import org.springframework.beans.factory.ObjectProvider;
3522
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
3623
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
3724
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
25+
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientConfigurations.RestClientBuilderConfiguration;
26+
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientConfigurations.RestClientSnifferConfiguration;
27+
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientConfigurations.RestHighLevelClientConfiguration;
3828
import org.springframework.boot.context.properties.EnableConfigurationProperties;
39-
import org.springframework.boot.context.properties.PropertyMapper;
40-
import org.springframework.context.annotation.Bean;
4129
import org.springframework.context.annotation.Configuration;
42-
import org.springframework.util.StringUtils;
30+
import org.springframework.context.annotation.Import;
4331

4432
/**
4533
* {@link EnableAutoConfiguration Auto-configuration} for Elasticsearch REST clients.
@@ -52,139 +40,8 @@
5240
@ConditionalOnClass(RestHighLevelClient.class)
5341
@ConditionalOnMissingBean(RestClient.class)
5442
@EnableConfigurationProperties(ElasticsearchRestClientProperties.class)
43+
@Import({ RestClientBuilderConfiguration.class, RestHighLevelClientConfiguration.class,
44+
RestClientSnifferConfiguration.class })
5545
public class ElasticsearchRestClientAutoConfiguration {
5646

57-
@Configuration(proxyBeanMethods = false)
58-
@ConditionalOnMissingBean(RestClientBuilder.class)
59-
static class RestClientBuilderConfiguration {
60-
61-
@Bean
62-
RestClientBuilderCustomizer defaultRestClientBuilderCustomizer(ElasticsearchRestClientProperties properties) {
63-
return new DefaultRestClientBuilderCustomizer(properties);
64-
}
65-
66-
@Bean
67-
RestClientBuilder elasticsearchRestClientBuilder(ElasticsearchRestClientProperties properties,
68-
ObjectProvider<RestClientBuilderCustomizer> builderCustomizers) {
69-
HttpHost[] hosts = properties.getUris().stream().map(this::createHttpHost).toArray(HttpHost[]::new);
70-
RestClientBuilder builder = RestClient.builder(hosts);
71-
builder.setHttpClientConfigCallback((httpClientBuilder) -> {
72-
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(httpClientBuilder));
73-
return httpClientBuilder;
74-
});
75-
builder.setRequestConfigCallback((requestConfigBuilder) -> {
76-
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(requestConfigBuilder));
77-
return requestConfigBuilder;
78-
});
79-
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
80-
return builder;
81-
}
82-
83-
private HttpHost createHttpHost(String uri) {
84-
try {
85-
return createHttpHost(URI.create(uri));
86-
}
87-
catch (IllegalArgumentException ex) {
88-
return HttpHost.create(uri);
89-
}
90-
}
91-
92-
private HttpHost createHttpHost(URI uri) {
93-
if (!StringUtils.hasLength(uri.getUserInfo())) {
94-
return HttpHost.create(uri.toString());
95-
}
96-
try {
97-
return HttpHost.create(new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(),
98-
uri.getQuery(), uri.getFragment()).toString());
99-
}
100-
catch (URISyntaxException ex) {
101-
throw new IllegalStateException(ex);
102-
}
103-
}
104-
105-
}
106-
107-
@Configuration(proxyBeanMethods = false)
108-
@ConditionalOnMissingBean(RestHighLevelClient.class)
109-
static class RestHighLevelClientConfiguration {
110-
111-
@Bean
112-
RestHighLevelClient elasticsearchRestHighLevelClient(RestClientBuilder restClientBuilder) {
113-
return new RestHighLevelClient(restClientBuilder);
114-
}
115-
116-
}
117-
118-
static class DefaultRestClientBuilderCustomizer implements RestClientBuilderCustomizer {
119-
120-
private static final PropertyMapper map = PropertyMapper.get();
121-
122-
private final ElasticsearchRestClientProperties properties;
123-
124-
DefaultRestClientBuilderCustomizer(ElasticsearchRestClientProperties properties) {
125-
this.properties = properties;
126-
}
127-
128-
@Override
129-
public void customize(RestClientBuilder builder) {
130-
}
131-
132-
@Override
133-
public void customize(HttpAsyncClientBuilder builder) {
134-
builder.setDefaultCredentialsProvider(new PropertiesCredentialsProvider(this.properties));
135-
}
136-
137-
@Override
138-
public void customize(RequestConfig.Builder builder) {
139-
map.from(this.properties::getConnectionTimeout).whenNonNull().asInt(Duration::toMillis)
140-
.to(builder::setConnectTimeout);
141-
map.from(this.properties::getReadTimeout).whenNonNull().asInt(Duration::toMillis)
142-
.to(builder::setSocketTimeout);
143-
}
144-
145-
}
146-
147-
private static class PropertiesCredentialsProvider extends BasicCredentialsProvider {
148-
149-
PropertiesCredentialsProvider(ElasticsearchRestClientProperties properties) {
150-
if (StringUtils.hasText(properties.getUsername())) {
151-
Credentials credentials = new UsernamePasswordCredentials(properties.getUsername(),
152-
properties.getPassword());
153-
setCredentials(AuthScope.ANY, credentials);
154-
}
155-
properties.getUris().stream().map(this::toUri).filter(this::hasUserInfo)
156-
.forEach(this::addUserInfoCredentials);
157-
}
158-
159-
private URI toUri(String uri) {
160-
try {
161-
return URI.create(uri);
162-
}
163-
catch (IllegalArgumentException ex) {
164-
return null;
165-
}
166-
}
167-
168-
private boolean hasUserInfo(URI uri) {
169-
return uri != null && StringUtils.hasLength(uri.getUserInfo());
170-
}
171-
172-
private void addUserInfoCredentials(URI uri) {
173-
AuthScope authScope = new AuthScope(uri.getHost(), uri.getPort());
174-
Credentials credentials = createUserInfoCredentials(uri.getUserInfo());
175-
setCredentials(authScope, credentials);
176-
}
177-
178-
private Credentials createUserInfoCredentials(String userInfo) {
179-
int delimiter = userInfo.indexOf(":");
180-
if (delimiter == -1) {
181-
return new UsernamePasswordCredentials(userInfo, null);
182-
}
183-
String username = userInfo.substring(0, delimiter);
184-
String password = userInfo.substring(delimiter + 1);
185-
return new UsernamePasswordCredentials(username, password);
186-
}
187-
188-
}
189-
19047
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
/*
2+
* Copyright 2012-2020 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;
18+
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;
30+
import org.elasticsearch.client.RestClient;
31+
import org.elasticsearch.client.RestClientBuilder;
32+
import org.elasticsearch.client.RestHighLevelClient;
33+
import org.elasticsearch.client.sniff.Sniffer;
34+
import org.elasticsearch.client.sniff.SnifferBuilder;
35+
36+
import org.springframework.beans.factory.ObjectProvider;
37+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
38+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
39+
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
40+
import org.springframework.boot.context.properties.PropertyMapper;
41+
import org.springframework.context.annotation.Bean;
42+
import org.springframework.context.annotation.Configuration;
43+
import org.springframework.util.StringUtils;
44+
45+
/**
46+
* Elasticsearch rest client configurations.
47+
*
48+
* @author Stephane Nicoll
49+
*/
50+
class ElasticsearchRestClientConfigurations {
51+
52+
@Configuration(proxyBeanMethods = false)
53+
@ConditionalOnMissingBean(RestClientBuilder.class)
54+
static class RestClientBuilderConfiguration {
55+
56+
@Bean
57+
RestClientBuilderCustomizer defaultRestClientBuilderCustomizer(ElasticsearchRestClientProperties properties) {
58+
return new DefaultRestClientBuilderCustomizer(properties);
59+
}
60+
61+
@Bean
62+
RestClientBuilder elasticsearchRestClientBuilder(ElasticsearchRestClientProperties properties,
63+
ObjectProvider<RestClientBuilderCustomizer> builderCustomizers) {
64+
HttpHost[] hosts = properties.getUris().stream().map(this::createHttpHost).toArray(HttpHost[]::new);
65+
RestClientBuilder builder = RestClient.builder(hosts);
66+
builder.setHttpClientConfigCallback((httpClientBuilder) -> {
67+
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(httpClientBuilder));
68+
return httpClientBuilder;
69+
});
70+
builder.setRequestConfigCallback((requestConfigBuilder) -> {
71+
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(requestConfigBuilder));
72+
return requestConfigBuilder;
73+
});
74+
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
75+
return builder;
76+
}
77+
78+
private HttpHost createHttpHost(String uri) {
79+
try {
80+
return createHttpHost(URI.create(uri));
81+
}
82+
catch (IllegalArgumentException ex) {
83+
return HttpHost.create(uri);
84+
}
85+
}
86+
87+
private HttpHost createHttpHost(URI uri) {
88+
if (!StringUtils.hasLength(uri.getUserInfo())) {
89+
return HttpHost.create(uri.toString());
90+
}
91+
try {
92+
return HttpHost.create(new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(),
93+
uri.getQuery(), uri.getFragment()).toString());
94+
}
95+
catch (URISyntaxException ex) {
96+
throw new IllegalStateException(ex);
97+
}
98+
}
99+
100+
}
101+
102+
@Configuration(proxyBeanMethods = false)
103+
@ConditionalOnMissingBean(RestHighLevelClient.class)
104+
static class RestHighLevelClientConfiguration {
105+
106+
@Bean
107+
RestHighLevelClient elasticsearchRestHighLevelClient(RestClientBuilder restClientBuilder) {
108+
return new RestHighLevelClient(restClientBuilder);
109+
}
110+
111+
}
112+
113+
@Configuration(proxyBeanMethods = false)
114+
@ConditionalOnClass(Sniffer.class)
115+
@ConditionalOnSingleCandidate(RestHighLevelClient.class)
116+
static class RestClientSnifferConfiguration {
117+
118+
@Bean
119+
@ConditionalOnMissingBean
120+
Sniffer elasticsearchSniffer(RestHighLevelClient client, ElasticsearchRestClientProperties properties) {
121+
SnifferBuilder builder = Sniffer.builder(client.getLowLevelClient());
122+
PropertyMapper get = PropertyMapper.get().alwaysApplyingWhenNonNull();
123+
get.from(properties.getSniffer().getInterval()).asInt(Duration::toMillis)
124+
.to(builder::setSniffIntervalMillis);
125+
get.from(properties.getSniffer().getDelayAfterFailure()).asInt(Duration::toMillis)
126+
.to(builder::setSniffAfterFailureDelayMillis);
127+
return builder.build();
128+
}
129+
130+
}
131+
132+
static class DefaultRestClientBuilderCustomizer implements RestClientBuilderCustomizer {
133+
134+
private static final PropertyMapper map = PropertyMapper.get();
135+
136+
private final ElasticsearchRestClientProperties properties;
137+
138+
DefaultRestClientBuilderCustomizer(ElasticsearchRestClientProperties properties) {
139+
this.properties = properties;
140+
}
141+
142+
@Override
143+
public void customize(RestClientBuilder builder) {
144+
}
145+
146+
@Override
147+
public void customize(HttpAsyncClientBuilder builder) {
148+
builder.setDefaultCredentialsProvider(new PropertiesCredentialsProvider(this.properties));
149+
}
150+
151+
@Override
152+
public void customize(RequestConfig.Builder builder) {
153+
map.from(this.properties::getConnectionTimeout).whenNonNull().asInt(Duration::toMillis)
154+
.to(builder::setConnectTimeout);
155+
map.from(this.properties::getReadTimeout).whenNonNull().asInt(Duration::toMillis)
156+
.to(builder::setSocketTimeout);
157+
}
158+
159+
}
160+
161+
private static class PropertiesCredentialsProvider extends BasicCredentialsProvider {
162+
163+
PropertiesCredentialsProvider(ElasticsearchRestClientProperties properties) {
164+
if (StringUtils.hasText(properties.getUsername())) {
165+
Credentials credentials = new UsernamePasswordCredentials(properties.getUsername(),
166+
properties.getPassword());
167+
setCredentials(AuthScope.ANY, credentials);
168+
}
169+
properties.getUris().stream().map(this::toUri).filter(this::hasUserInfo)
170+
.forEach(this::addUserInfoCredentials);
171+
}
172+
173+
private URI toUri(String uri) {
174+
try {
175+
return URI.create(uri);
176+
}
177+
catch (IllegalArgumentException ex) {
178+
return null;
179+
}
180+
}
181+
182+
private boolean hasUserInfo(URI uri) {
183+
return uri != null && StringUtils.hasLength(uri.getUserInfo());
184+
}
185+
186+
private void addUserInfoCredentials(URI uri) {
187+
AuthScope authScope = new AuthScope(uri.getHost(), uri.getPort());
188+
Credentials credentials = createUserInfoCredentials(uri.getUserInfo());
189+
setCredentials(authScope, credentials);
190+
}
191+
192+
private Credentials createUserInfoCredentials(String userInfo) {
193+
int delimiter = userInfo.indexOf(":");
194+
if (delimiter == -1) {
195+
return new UsernamePasswordCredentials(userInfo, null);
196+
}
197+
String username = userInfo.substring(0, delimiter);
198+
String password = userInfo.substring(delimiter + 1);
199+
return new UsernamePasswordCredentials(username, password);
200+
}
201+
202+
}
203+
204+
}

0 commit comments

Comments
 (0)