DATAMONGO-808 - Improve ServerAddressPropertyEditor to support IPv6 addresses.
Improved parsing of ServerAddress to be able to handle IPv6 addresses correctly. We now use the actor ServerAddress(InetAddress) to be able to pass an IPv6 address. The constructor which takes a String as the hostname can't deal with IPv6 addresses directly because it tries to extract a port at the wrong location of such an address. This change should not change the behavior too much, since the constructor ServerAddress(String, int) already calls InetAddress.getByName(...) internally. Original pull request: #103.
This commit is contained in:
committed by
Oliver Gierke
parent
57c7524c77
commit
8ff1913ec7
@@ -16,12 +16,14 @@
|
||||
package org.springframework.data.mongodb.config;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.ServerAddress;
|
||||
@@ -35,6 +37,11 @@ import com.mongodb.ServerAddress;
|
||||
*/
|
||||
public class ServerAddressPropertyEditor extends PropertyEditorSupport {
|
||||
|
||||
/**
|
||||
* A port is a number without a leading 0 at the end of the address that is proceeded by just a single :.
|
||||
*/
|
||||
private static final String HOST_PORT_SPLIT_PATTERN = "(?<!:):(?=[123456789]\\d*$)";
|
||||
private static final String COULD_NOT_PARSE_ADDRESS_MESSAGE = "Could not parse address {} '{}'. Check your replica set configuration!";
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ServerAddressPropertyEditor.class);
|
||||
|
||||
/*
|
||||
@@ -77,22 +84,53 @@ public class ServerAddressPropertyEditor extends PropertyEditorSupport {
|
||||
*/
|
||||
private ServerAddress parseServerAddress(String source) {
|
||||
|
||||
String[] hostAndPort = StringUtils.delimitedListToStringArray(source.trim(), ":");
|
||||
if (!StringUtils.hasText(source)) {
|
||||
LOG.warn(COULD_NOT_PARSE_ADDRESS_MESSAGE, "source", source);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!StringUtils.hasText(source) || hostAndPort.length > 2) {
|
||||
LOG.warn("Could not parse address source '{}'. Check your replica set configuration!", source);
|
||||
String[] hostAndPort = extractHostAddressAndPort(source.trim());
|
||||
|
||||
if (hostAndPort.length > 2) {
|
||||
LOG.warn(COULD_NOT_PARSE_ADDRESS_MESSAGE, "source", source);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return hostAndPort.length == 1 ? new ServerAddress(hostAndPort[0]) : new ServerAddress(hostAndPort[0],
|
||||
Integer.parseInt(hostAndPort[1]));
|
||||
InetAddress hostAddress = InetAddress.getByName(hostAndPort[0]);
|
||||
Integer port = hostAndPort.length == 1 ? null : Integer.parseInt(hostAndPort[1]);
|
||||
|
||||
return port == null ? new ServerAddress(hostAddress) : new ServerAddress(hostAddress, port);
|
||||
} catch (UnknownHostException e) {
|
||||
LOG.warn("Could not parse host '{}'. Check your replica set configuration!", hostAndPort[0]);
|
||||
LOG.warn(COULD_NOT_PARSE_ADDRESS_MESSAGE, "host", hostAndPort[0]);
|
||||
} catch (NumberFormatException e) {
|
||||
LOG.warn("Could not parse port '{}'. Check your replica set configuration!", hostAndPort[1]);
|
||||
LOG.warn(COULD_NOT_PARSE_ADDRESS_MESSAGE, "port", hostAndPort[1]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the host and port from the given {@link String}.
|
||||
*
|
||||
* @param addressAndPortSource must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private String[] extractHostAddressAndPort(String addressAndPortSource) {
|
||||
|
||||
Assert.notNull(addressAndPortSource, "Address and port source must not be null!");
|
||||
|
||||
String[] hostAndPort = addressAndPortSource.split(HOST_PORT_SPLIT_PATTERN);
|
||||
String hostAddress = hostAndPort[0];
|
||||
|
||||
if (isHostAddressInIPv6BracketNotation(hostAddress)) {
|
||||
hostAndPort[0] = hostAddress.substring(1, hostAddress.length() - 1);
|
||||
}
|
||||
|
||||
return hostAndPort;
|
||||
}
|
||||
|
||||
private boolean isHostAddressInIPv6BracketNotation(String hostAddress) {
|
||||
return hostAddress.startsWith("[") && hostAddress.endsWith("]");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.data.mongodb.config;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Ignore;
|
||||
@@ -40,8 +41,7 @@ import com.mongodb.ServerAddress;
|
||||
@ContextConfiguration
|
||||
public class MongoNamespaceReplicaSetTests {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext ctx;
|
||||
@Autowired private ApplicationContext ctx;
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -53,7 +53,10 @@ public class MongoNamespaceReplicaSetTests {
|
||||
List<ServerAddress> replicaSetSeeds = (List<ServerAddress>) ReflectionTestUtils.getField(mfb, "replicaSetSeeds");
|
||||
|
||||
assertThat(replicaSetSeeds, is(notNullValue()));
|
||||
assertThat(replicaSetSeeds, hasItems(new ServerAddress("127.0.0.1", 10001), new ServerAddress("localhost", 10002)));
|
||||
assertThat(
|
||||
replicaSetSeeds,
|
||||
hasItems(new ServerAddress(InetAddress.getByName("127.0.0.1"), 10001),
|
||||
new ServerAddress(InetAddress.getByName("localhost"), 10002)));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -18,12 +18,15 @@ package org.springframework.data.mongodb.config;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import com.mongodb.ServerAddress;
|
||||
|
||||
@@ -35,6 +38,8 @@ import com.mongodb.ServerAddress;
|
||||
*/
|
||||
public class ServerAddressPropertyEditorUnitTests {
|
||||
|
||||
@Rule public ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
ServerAddressPropertyEditor editor;
|
||||
|
||||
@Before
|
||||
@@ -81,11 +86,111 @@ public class ServerAddressPropertyEditorUnitTests {
|
||||
assertNull(editor.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-808
|
||||
*/
|
||||
@Test
|
||||
public void handleIPv6HostaddressLoopbackShort() throws UnknownHostException {
|
||||
|
||||
String hostAddress = "::1";
|
||||
editor.setAsText(hostAddress);
|
||||
|
||||
assertSingleAddressWithPort(hostAddress, null, editor.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-808
|
||||
*/
|
||||
@Test
|
||||
public void handleIPv6HostaddressLoopbackShortWithPort() throws UnknownHostException {
|
||||
|
||||
String hostAddress = "::1";
|
||||
int port = 27017;
|
||||
editor.setAsText(hostAddress + ":" + port);
|
||||
|
||||
assertSingleAddressWithPort(hostAddress, port, editor.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Here we detect no port since the last segment of the address contains leading zeros.
|
||||
*
|
||||
* @see DATAMONGO-808
|
||||
*/
|
||||
@Test
|
||||
public void handleIPv6HostaddressLoopbackLong() throws UnknownHostException {
|
||||
|
||||
String hostAddress = "0000:0000:0000:0000:0000:0000:0000:0001";
|
||||
editor.setAsText(hostAddress);
|
||||
|
||||
assertSingleAddressWithPort(hostAddress, null, editor.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-808
|
||||
*/
|
||||
@Test
|
||||
public void handleIPv6HostaddressLoopbackLongWithBrackets() throws UnknownHostException {
|
||||
|
||||
String hostAddress = "[0000:0000:0000:0000:0000:0000:0000:0001]";
|
||||
editor.setAsText(hostAddress);
|
||||
|
||||
assertSingleAddressWithPort(hostAddress, null, editor.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* We can't tell whether the last part of the hostAddress represents a port or not.
|
||||
*
|
||||
* @see DATAMONGO-808
|
||||
*/
|
||||
@Test
|
||||
public void shouldFailToHandleAmbiguousIPv6HostaddressLongWithoutPortAndWithoutBrackets() throws UnknownHostException {
|
||||
|
||||
expectedException.expect(IllegalArgumentException.class);
|
||||
|
||||
String hostAddress = "0000:0000:0000:0000:0000:0000:0000:128";
|
||||
editor.setAsText(hostAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-808
|
||||
*/
|
||||
@Test
|
||||
public void handleIPv6HostaddressExampleAddressWithPort() throws UnknownHostException {
|
||||
|
||||
String hostAddress = "0000:0000:0000:0000:0000:0000:0000:0001";
|
||||
int port = 27017;
|
||||
editor.setAsText(hostAddress + ":" + port);
|
||||
|
||||
assertSingleAddressWithPort(hostAddress, port, editor.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-808
|
||||
*/
|
||||
@Test
|
||||
public void handleIPv6HostaddressExampleAddressInBracketsWithPort() throws UnknownHostException {
|
||||
|
||||
String hostAddress = "[0000:0000:0000:0000:0000:0000:0000:0001]";
|
||||
int port = 27017;
|
||||
editor.setAsText(hostAddress + ":" + port);
|
||||
|
||||
assertSingleAddressWithPort(hostAddress, port, editor.getValue());
|
||||
}
|
||||
|
||||
private static void assertSingleAddressOfLocalhost(Object result) throws UnknownHostException {
|
||||
assertSingleAddressWithPort("localhost", null, result);
|
||||
}
|
||||
|
||||
private static void assertSingleAddressWithPort(String hostAddress, Integer port, Object result)
|
||||
throws UnknownHostException {
|
||||
|
||||
assertThat(result, is(instanceOf(ServerAddress[].class)));
|
||||
Collection<ServerAddress> addresses = Arrays.asList((ServerAddress[]) result);
|
||||
assertThat(addresses, hasSize(1));
|
||||
assertThat(addresses, hasItem(new ServerAddress("localhost")));
|
||||
if (port == null) {
|
||||
assertThat(addresses, hasItem(new ServerAddress(InetAddress.getByName(hostAddress))));
|
||||
} else {
|
||||
assertThat(addresses, hasItem(new ServerAddress(InetAddress.getByName(hostAddress), port)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user