Skip to content

Commit e2a355f

Browse files
committed
Consolidate Elasticsearch configuration properties
Previously, a number of Elasticsearch properties were duplicated across the spring.elasticsearch.rest and spring.data.elasticsearch.client.reactive prefixes for configuring the blocking REST client provided by Elasticsearch and the reactive client provided by Spring Data respectively. This could cause problems when using the Elasticsearch REST client configured with a custom spring.elasticsearch.rest.uris. If Spring WebFlux (to make use of WebClient) and Spring Data Elasticsearch were on the classpath, the reactive Elasticsearch Client would be autoconfigured but it would use the default value of its analogous spring.data.elasticsearch.client.reactive.endpoints property. It would be unable to connect, causing a startup failure. This commit consoliates the configuration properties where possible. Each setting that is common across the two clients is now configured using a single, shared spring.elasticsearch property. Each setting that is specific to the blocked REST client or the WebClient-based reactive client now have prefixes of spring.elasticsearch.restclient and spring.elasticsearch.webclient respectively. The old properties beneath spring.elasticsearch.rest and spring.data.elasticsearch.client.reactive have been deprecated. If a any deprecated property is set, all of the new properties are ignored. In other words, to migrate to the new properties, each usage of a now-deprecated property must be updated to use its new replacement instead. Closes spring-projectsgh-23106
1 parent dd366af commit e2a355f

15 files changed

+959
-290
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/*
2+
* Copyright 2012-2021 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.data.elasticsearch;
18+
19+
import java.time.Duration;
20+
import java.util.ArrayList;
21+
import java.util.Collections;
22+
import java.util.List;
23+
24+
import org.springframework.boot.context.properties.ConfigurationProperties;
25+
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
26+
import org.springframework.util.unit.DataSize;
27+
28+
/**
29+
* Deprecated configuration properties for Elasticsearch Reactive REST clients.
30+
*
31+
* @author Brian Clozel
32+
* @deprecated since 2.6.0 for removal in 2.8.0
33+
*/
34+
@Deprecated
35+
@ConfigurationProperties(prefix = "spring.data.elasticsearch.client.reactive")
36+
class DeprecatedReactiveElasticsearchRestClientProperties {
37+
38+
/**
39+
* Comma-separated list of the Elasticsearch endpoints to connect to.
40+
*/
41+
private List<String> endpoints = new ArrayList<>(Collections.singletonList("localhost:9200"));
42+
43+
/**
44+
* Whether the client should use SSL to connect to the endpoints.
45+
*/
46+
private boolean useSsl = false;
47+
48+
/**
49+
* Credentials username.
50+
*/
51+
private String username;
52+
53+
/**
54+
* Credentials password.
55+
*/
56+
private String password;
57+
58+
/**
59+
* Connection timeout.
60+
*/
61+
private Duration connectionTimeout;
62+
63+
/**
64+
* Read and Write Socket timeout.
65+
*/
66+
private Duration socketTimeout;
67+
68+
/**
69+
* Limit on the number of bytes that can be buffered whenever the input stream needs
70+
* to be aggregated.
71+
*/
72+
private DataSize maxInMemorySize;
73+
74+
private boolean customized = false;
75+
76+
@DeprecatedConfigurationProperty(replacement = "spring.elasticsearch.uris")
77+
public List<String> getEndpoints() {
78+
return this.endpoints;
79+
}
80+
81+
public void setEndpoints(List<String> endpoints) {
82+
this.customized = true;
83+
this.endpoints = endpoints;
84+
}
85+
86+
@DeprecatedConfigurationProperty(reason = "Use of SSL should be indicated through an https URI scheme")
87+
public boolean isUseSsl() {
88+
return this.useSsl;
89+
}
90+
91+
public void setUseSsl(boolean useSsl) {
92+
this.customized = true;
93+
this.useSsl = useSsl;
94+
}
95+
96+
@DeprecatedConfigurationProperty(replacement = "spring.elasticsearch.username")
97+
public String getUsername() {
98+
return this.username;
99+
}
100+
101+
public void setUsername(String username) {
102+
this.customized = true;
103+
this.username = username;
104+
}
105+
106+
@DeprecatedConfigurationProperty(replacement = "spring.elasticsearch.password")
107+
public String getPassword() {
108+
return this.password;
109+
}
110+
111+
public void setPassword(String password) {
112+
this.customized = true;
113+
this.password = password;
114+
}
115+
116+
@DeprecatedConfigurationProperty(replacement = "spring.elasticsearch.connection-timeout")
117+
public Duration getConnectionTimeout() {
118+
return this.connectionTimeout;
119+
}
120+
121+
public void setConnectionTimeout(Duration connectionTimeout) {
122+
this.customized = true;
123+
this.connectionTimeout = connectionTimeout;
124+
}
125+
126+
@DeprecatedConfigurationProperty(replacement = "spring.elasticsearch.socket-timeout")
127+
public Duration getSocketTimeout() {
128+
return this.socketTimeout;
129+
}
130+
131+
public void setSocketTimeout(Duration socketTimeout) {
132+
this.customized = true;
133+
this.socketTimeout = socketTimeout;
134+
}
135+
136+
@DeprecatedConfigurationProperty(replacement = "spring.elasticsearch.webclient.max-in-memory-size")
137+
public DataSize getMaxInMemorySize() {
138+
return this.maxInMemorySize;
139+
}
140+
141+
public void setMaxInMemorySize(DataSize maxInMemorySize) {
142+
this.customized = true;
143+
this.maxInMemorySize = maxInMemorySize;
144+
}
145+
146+
boolean isCustomized() {
147+
return this.customized;
148+
}
149+
150+
}

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

