Skip to content

Commit 4694553

Browse files
committed
DATAES-470 - Fixed parsing of cluster nodes in TransportClientFactoryBean.
Extracted ClusterNodes value object to capture the parsing logic and actually properly test it. Added unit tests to verify the proper rejection and the two cases outlined in the ticket. Related tickets: DATAES-283.
1 parent cdbc832 commit 4694553

File tree

3 files changed

+201
-25
lines changed

3 files changed

+201
-25
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright 2018 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+
* http://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+
package org.springframework.data.elasticsearch.client;
17+
18+
import java.net.InetAddress;
19+
import java.net.UnknownHostException;
20+
import java.util.Arrays;
21+
import java.util.Iterator;
22+
import java.util.List;
23+
import java.util.stream.Collectors;
24+
25+
import org.elasticsearch.common.transport.TransportAddress;
26+
import org.springframework.data.util.Streamable;
27+
import org.springframework.util.Assert;
28+
import org.springframework.util.StringUtils;
29+
30+
/**
31+
* Value object to represent a list of cluster nodes.
32+
*
33+
* @author Oliver Gierke
34+
* @since 3.1
35+
*/
36+
class ClusterNodes implements Streamable<TransportAddress> {
37+
38+
public static ClusterNodes DEFAULT = ClusterNodes.of("127.0.0.1:9300");
39+
40+
private static final String COLON = ":";
41+
private static final String COMMA = ",";
42+
43+
private final List<TransportAddress> clusterNodes;
44+
45+
/**
46+
* Creates a new {@link ClusterNodes} by parsing the given source.
47+
*
48+
* @param source must not be {@literal null} or empty.
49+
*/
50+
private ClusterNodes(String source) {
51+
52+
Assert.hasText(source, "Cluster nodes source must not be null or empty!");
53+
54+
String[] nodes = StringUtils.delimitedListToStringArray(source, COMMA);
55+
56+
this.clusterNodes = Arrays.stream(nodes).map(node -> {
57+
58+
String[] segments = StringUtils.delimitedListToStringArray(node, COLON);
59+
60+
Assert.isTrue(segments.length == 2,
61+
() -> String.format("Invalid cluster node %s in %s! Must be in the format host:port!", node, source));
62+
63+
String host = segments[0].trim();
64+
String port = segments[1].trim();
65+
66+
Assert.hasText(host, () -> String.format("No host name given cluster node %s!", node));
67+
Assert.hasText(port, () -> String.format("No port given in cluster node %s!", node));
68+
69+
return new TransportAddress(toInetAddress(host), Integer.valueOf(port));
70+
71+
}).collect(Collectors.toList());
72+
}
73+
74+
/**
75+
* Creates a new {@link ClusterNodes} by parsing the given source. The expected format is a comma separated list of
76+
* host-port-combinations separated by a colon: {@code host:port,host:port,…}.
77+
*
78+
* @param source must not be {@literal null} or empty.
79+
* @return
80+
*/
81+
public static ClusterNodes of(String source) {
82+
return new ClusterNodes(source);
83+
}
84+
85+
/*
86+
* (non-Javadoc)
87+
* @see java.lang.Iterable#iterator()
88+
*/
89+
@Override
90+
public Iterator<TransportAddress> iterator() {
91+
return clusterNodes.iterator();
92+
}
93+
94+
private static InetAddress toInetAddress(String host) {
95+
96+
try {
97+
return InetAddress.getByName(host);
98+
} catch (UnknownHostException o_O) {
99+
throw new IllegalArgumentException(o_O);
100+
}
101+
}
102+
}

src/main/java/org/springframework/data/elasticsearch/client/TransportClientFactoryBean.java

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,16 @@
1515
*/
1616
package org.springframework.data.elasticsearch.client;
1717

18-
import java.net.InetAddress;
1918
import java.util.Properties;
2019

2120
import org.elasticsearch.client.transport.TransportClient;
2221
import org.elasticsearch.common.settings.Settings;
23-
import org.elasticsearch.common.transport.TransportAddress;
2422
import org.elasticsearch.transport.client.PreBuiltTransportClient;
2523
import org.slf4j.Logger;
2624
import org.slf4j.LoggerFactory;
2725
import org.springframework.beans.factory.DisposableBean;
2826
import org.springframework.beans.factory.FactoryBean;
29-
import org.springframework.beans.factory.InitializingBean;
30-
import org.springframework.util.Assert;
31-
import org.springframework.util.StringUtils;
27+
import org.springframework.beans.factory.InitializingBean;
3228

