Browse Source

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.
pull/103/merge
Thomas Darimont 12 years ago committed by Oliver Gierke
parent
commit
f3b31fc467
  1. 52
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ServerAddressPropertyEditor.java
  2. 9
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceReplicaSetTests.java
  3. 107
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/ServerAddressPropertyEditorUnitTests.java

52
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ServerAddressPropertyEditor.java

@ -16,12 +16,14 @@
package org.springframework.data.mongodb.config; package org.springframework.data.mongodb.config;
import java.beans.PropertyEditorSupport; import java.beans.PropertyEditorSupport;
import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import com.mongodb.ServerAddress; import com.mongodb.ServerAddress;
@ -35,6 +37,11 @@ import com.mongodb.ServerAddress;
*/ */
public class ServerAddressPropertyEditor extends PropertyEditorSupport { 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); private static final Logger LOG = LoggerFactory.getLogger(ServerAddressPropertyEditor.class);
/* /*
@ -77,22 +84,53 @@ public class ServerAddressPropertyEditor extends PropertyEditorSupport {
*/ */
private ServerAddress parseServerAddress(String source) { 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;
}
String[] hostAndPort = extractHostAddressAndPort(source.trim());
if (!StringUtils.hasText(source) || hostAndPort.length > 2) { if (hostAndPort.length > 2) {
LOG.warn("Could not parse address source '{}'. Check your replica set configuration!", source); LOG.warn(COULD_NOT_PARSE_ADDRESS_MESSAGE, "source", source);
return null; return null;
} }
try { try {
return hostAndPort.length == 1 ? new ServerAddress(hostAndPort[0]) : new ServerAddress(hostAndPort[0], InetAddress hostAddress = InetAddress.getByName(hostAndPort[0]);
Integer.parseInt(hostAndPort[1])); Integer port = hostAndPort.length == 1 ? null : Integer.parseInt(hostAndPort[1]);
return port == null ? new ServerAddress(hostAddress) : new ServerAddress(hostAddress, port);
} catch (UnknownHostException e) { } 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) { } 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; 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("]");
}
} }

9
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceReplicaSetTests.java

@ -19,6 +19,7 @@ package org.springframework.data.mongodb.config;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.net.InetAddress;
import java.util.List; import java.util.List;
import org.junit.Ignore; import org.junit.Ignore;
@ -40,8 +41,7 @@ import com.mongodb.ServerAddress;
@ContextConfiguration @ContextConfiguration
public class MongoNamespaceReplicaSetTests { public class MongoNamespaceReplicaSetTests {
@Autowired @Autowired private ApplicationContext ctx;
private ApplicationContext ctx;
@Test @Test
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -53,7 +53,10 @@ public class MongoNamespaceReplicaSetTests {
List<ServerAddress> replicaSetSeeds = (List<ServerAddress>) ReflectionTestUtils.getField(mfb, "replicaSetSeeds"); List<ServerAddress> replicaSetSeeds = (List<ServerAddress>) ReflectionTestUtils.getField(mfb, "replicaSetSeeds");
assertThat(replicaSetSeeds, is(notNullValue())); 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 @Test

107
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/ServerAddressPropertyEditorUnitTests.java

@ -18,12 +18,15 @@ package org.springframework.data.mongodb.config;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException;
import com.mongodb.ServerAddress; import com.mongodb.ServerAddress;
@ -35,6 +38,8 @@ import com.mongodb.ServerAddress;
*/ */
public class ServerAddressPropertyEditorUnitTests { public class ServerAddressPropertyEditorUnitTests {
@Rule public ExpectedException expectedException = ExpectedException.none();
ServerAddressPropertyEditor editor; ServerAddressPropertyEditor editor;
@Before @Before
@ -81,11 +86,111 @@ public class ServerAddressPropertyEditorUnitTests {
assertNull(editor.getValue()); 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 { 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))); assertThat(result, is(instanceOf(ServerAddress[].class)));
Collection<ServerAddress> addresses = Arrays.asList((ServerAddress[]) result); Collection<ServerAddress> addresses = Arrays.asList((ServerAddress[]) result);
assertThat(addresses, hasSize(1)); 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)));
}
} }
} }

Loading…
Cancel
Save