Lines changed: 191 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,18 @@
1616

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

19+
import java.net.URI;
20+
import java.time.Duration;
21+
import java.util.List;
22+
import java.util.Set;
23+
import java.util.stream.Collectors;
24+
1925
import reactor.netty.http.client.HttpClient;
2026

2127
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
2228
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2329
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
30+
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchProperties;
2431
import org.springframework.boot.context.properties.EnableConfigurationProperties;
2532
import org.springframework.boot.context.properties.PropertyMapper;
2633
import org.springframework.context.annotation.Bean;
@@ -29,6 +36,7 @@
2936
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
3037
import org.springframework.data.elasticsearch.client.reactive.ReactiveRestClients;
3138
import org.springframework.data.elasticsearch.client.reactive.ReactiveRestClients.WebClientConfigurationCallback;
39+
import org.springframework.util.Assert;
3240
import org.springframework.util.unit.DataSize;
3341
import org.springframework.web.reactive.function.client.ExchangeStrategies;
3442
import org.springframework.web.reactive.function.client.WebClient;
@@ -40,30 +48,39 @@
4048
* @author Brian Clozel
4149
* @since 2.2.0
4250
*/
51+
@SuppressWarnings("deprecation")
4352
@Configuration(proxyBeanMethods = false)
4453
@ConditionalOnClass({ ReactiveRestClients.class, WebClient.class, HttpClient.class })
45-
@EnableConfigurationProperties(ReactiveElasticsearchRestClientProperties.class)
54+
@EnableConfigurationProperties({ ElasticsearchProperties.class, ReactiveElasticsearchRestClientProperties.class,
55+
DeprecatedReactiveElasticsearchRestClientProperties.class })
4656
public class ReactiveElasticsearchRestClientAutoConfiguration {
4757

58+
private final ConsolidatedProperties properties;
59+
60+
ReactiveElasticsearchRestClientAutoConfiguration(ElasticsearchProperties properties,
61+
ReactiveElasticsearchRestClientProperties restClientProperties,
62+
DeprecatedReactiveElasticsearchRestClientProperties reactiveProperties) {
63+
this.properties = new ConsolidatedProperties(properties, restClientProperties, reactiveProperties);
64+
}
65+
4866
@Bean
4967
@ConditionalOnMissingBean
50-
public ClientConfiguration clientConfiguration(ReactiveElasticsearchRestClientProperties properties) {
68+
public ClientConfiguration clientConfiguration() {
5169
ClientConfiguration.MaybeSecureClientConfigurationBuilder builder = ClientConfiguration.builder()
52-
.connectedTo(properties.getEndpoints().toArray(new String[0]));
70+
.connectedTo(this.properties.getEndpoints().toArray(new String[0]));
5371
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
54-
map.from(properties.isUseSsl()).whenTrue().toCall(builder::usingSsl);
55-
map.from(properties.getUsername()).whenHasText()
56-
.to((username) -> builder.withBasicAuth(username, properties.getPassword()));
57-
map.from(properties.getConnectionTimeout()).to(builder::withConnectTimeout);
58-
map.from(properties.getSocketTimeout()).to(builder::withSocketTimeout);
59-
configureExchangeStrategies(map, builder, properties);
72+
map.from(this.properties.isUseSsl()).whenTrue().toCall(builder::usingSsl);
73+
map.from(this.properties.getCredentials())
74+
.to((credentials) -> builder.withBasicAuth(credentials.getUsername(), credentials.getPassword()));
75+
map.from(this.properties.getConnectionTimeout()).to(builder::withConnectTimeout);
76+
map.from(this.properties.getSocketTimeout()).to(builder::withSocketTimeout);
77+
configureExchangeStrategies(map, builder);
6078
return builder.build();
6179
}
6280

6381
private void configureExchangeStrategies(PropertyMapper map,
64-
ClientConfiguration.TerminalClientConfigurationBuilder builder,
65-
ReactiveElasticsearchRestClientProperties properties) {
66-
map.from(properties.getMaxInMemorySize()).asInt(DataSize::toBytes).to((maxInMemorySize) -> {
82+
ClientConfiguration.TerminalClientConfigurationBuilder builder) {
83+
map.from(this.properties.getMaxInMemorySize()).asInt(DataSize::toBytes).to((maxInMemorySize) -> {
6784
builder.withClientConfigurer(WebClientConfigurationCallback.from((webClient) -> {
6885
ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
6986
.codecs((configurer) -> configurer.defaultCodecs().maxInMemorySize(maxInMemorySize)).build();
@@ -78,4 +95,166 @@ public ReactiveElasticsearchClient reactiveElasticsearchClient(ClientConfigurati
7895
return ReactiveRestClients.create(clientConfiguration);
7996
}
8097

98+
private static final class ConsolidatedProperties {
99+
100+
private final ElasticsearchProperties properties;
101+
102+
private final ReactiveElasticsearchRestClientProperties restClientProperties;
103+
104+
private final DeprecatedReactiveElasticsearchRestClientProperties deprecatedProperties;
105+
106+
private final List<URI> uris;
107+
108+
private ConsolidatedProperties(ElasticsearchProperties properties,
109+
ReactiveElasticsearchRestClientProperties restClientProperties,
110+
DeprecatedReactiveElasticsearchRestClientProperties deprecatedreactiveProperties) {
111+
this.properties = properties;
112+
this.restClientProperties = restClientProperties;
113+
this.deprecatedProperties = deprecatedreactiveProperties;
114+
this.uris = properties.getUris().stream().map((s) -> s.startsWith("http") ? s : "http://" + s)
115+
.map(URI::create).collect(Collectors.toList());
116+
}
117+
118+
private List<String> getEndpoints() {
119+
if (this.deprecatedProperties.isCustomized()) {
120+
return this.deprecatedProperties.getEndpoints();
121+
}
122+
return this.uris.stream().map((uri) -> uri.getHost() + ":" + uri.getPort()).collect(Collectors.toList());
123+
}
124+
125+
private Credentials getCredentials() {
126+
if (this.deprecatedProperties.isCustomized()) {
127+
return Credentials.from(this.deprecatedProperties);
128+
}
129+
Credentials propertyCredentials = Credentials.from(this.properties);
130+
Credentials uriCredentials = Credentials.from(this.properties.getUris());
131+
if (uriCredentials == null) {
132+
return propertyCredentials;
133+
}
134+
if (propertyCredentials != null && !uriCredentials.equals(propertyCredentials)) {
135+
throw new IllegalArgumentException(
136+
"Credentials from URI user info do not match those from spring.elasticsearch.username and "
137+
+ "spring.elasticsearch.password");
138+
}
139+
return uriCredentials;
140+
141+
}
142+
143+
private Duration getConnectionTimeout() {
144+
return this.deprecatedProperties.isCustomized() ? this.deprecatedProperties.getConnectionTimeout()
145+
: this.properties.getConnectionTimeout();
146+
}
147+
148+
private Duration getSocketTimeout() {
149+
return this.deprecatedProperties.isCustomized() ? this.deprecatedProperties.getSocketTimeout()
150+
: this.properties.getSocketTimeout();
151+
}
152+
153+
private boolean isUseSsl() {
154+
if (this.deprecatedProperties.isCustomized()) {
155+
return this.deprecatedProperties.isUseSsl();
156+
}
157+
Set<String> schemes = this.uris.stream().map((uri) -> uri.getScheme()).collect(Collectors.toSet());
158+
Assert.isTrue(schemes.size() == 1, () -> "Configured Elasticsearch URIs have varying schemes");
159+
return schemes.iterator().next().equals("https");
160+
}
161+
162+
private DataSize getMaxInMemorySize() {
163+
return this.deprecatedProperties.isCustomized() ? this.deprecatedProperties.getMaxInMemorySize()
164+
: this.restClientProperties.getMaxInMemorySize();
165+
}
166+
167+
private static final class Credentials {
168+
169+
private final String username;
170+
171+
private final String password;
172+
173+
private Credentials(String username, String password) {
174+
this.username = username;
175+
this.password = password;
176+
}
177+
178+
private String getUsername() {
179+
return this.username;
180+
}
181+
182+
private String getPassword() {
183+
return this.password;
184+
}
185+
186+
private static Credentials from(List<String> uris) {
187+
Set<String> userInfos = uris.stream().map(URI::create).map((uri) -> uri.getUserInfo())
188+
.collect(Collectors.toSet());
189+
Assert.isTrue(userInfos.size() == 1, () -> "Configured Elasticsearch URIs have varying user infos");
190+
String userInfo = userInfos.iterator().next();
191+
if (userInfo != null) {
192+
String[] parts = userInfo.split(":");
193+
return new Credentials(parts[0], (parts.length == 2) ? parts[1] : "");
194+
}
195+
return null;
196+
}
197+
198+
private static Credentials from(ElasticsearchProperties properties) {
199+
String username = properties.getUsername();
200+
String password = properties.getPassword();
201+
if (username == null && password == null) {
202+
return null;
203+
}
204+
return new Credentials(username, password);
205+
}
206+
207+
private static Credentials from(DeprecatedReactiveElasticsearchRestClientProperties properties) {
208+
String username = properties.getUsername();
209+
String password = properties.getPassword();
210+
if (username == null && password == null) {
211+
return null;
212+
}
213+
return new Credentials(username, password);
214+
}
215+
216+
@Override
217+
public boolean equals(Object obj) {
218+
if (this == obj) {
219+
return true;
220+
}
221+
if (obj == null) {
222+
return false;
223+
}
224+
if (getClass() != obj.getClass()) {
225+
return false;
226+
}
227+
Credentials other = (Credentials) obj;
228+
if (this.password == null) {
229+
if (other.password != null) {
230+
return false;
231+
}
232+
}
233+
else if (!this.password.equals(other.password)) {
234+
return false;
235+
}
236+
if (this.username == null) {
237+
if (other.username != null) {
238+
return false;
239+
}
240+
}
241+
else if (!this.username.equals(other.username)) {
242+
return false;
243+
}
244+
return true;
245+
}
246+
247+
@Override
248+
public int hashCode() {
249+
final int prime = 31;
250+
int result = 1;
251+
result = prime * result + ((this.password == null) ? 0 : this.password.hashCode());
252+
result = prime * result + ((this.username == null) ? 0 : this.username.hashCode());
253+
return result;
254+
}
255+
256+
}
257+
258+
}
259+
81260
}

0 commit comments

Comments
 (0)