3329
/**
3430
* TransportClientFactoryBean
@@ -38,21 +34,19 @@
3834
* @author Jakub Vavrik
3935
* @author Piotr Betkier
4036
* @author Ilkang Na
37+
* @author Oliver Gierke
4138
*/
42-
4339
public class TransportClientFactoryBean implements FactoryBean<TransportClient>, InitializingBean, DisposableBean {
4440

4541
private static final Logger logger = LoggerFactory.getLogger(TransportClientFactoryBean.class);
46-
private String clusterNodes = "127.0.0.1:9300";
42+
private ClusterNodes clusterNodes = ClusterNodes.of("127.0.0.1:9300");
4743
private String clusterName = "elasticsearch";
4844
private Boolean clientTransportSniff = true;
4945
private Boolean clientIgnoreClusterName = Boolean.FALSE;
5046
private String clientPingTimeout = "5s";
5147
private String clientNodesSamplerInterval = "5s";
5248
private TransportClient client;
5349
private Properties properties;
54-
static final String COLON = ":";
55-
static final String COMMA = ",";
5650

5751
@Override
5852
public void destroy() throws Exception {
@@ -89,21 +83,11 @@ public void afterPropertiesSet() throws Exception {
8983
protected void buildClient() throws Exception {
9084

9185
client = new PreBuiltTransportClient(settings());
92-
Assert.hasText(clusterNodes, "[Assertion failed] clusterNodes settings missing.");
93-
String[] clusterNodesArray = StringUtils.split(clusterNodes, COMMA);
94-
if (clusterNodesArray != null) {
95-
for (String clusterNode : clusterNodesArray) {
96-
if (clusterNode != null) {
97-
int colonPosition = clusterName.lastIndexOf(COLON);
98-
String hostName = colonPosition != -1 ? clusterNode.substring(0, colonPosition) : clusterNode;
99-
String port = colonPosition != -1 ? clusterNode.substring(colonPosition, clusterNode.length()) : "";
100-
Assert.hasText(hostName, "[Assertion failed] missing host name in 'clusterNodes'");
101-
Assert.hasText(port, "[Assertion failed] missing port in 'clusterNodes'");
102-
logger.info("adding transport node : " + clusterNode);
103-
client.addTransportAddress(new TransportAddress(InetAddress.getByName(hostName), Integer.valueOf(port)));
104-
}
105-
}
106-
}
86+
87+
clusterNodes.stream() //
88+
.peek(it -> logger.info("Adding transport node : " + it.toString())) //
89+
.forEach(client::addTransportAddress);
90+
10791
client.connectedNodes();
10892
}
10993

@@ -127,7 +111,7 @@ private Settings settings() {
127111
}
128112

129113
public void setClusterNodes(String clusterNodes) {
130-
this.clusterNodes = clusterNodes;
114+
this.clusterNodes = ClusterNodes.of(clusterNodes);
131115
}
132116

133117
public void setClusterName(String clusterName) {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright 2018 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+
* http://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+
package org.springframework.data.elasticsearch.client;
17+
18+
import static org.assertj.core.api.Assertions.*;
19+
20+
import java.util.List;
21+
22+
import org.elasticsearch.common.transport.TransportAddress;
23+
import org.junit.Test;
24+
25+
/**
26+
* Unit tests for {@link ClusterNodes}.
27+
*
28+
* @author Oliver Gierke
29+
*/
30+
public class ClusterNodesUnitTests {
31+
32+
@Test // DATAES-470
33+
public void parsesSingleClusterNode() {
34+
35+
ClusterNodes nodes = ClusterNodes.DEFAULT;
36+
37+
assertThat(nodes).hasSize(1) //
38+
.first().satisfies(it -> {
39+
assertThat(it.getAddress()).isEqualTo("127.0.0.1");
40+
assertThat(it.getPort()).isEqualTo(9300);
41+
});
42+
}
43+
44+
@Test // DATAES-470
45+
public void parsesMultiClusterNode() {
46+
47+
ClusterNodes nodes = ClusterNodes.of("127.0.0.1:1234,10.1.0.1:5678");
48+
49+
assertThat(nodes.stream()).hasSize(2); //
50+
assertThat(nodes.stream()).element(0).satisfies(it -> {
51+
assertThat(it.getAddress()).isEqualTo("127.0.0.1");
52+
assertThat(it.getPort()).isEqualTo(1234);
53+
});
54+
assertThat(nodes.stream()).element(1).satisfies(it -> {
55+
assertThat(it.getAddress()).isEqualTo("10.1.0.1");
56+
assertThat(it.getPort()).isEqualTo(5678);
57+
});
58+
}
59+
60+
@Test // DATAES-470
61+
public void rejectsEmptyHostName() {
62+
63+
assertThatExceptionOfType(IllegalArgumentException.class) //
64+
.isThrownBy(() -> ClusterNodes.of(":8080")) //
65+
.withMessageContaining("host");
66+
}
67+
68+
@Test // DATAES-470
69+
public void rejectsEmptyPort() {
70+
71+
assertThatExceptionOfType(IllegalArgumentException.class) //
72+
.isThrownBy(() -> ClusterNodes.of("localhost:")) //
73+
.withMessageContaining("port");
74+
}
75+
76+
@Test // DATAES-470
77+
public void rejectsMissingPort() {
78+
79+
assertThatExceptionOfType(IllegalArgumentException.class) //
80+
.isThrownBy(() -> ClusterNodes.of("localhost")) //
81+
.withMessageContaining("host:port");
82+
}
83+
84+
@Test // DATAES-470
85+
public void rejectsUnresolvableHost() {
86+
87+
assertThatExceptionOfType(IllegalArgumentException.class) //
88+
.isThrownBy(() -> ClusterNodes.of("mylocalhost:80"));
89+
}
90+
}

0 commit comments

Comments
 (0)