From 35caf248285c817f6c459f01f3e4c912a6d87086 Mon Sep 17 00:00:00 2001
From: Jean-Noel Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Tue, 24 Sep 2013 09:07:45 +0000
Subject: [PATCH] OPENDJ-1134 (CR-2338) Introduce a class in replication for encapsulating host+port combinations
---
opends/src/server/org/opends/server/types/HostPort.java | 110 +++++++++++++++++++++++++++---------
opends/tests/unit-tests-testng/src/server/org/opends/server/types/HostPortTest.java | 38 +++++++++---
2 files changed, 111 insertions(+), 37 deletions(-)
diff --git a/opends/src/server/org/opends/server/types/HostPort.java b/opends/src/server/org/opends/server/types/HostPort.java
index a509373..256d642 100644
--- a/opends/src/server/org/opends/server/types/HostPort.java
+++ b/opends/src/server/org/opends/server/types/HostPort.java
@@ -32,14 +32,20 @@
import java.util.HashSet;
import java.util.Set;
+import org.opends.messages.Message;
import org.opends.server.loggers.debug.DebugTracer;
+import static org.opends.messages.ReplicationMessages.*;
+import static org.opends.server.loggers.ErrorLogger.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
/**
- * This class defines a data structure that combines an address and
- * port number, as may be used to accept a connection from or initiate
- * a connection to a remote system.
+ * This class defines a data structure that combines an address and port number,
+ * as may be used to accept a connection from or initiate a connection to a
+ * remote system.
+ * <p>
+ * Due to the possibility of live network configuration changes, instances of
+ * this class are not intended for caching and should be rebuilt on demand.
*/
@org.opends.server.types.PublicAPI(
stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
@@ -48,9 +54,13 @@
mayInvoke=true)
public final class HostPort
{
+
/** The tracer object for the debug logger. */
private static final DebugTracer TRACER = getTracer();
+ /** Constant that represents the local host. */
+ private static final String LOCALHOST = "localhost";
+
/**
* The wildcard address allows to instruct a server to
* "listen to all addresses".
@@ -61,14 +71,22 @@
-
- /** The supplied host for this object. */
+ /**
+ * The supplied host for this object.
+ * <p>
+ * Keeping the supplied host name allows to rebuild the HostPort object in
+ * case the network configuration changed on the current machine.
+ */
private final String host;
/**
* The normalized host for this object.
* <p>
- * Normalization consists of converting local addresses to "localhost".
+ * Normalization consists of:
+ * <ul>
+ * <li>convert all local addresses to "localhost"</li>
+ * <li>convert remote host name / addresses to the equivalent IP address</li>
+ * </ul>
*/
private final String normalizedHost;
@@ -203,7 +221,7 @@
public HostPort(String host, int port)
{
this.host = removeExtraChars(host);
- this.normalizedHost = normalizedHost(this.host);
+ this.normalizedHost = normalizeHost(this.host);
this.port = normalizePort(port);
}
@@ -228,26 +246,36 @@
IllegalArgumentException
{
final int sepIndex = hostPort.lastIndexOf(':');
- if (sepIndex == -1)
+ if ((hostPort.charAt(0) == '['
+ && hostPort.charAt(hostPort.length() - 1) == ']')
+ || sepIndex == -1)
{
throw new IllegalArgumentException(
"Invalid host/port string: no network port was provided in '"
+ hostPort + "'");
}
- if (sepIndex == 0)
+ else if (sepIndex == 0)
{
throw new IllegalArgumentException(
"Invalid host/port string: no host name was provided in '" + hostPort
+ "'");
}
+ else if (hostPort.lastIndexOf(':', sepIndex - 1) != -1
+ && (hostPort.charAt(0) != '[' || hostPort.charAt(sepIndex - 1) != ']'))
+ {
+ throw new IllegalArgumentException(
+ "Invalid host/port string: Suspected IPv6 address provided in '"
+ + hostPort + "'. The only allowed format for providing IPv6 "
+ + "addresses is '[IPv6 address]:port'");
+ }
String host = sepIndex != -1 ? hostPort.substring(0, sepIndex) : hostPort;
- int port = Integer.parseInt(hostPort.substring(sepIndex + 1).trim());
+ int port = Integer.parseInt(hostPort.substring(sepIndex + 1));
return new HostPort(host, port);
}
/**
- * Removes extra characters from the host name: leading and trailing white
- * spaces, and surrounding square brackets for IPv6 addresses.
+ * Removes extra characters from the host name: surrounding square brackets
+ * for IPv6 addresses.
*
* @param host
* the host name to clean
@@ -255,7 +283,6 @@
*/
private String removeExtraChars(String host)
{
- host = host.trim();
final int startsWith = host.indexOf("[");
if (startsWith == -1)
{
@@ -272,13 +299,33 @@
* @return a normalized String representation of the supplied host.
* @see #normalizedHost what host normalization covers
*/
- private String normalizedHost(String host)
+ private String normalizeHost(String host)
{
- if (isLocalAddress(host))
- {
- return "localhost";
+ if (LOCALHOST.equals(host))
+ { // it is already normalized
+ return LOCALHOST;
}
- return host;
+
+ try
+ {
+ final InetAddress inetAddress = InetAddress.getByName(host);
+ if (isLocalAddress(inetAddress))
+ {
+ // normalize to localhost for easier identification.
+ return LOCALHOST;
+ }
+ // else normalize to IP address for easier identification.
+ // FIXME, this does not fix the multi homing issue where a single machine
+ // has several IP addresses
+ return inetAddress.getHostAddress();
+ }
+ catch (UnknownHostException e)
+ {
+ // We could not resolve this host name, default to the provided host name
+ Message message = ERR_COULD_NOT_SOLVE_HOSTNAME.get(host);
+ logError(message);
+ return host;
+ }
}
/**
@@ -329,7 +376,7 @@
*/
public boolean isLocalAddress()
{
- return isLocalAddress(getHost());
+ return LOCALHOST.equals(this.normalizedHost);
}
/**
@@ -452,12 +499,17 @@
return false;
if (obj == this)
return true;
- if (obj instanceof HostPort)
- {
- final HostPort other = (HostPort) obj;
- return toNormalizedString().equals(other.toNormalizedString());
- }
- return false;
+ if (getClass() != obj.getClass())
+ return false;
+
+ HostPort other = (HostPort) obj;
+ if (normalizedHost == null)
+ if (other.normalizedHost != null)
+ return false;
+ else if (!normalizedHost.equals(other.normalizedHost))
+ return false;
+
+ return port == other.port;
}
/**
@@ -468,8 +520,12 @@
@Override
public int hashCode()
{
- return toNormalizedString().hashCode();
+ final int prime = 31;
+ int result = 1;
+ result = prime * result
+ + ((normalizedHost == null) ? 0 : normalizedHost.hashCode());
+ result = prime * result + port;
+ return result;
}
}
-
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/HostPortTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/HostPortTest.java
index f875856..9f8792e 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/HostPortTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/HostPortTest.java
@@ -26,6 +26,7 @@
*/
package org.opends.server.types;
+import org.testng.Assert;
import org.testng.annotations.Test;
import static org.assertj.core.api.Assertions.*;
@@ -35,7 +36,9 @@
public class HostPortTest extends TypesTestCase
{
- public void valueOfIPv4NoSpaces()
+ private static final String IPV6_ADDRESS = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
+
+ public void valueOfHostName()
{
final String serverURL = "home:1";
final HostPort hp = HostPort.valueOf(serverURL);
@@ -44,11 +47,11 @@
assertThat(hp.toString()).isEqualTo(serverURL);
}
- public void valueOfIPv4Spaces()
+ public void valueOfIPv4()
{
- final String serverURL = "home:1";
- final HostPort hp = HostPort.valueOf(" " + serverURL + " ");
- assertThat(hp.getHost()).isEqualTo("home");
+ final String serverURL = "192.168.1.1:1";
+ final HostPort hp = HostPort.valueOf(serverURL);
+ assertThat(hp.getHost()).isEqualTo("192.168.1.1");
assertThat(hp.getPort()).isEqualTo(1);
assertThat(hp.toString()).isEqualTo(serverURL);
}
@@ -56,14 +59,14 @@
public void valueOfEqualsHashCodeIPv4()
{
final HostPort hp1 = HostPort.valueOf("home:1");
- final HostPort hp2 = HostPort.valueOf(" home:1 ");
+ final HostPort hp2 = new HostPort("home", 1);
assertThat(hp1).isEqualTo(hp2);
assertThat(hp1.hashCode()).isEqualTo(hp2.hashCode());
}
public void valueOfIPv6Brackets()
{
- final String hostName = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
+ final String hostName = IPV6_ADDRESS;
final String serverURL = "[" + hostName + "]:389";
final HostPort hp = HostPort.valueOf(serverURL);
assertThat(hp.getHost()).isEqualTo(hostName);
@@ -71,9 +74,10 @@
assertThat(hp.toString()).isEqualTo(serverURL);
}
+ @Test(expectedExceptions = IllegalArgumentException.class)
public void valueOfIPv6NoBrackets()
{
- final String hostName = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
+ final String hostName = IPV6_ADDRESS;
final HostPort hp = HostPort.valueOf(hostName + ":389");
assertThat(hp.getHost()).isEqualTo(hostName);
assertThat(hp.getPort()).isEqualTo(389);
@@ -82,9 +86,9 @@
public void valueOfEqualsHashCodeIPv6()
{
- final String hostName = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
+ final String hostName = IPV6_ADDRESS;
final HostPort hp1 = HostPort.valueOf("[" + hostName + "]:389");
- final HostPort hp2 = HostPort.valueOf(" " + hostName + " : 389 ");
+ final HostPort hp2 = new HostPort(hostName, 389);
assertThat(hp1).isEqualTo(hp2);
assertThat(hp1.hashCode()).isEqualTo(hp2.hashCode());
}
@@ -119,4 +123,18 @@
HostPort.valueOf("host:99999999");
}
+ public void valueOfIPv6NoPort()
+ {
+ try
+ {
+ final String hostName = "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]";
+ HostPort hp = HostPort.valueOf(hostName);
+ assertThat(hp.getHost()).isEqualTo(hostName);
+ }
+ catch (IllegalArgumentException e)
+ {
+ Assert.assertEquals(e.getClass(), IllegalArgumentException.class);
+ }
+ }
+
}
--
Gitblit v1.10.0