diff --git a/README.md b/README.md
index e41bf3f..c31fa1b 100644
--- a/README.md
+++ b/README.md
@@ -76,6 +76,17 @@ UUID uuid = gen.generate();
UUID anotherUuid = gen.generate();
```
+If your machine has a standard IP networking setup, the `Generators.defaultTimeBasedGenerator` (added in JUG 4.2)
+factory method will try to determine which network interface corresponds to the default route for
+all outgoing network traffic, and use that for creating a time based generator.
+This is likely a good choice for common usage scenarios if you want a version 1 UUID generator.
+
+```java
+TimeBasedGenerator gen = Generators.defaultTimeBasedGenerator();
+UUID uuid = gen.generate();
+UUID anotherUuid = gen.generate();
+```
+
Generators are fully thread-safe, so a single instance may be shared among multiple threads.
Javadocs for further information can be found from [Project Wiki](../../wiki).
diff --git a/release-notes/CREDITS b/release-notes/CREDITS
index 457fd84..80d1889 100644
--- a/release-notes/CREDITS
+++ b/release-notes/CREDITS
@@ -126,3 +126,7 @@ Hal Hildebrand (Hellblazer@github)
Dirk-Jan Rutten (excitement-engineer@github)
* Reported #69: UUID version 7 implementation sorting incorrect?
[4.1.1]
+
+Paul Galbraith (pgalbraith@github)
+ * Contributed #52: Add `Generators.egressTimeBasedGenerator()` method that constructs
+ `TimedBasedGenerator` with a sensible choice of interface
diff --git a/src/main/java/com/fasterxml/uuid/EgressInterfaceFinder.java b/src/main/java/com/fasterxml/uuid/EgressInterfaceFinder.java
new file mode 100644
index 0000000..3346822
--- /dev/null
+++ b/src/main/java/com/fasterxml/uuid/EgressInterfaceFinder.java
@@ -0,0 +1,416 @@
+package com.fasterxml.uuid;
+
+import java.io.IOException;
+import java.net.*;
+import java.util.*;
+
+import static java.lang.String.format;
+
+/**
+ * A utility to attempt to find the default egress interface on the current
+ * system. The egress interface is the interface which is assigned the default
+ * network route, such that outbound network traffic is routed out through that
+ * interface.
+ *
+ * @since 4.2
+ */
+public class EgressInterfaceFinder {
+
+ public static final int DEFAULT_TIMEOUT_MILLIS = 5000;
+
+ /**
+ * Attempt to find the default egress interface on the current system.
+ *
+ *
This is done on a best efforts basis, as Java does not provide the
+ * necessary level of OS integration that is required to do this robustly.
+ * However, this utility should do a decent job on Windows, Linux and macOS
+ * so long as the local system has a working network connection at the time
+ * of execution. If the current system is multihomed with multiple egress
+ * interfaces, one such interface will be chosen indeterminately.
+ *
+ *
Accurately determining the egress interface necessitates us attempting
+ * to make outbound network connections. This will be done
+ * synchronously and can be a very slow process. You can tune the amount of
+ * time allowed to establish the outbound connections by
+ * increasing/decreasing the timeout value.
+ *
+ * @return the egress interface
+ * @throws EgressResolutionException if an egress interface could not be
+ * determined
+ * @since 4.2
+ */
+ public NetworkInterface egressInterface() throws EgressResolutionException {
+ return fromDefaultMechanisms(DEFAULT_TIMEOUT_MILLIS);
+ }
+
+ /**
+ * Attempt to find the default egress interface on the current system,
+ * using the specified connection timeout duration.
+ *
+ *
This will attempt to connect to one of the root DNS nameservers
+ * (chosen randomly), and failing that, simply to IPv4 address 1.1.1.1
+ * and finally IPv6 address 1::1.
+ *
+ * @param timeoutMillis the amount of time (milliseconds) allowed to
+ * establish an outbound connection
+ * @return the egress interface
+ * @throws EgressResolutionException if an egress interface could not be
+ * determined
+ * @since 4.2
+ */
+ public NetworkInterface fromDefaultMechanisms(final int timeoutMillis)
+ throws EgressResolutionException {
+
+ Finder[] finders = new Finder[] {
+ rootNameServerFinder(timeoutMillis),
+ remoteConnectionFinder(timeoutMillis,
+ new InetSocketAddress("1.1.1.1", 0)),
+ remoteConnectionFinder(timeoutMillis,
+ new InetSocketAddress("1::1", 0))
+ };
+
+ return fromAggregate(finders);
+ }
+
+ /**
+ * Attempt to find the default egress interface on the current system,
+ * by trying each of the specified discovery mechanisms, in order, until
+ * one of them succeeds.
+ *
+ * @return the egress interface
+ * @param finders array of finder callbacks to be executed
+ * @throws EgressResolutionException if an egress interface could not be
+ * determined
+ * @since 4.2
+ */
+ public NetworkInterface fromAggregate(Finder[] finders)
+ throws EgressResolutionException {
+
+ Collection exceptions =
+ new ArrayList();
+
+ for (Finder finder : finders) {
+ try {
+ return finder.egressInterface();
+ } catch (EgressResolutionException e) {
+ exceptions.add(e);
+ }
+ }
+
+ throw new EgressResolutionException(exceptions.toArray(
+ new EgressResolutionException[0]));
+ }
+
+ private Finder rootNameServerFinder(final int timeoutMillis) {
+ return new Finder() {
+ @Override
+ public NetworkInterface egressInterface()
+ throws EgressResolutionException {
+ return fromRootNameserverConnection(timeoutMillis);
+ }
+ };
+ }
+
+ /**
+ * Attempt to find the default egress interface on the current system,
+ * by connecting to one of the root name servers (chosen at random).
+ *
+ * @param timeoutMillis the amount of time (milliseconds) allowed to
+ * establish an outbound connection
+ * @return the egress interface
+ * @throws EgressResolutionException if an egress interface could not be
+ * determined
+ * @since 4.2
+ */
+ public NetworkInterface fromRootNameserverConnection(int timeoutMillis)
+ throws EgressResolutionException {
+ String domainName = randomRootServerName();
+ InetSocketAddress address = new InetSocketAddress(domainName, 53);
+ return fromRemoteConnection(timeoutMillis, address);
+ }
+
+ static String randomRootServerName() {
+ String roots = "abcdefghijklm";
+ int index = new Random().nextInt(roots.length());
+ return roots.charAt(index) + ".root-servers.net";
+ }
+
+ private Finder remoteConnectionFinder(final int timeoutMillis,
+ final InetSocketAddress address) {
+ return new Finder() {
+ @Override
+ public NetworkInterface egressInterface()
+ throws EgressResolutionException {
+ return fromRemoteConnection(timeoutMillis, address);
+ }
+ };
+ }
+
+ /**
+ * Attempt to find the default egress interface on the current system,
+ * by connection to the specified address. This will try two different
+ * methods:
+ *
+ * - using a {@link DatagramSocket}, which seems to work well for Windows
+ * & Linux, and is faster to uses than {@link Socket} as opening one does
+ * not actually require negotiate a handshake connection, but this does
+ * not appear to work on MacOS
+ *
- using a {@link Socket}, which seems to work better for MacOS, but
+ * needs to actually negotiate a connection handshake from a remote host
+ *
+ *
+ * @param timeoutMillis the amount of time (milliseconds) allowed to
+ * establish an outbound connection
+ * @param remoteAddress the address to which a connection will be attempted
+ * in order to determine which interface is used to
+ * connect
+ * @return the egress interface
+ * @throws EgressResolutionException if an egress interface could not be
+ * determined
+ * @since 4.2
+ */
+ public NetworkInterface fromRemoteConnection(
+ int timeoutMillis, InetSocketAddress remoteAddress)
+ throws EgressResolutionException {
+
+ if (remoteAddress.isUnresolved()) {
+ throw new EgressResolutionException(
+ format("remote address [%s] is unresolved", remoteAddress));
+ }
+
+ Finder socketFinder =
+ remoteSocketConnectionFinder(timeoutMillis, remoteAddress);
+
+ Finder datagramSocketFinder =
+ remoteDatagramSocketConnectionFinder(remoteAddress);
+
+ // try DatagramSocket first, by default
+ Finder[] finders = new Finder[] { datagramSocketFinder, socketFinder };
+
+ String osName = System.getProperty("os.name");
+ if (osName != null && osName.startsWith("Mac")) {
+ // instead try Socket first, for macOS
+ finders = new Finder[] { socketFinder, datagramSocketFinder };
+ }
+
+ return fromAggregate(finders);
+ }
+
+ /**
+ * Returns a finder that tries to determine egress interface by connecting
+ * to the specified remote address.
+ *
+ * @param timeoutMillis give up after this length of time
+ * @param address the remote address to connect to
+ * @return finder callback
+ */
+ private Finder remoteSocketConnectionFinder(
+ final int timeoutMillis, final InetSocketAddress address) {
+ return new Finder() {
+ @Override
+ public NetworkInterface egressInterface()
+ throws EgressResolutionException {
+ return fromRemoteSocketConnection(timeoutMillis, address);
+ }
+ };
+ }
+
+ /**
+ * Attempt to find the default egress interface on the current system,
+ * using the specified connection timeout duration and connecting with
+ * a {@link Socket}.
+ *
+ * @param timeoutMillis the amount of time (milliseconds) allowed to
+ * establish an outbound connection
+ * @param remoteAddress the address to which a connection will be attempted
+ * in order to determine which interface is used to
+ * connect
+ * @return the egress interface
+ * @throws EgressResolutionException if an egress interface could not be
+ * determined
+ * @since 4.2
+ */
+ public NetworkInterface fromRemoteSocketConnection(
+ int timeoutMillis, InetSocketAddress remoteAddress)
+ throws EgressResolutionException {
+
+ Socket socket = new Socket();
+
+ try {
+ socket.connect(remoteAddress, timeoutMillis);
+ return fromLocalAddress(socket.getLocalAddress());
+ } catch (IOException e) {
+ throw new EgressResolutionException(
+ format("Socket connection to [%s]", remoteAddress), e);
+ } finally {
+ try {
+ socket.close();
+ } catch (IOException e) {
+ // ignore;
+ }
+ }
+ }
+
+ private Finder remoteDatagramSocketConnectionFinder(
+ final InetSocketAddress address) {
+ return new Finder() {
+ @Override
+ public NetworkInterface egressInterface()
+ throws EgressResolutionException {
+ return fromRemoteDatagramSocketConnection(address);
+ }
+ };
+ }
+
+ /**
+ * Attempt to find the default egress interface on the current system,
+ * using the specified connection timeout duration and connecting with
+ * a {@link DatagramSocket}.
+ *
+ * @param remoteAddress the address to which a connection will be attempted
+ * in order to determine which interface is used to
+ * connect
+ * @return the egress interface
+ * @throws EgressResolutionException if an egress interface could not be
+ * determined
+ * @since 4.2
+ */
+ public NetworkInterface fromRemoteDatagramSocketConnection(
+ InetSocketAddress remoteAddress)
+ throws EgressResolutionException {
+
+ DatagramSocket socket = null;
+
+ try {
+ socket = new DatagramSocket();
+ socket.connect(remoteAddress);
+ return fromLocalAddress(socket.getLocalAddress());
+ } catch (IOException e) {
+ throw new EgressResolutionException(
+ format("DatagramSocket connection to [%s]", remoteAddress),
+ e);
+ } finally {
+ if (socket != null) {
+ socket.close();
+ }
+ }
+ }
+
+ /**
+ * Attempt to find the default egress interface on the current system, by
+ * finding a {@link NetworkInterface} that has the specified network
+ * address. If more than one interface has the specified address, then
+ * one of them will be selected indeterminately.
+ *
+ * @param localAddress the local address which is assigned to an interface
+ * @return the egress interface
+ * @throws EgressResolutionException if an egress interface could not be
+ * determined
+ * @since 4.2
+ */
+ public NetworkInterface fromLocalAddress(InetAddress localAddress)
+ throws EgressResolutionException {
+ try {
+ InetAddress unspecifiedIPv4 = InetAddress.getByName("0.0.0.0");
+ InetAddress unspecifiedIPv6 = InetAddress.getByName("::");
+
+ if (localAddress.equals(unspecifiedIPv4) ||
+ localAddress.equals(unspecifiedIPv6)) {
+ throw new EgressResolutionException(
+ format("local address [%s] is unspecified",
+ localAddress));
+ }
+
+ NetworkInterface ni =
+ NetworkInterface.getByInetAddress(localAddress);
+
+ if (ni == null) {
+ throw new EgressResolutionException(format(
+ "no interface found with local address [%s]",
+ localAddress));
+ }
+
+ return ni;
+ } catch (IOException e) {
+ throw new EgressResolutionException(
+ format("local address [%s]", localAddress), e);
+ }
+ }
+
+ /**
+ * An exception representing a failure to determine a default egress
+ * network interface. Please help improve this functionality by
+ * providing feedback from the {@link #report()} method, if this is not
+ * working for you.
+ *
+ * @since 4.2
+ */
+ public static class EgressResolutionException extends Exception {
+ private final List messages = new ArrayList();
+
+ public EgressResolutionException(String message) {
+ super(message);
+ messages.add(message);
+ }
+
+ public EgressResolutionException(String message, Throwable cause) {
+ super(message, cause);
+ messages.add(message);
+ messages.add(cause.toString());
+ }
+
+ public EgressResolutionException(EgressResolutionException[] priors) {
+ super(Arrays.toString(priors));
+ for (EgressResolutionException e : priors) {
+ messages.add("----------------------------------------------------------------------------");
+ messages.addAll(e.messages);
+ }
+ }
+
+ public void report() {
+ reportLine("");
+ reportLine("====================================");
+ reportLine("| Egress Resolution Failure Report |");
+ reportLine("====================================");
+ reportLine("");
+ reportLine("Please share this report in order to help improve the egress resolution");
+ reportLine("mechanism. Also please indicate if you believe that you have a currently");
+ reportLine("working network connection.");
+ reportLine("");
+ showProperty("java.version");
+ showProperty("java.version.date");
+ showProperty("java.runtime.name");
+ showProperty("java.runtime.version");
+ showProperty("java.vendor");
+ showProperty("java.vendor.url");
+ showProperty("java.vendor.url.bug");
+ showProperty("java.vendor.version");
+ showProperty("java.vm.name");
+ showProperty("java.vm.vendor");
+ showProperty("java.vm.version");
+ showProperty("os.arch");
+ showProperty("os.name");
+ showProperty("os.version");
+
+ for (String message : messages) {
+ reportLine(message);
+ }
+ }
+
+ protected void reportLine(String line) {
+ System.out.println(line);
+ }
+
+ private void showProperty(String key) {
+ reportLine(key + ": " + System.getProperty(key));
+ }
+
+ public Collection getMessages() {
+ return messages;
+ }
+ }
+
+ interface Finder {
+ NetworkInterface egressInterface() throws EgressResolutionException;
+ }
+}
diff --git a/src/main/java/com/fasterxml/uuid/EthernetAddress.java b/src/main/java/com/fasterxml/uuid/EthernetAddress.java
index 1bfb392..8e61c17 100644
--- a/src/main/java/com/fasterxml/uuid/EthernetAddress.java
+++ b/src/main/java/com/fasterxml/uuid/EthernetAddress.java
@@ -15,12 +15,11 @@
package com.fasterxml.uuid;
+import com.fasterxml.uuid.EgressInterfaceFinder.EgressResolutionException;
+
+import java.io.IOException;
import java.io.Serializable;
-import java.net.DatagramSocket;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
+import java.net.*;
import java.security.SecureRandom;
import java.util.Enumeration;
import java.util.Random;
@@ -275,9 +274,9 @@ public static EthernetAddress fromInterface()
while (en.hasMoreElements()) {
NetworkInterface nint = en.nextElement();
if (!nint.isLoopback()) {
- byte[] data = nint.getHardwareAddress();
- if ((data != null) && (data.length == 6)) {
- return new EthernetAddress(data);
+ EthernetAddress addr = fromInterface(nint);
+ if (addr != null) {
+ return addr;
}
}
}
@@ -287,6 +286,74 @@ public static EthernetAddress fromInterface()
return null;
}
+ /**
+ * A factory method to return the ethernet address of a specified network interface.
+ *
+ * @since 4.2
+ */
+ public static EthernetAddress fromInterface(NetworkInterface nint)
+ {
+ if (nint != null) {
+ try {
+ byte[] data = nint.getHardwareAddress();
+ if (data != null && data.length == 6) {
+ return new EthernetAddress(data);
+ }
+ } catch (SocketException e) {
+ // could not get address
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Factory method that locates a network interface that has
+ * a suitable mac address (ethernet cards, and things that
+ * emulate one), and return that address. It will first try to
+ * identify an egress interface, and failing that, it will select
+ * indeterminately from all non-loopback interfaces found.
+ * Method is meant for accessing an address needed to construct
+ * generator for time+location based UUID generation method.
+ *
+ * @return Ethernet address of one of interfaces system has;
+ * not including local or loopback addresses; if one exists,
+ * null if no such interfaces are found.
+ *
+ * @since 4.2
+ */
+ public static EthernetAddress fromPreferredInterface()
+ {
+ EthernetAddress egressIfAddress = fromEgressInterface();
+ if (egressIfAddress == null) {
+ return fromInterface();
+ } else {
+ return egressIfAddress;
+ }
+ }
+
+ /**
+ * A factory method that will try to determine the ethernet address of
+ * the network interface that connects to the default network gateway.
+ * To do this it will try to open a connection to one of the root DNS
+ * servers, or barring that, to address 1.1.1.1, or finally if that also
+ * fails then to IPv6 address "1::1". If a connection can be opened then
+ * the interface through which that connection is routed is determined
+ * to be the default egress interface, and the corresponding address of
+ * that interface will be returned. If all attempts are unsuccessful,
+ * null will be returned.
+ *
+ * @since 4.2
+ */
+ public static EthernetAddress fromEgressInterface()
+ {
+ try {
+ EgressInterfaceFinder finder = new EgressInterfaceFinder();
+ return fromInterface(finder.egressInterface());
+ } catch (EgressResolutionException e) {
+ return null;
+ }
+ }
+
/**
* Factory method that can be used to construct a random multicast
* address; to be used in cases where there is no "real" ethernet
diff --git a/src/main/java/com/fasterxml/uuid/Generators.java b/src/main/java/com/fasterxml/uuid/Generators.java
index 7b39399..7b76998 100644
--- a/src/main/java/com/fasterxml/uuid/Generators.java
+++ b/src/main/java/com/fasterxml/uuid/Generators.java
@@ -42,6 +42,11 @@ public class Generators
*/
protected static UUIDTimer _sharedTimer;
+ /**
+ * The hardware address of the egress network interface.
+ */
+ protected static EthernetAddress _preferredIfAddr = null;
+
// // Random-based generation
/**
@@ -139,6 +144,27 @@ public static TimeBasedEpochGenerator timeBasedEpochGenerator(Random random)
// // Time+location-based generation
+ /**
+ * Factory method for constructing UUID generator that generates UUID using variant 1
+ * (time+location based). This method will use the ethernet address of the interface
+ * that routes to the default gateway, or if that cannot be found, then the address of
+ * an indeterminately selected non-loopback interface. For most simple and common
+ * networking configurations this will be the most appropriate address to use. The
+ * default interface is determined by the calling {@link
+ * EthernetAddress#fromPreferredInterface()} method. Note that this will only
+ * identify the preferred interface once: if you have a complex network setup where
+ * your outbound routes/interfaces may change dynamically. If you want your UUIDs to
+ * accurately reflect a deterministic selection of network interface, you should
+ * instead use a generator implementation that uses an explicitly specified address,
+ * such as {@link #timeBasedGenerator(EthernetAddress)}.
+ *
+ * @since 4.2
+ */
+ public static TimeBasedGenerator defaultTimeBasedGenerator()
+ {
+ return timeBasedGenerator(preferredInterfaceAddress());
+ }
+
/**
* Factory method for constructing UUID generator that generates UUID using
* version 1 (time+location based).
@@ -261,4 +287,12 @@ private static synchronized UUIDTimer sharedTimer()
}
return _sharedTimer;
}
+
+ private static synchronized EthernetAddress preferredInterfaceAddress()
+ {
+ if (_preferredIfAddr == null) {
+ _preferredIfAddr = EthernetAddress.fromPreferredInterface();
+ }
+ return _preferredIfAddr;
+ }
}
diff --git a/src/main/java/test/EgressDiagnostics.java b/src/main/java/test/EgressDiagnostics.java
deleted file mode 100644
index fe325a9..0000000
--- a/src/main/java/test/EgressDiagnostics.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package test;
-
-import java.net.*;
-
-public class EgressDiagnostics {
- public static void main(String[] args) throws SocketException {
- showProperty("java.version");
- showProperty("java.version.date");
- showProperty("java.runtime.name");
- showProperty("java.runtime.version");
- showProperty("java.vendor");
- showProperty("java.vendor.url");
- showProperty("java.vendor.url.bug");
- showProperty("java.vendor.version");
- showProperty("java.vm.name");
- showProperty("java.vm.vendor");
- showProperty("java.vm.version");
- showProperty("os.arch");
- showProperty("os.name");
- showProperty("os.version");
- tryRemote(new InetSocketAddress("a.root-servers.net", 0));
- tryRemote(new InetSocketAddress("a.root-servers.net", 53));
- tryRemote(new InetSocketAddress("1.1.1.1", 0));
- tryRemote(new InetSocketAddress("1::1", 0));
- }
-
- public static void showProperty(String key) {
- System.out.println(key + ": " + System.getProperty(key));
- }
-
- public static void tryRemote(InetSocketAddress remote) {
- DatagramSocket socket = null;
- try {
- System.out.println("\nremote: " + remote);
- System.out.println("reachable: " + remote.getAddress().isReachable(3000));
- socket = new DatagramSocket();
- socket.connect(remote);
- InetAddress local = socket.getLocalAddress();
- System.out.println("local: " + local);
- NetworkInterface ni = NetworkInterface.getByInetAddress(local);
- System.out.println("interface: " + ni);
- System.out.println("hardware: " + (ni == null ? null : macBytesToHex(ni.getHardwareAddress())));
- } catch (Throwable t) {
- System.out.println(t);
- t.printStackTrace();
- } finally {
- if (socket != null) {
- socket.close();
- }
- }
- }
-
- public static String macBytesToHex(byte[] bytes) {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < bytes.length; i++) {
- sb.append(String.format("%02X%s", bytes[i], (i < bytes.length - 1) ? "-" : ""));
- }
- return sb.toString();
- }
-}
diff --git a/src/test/java/com/fasterxml/uuid/EgressInterfaceFinderTest.java b/src/test/java/com/fasterxml/uuid/EgressInterfaceFinderTest.java
new file mode 100644
index 0000000..4b6eb77
--- /dev/null
+++ b/src/test/java/com/fasterxml/uuid/EgressInterfaceFinderTest.java
@@ -0,0 +1,127 @@
+package com.fasterxml.uuid;
+
+import com.fasterxml.uuid.EgressInterfaceFinder.EgressResolutionException;
+import com.fasterxml.uuid.EgressInterfaceFinder.Finder;
+import junit.framework.TestCase;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.net.UnknownHostException;
+
+import static com.fasterxml.uuid.EgressInterfaceFinder.DEFAULT_TIMEOUT_MILLIS;
+
+public class EgressInterfaceFinderTest extends TestCase {
+
+ private final EgressInterfaceFinder finder = new EgressInterfaceFinder();
+
+ public void testUnspecifiedIPv4LocalAddress() throws UnknownHostException {
+ EgressResolutionException ex = null;
+ try {
+ finder.fromLocalAddress(InetAddress.getByName("0.0.0.0"));
+ } catch (EgressResolutionException e) {
+ ex = e;
+ }
+ assertNotNull("EgressResolutionException was not thrown", ex);
+ String message = ex.getMessage();
+ assertTrue(String.format(
+ "message [%s] does not begin with \"local address\"",
+ message),
+ message.startsWith("local address"));
+ assertEquals(1, ex.getMessages().size());
+ }
+
+ public void testUnspecifiedIPv6LocalAddress() throws Exception {
+ EgressResolutionException ex = null;
+ try {
+ finder.fromLocalAddress(InetAddress.getByName("::"));
+ } catch (EgressResolutionException e) {
+ ex = e;
+ }
+ assertNotNull("EgressResolutionException was not thrown", ex);
+ String message = ex.getMessage();
+ assertTrue(String.format(
+ "message [%s] does not begin with \"local address\"",
+ message),
+ message.startsWith("local address"));
+ assertEquals(1, ex.getMessages().size());
+ }
+
+ public void testFromLocalAddress() throws Exception {
+ NetworkInterface anInterface =
+ NetworkInterface.getNetworkInterfaces().nextElement();
+ InetAddress anAddress = anInterface.getInetAddresses().nextElement();
+ assertEquals(anInterface, finder.fromLocalAddress(anAddress));
+ }
+
+ public void testFromIncorrectLocalAddress() throws Exception {
+ EgressResolutionException ex = null;
+ try {
+ String name = EgressInterfaceFinder.randomRootServerName();
+ finder.fromLocalAddress(InetAddress.getByName(name));
+ } catch (EgressResolutionException e) {
+ ex = e;
+ }
+ assertNotNull("EgressResolutionException was not thrown", ex);
+ String message = ex.getMessage();
+ assertTrue(String.format(
+ "message [%s] does not begin with \"no interface found\"",
+ message),
+ message.startsWith("no interface found"));
+ assertEquals(1, ex.getMessages().size());
+ }
+
+ public void testFromRemoteDatagramSocketConnection() throws Exception {
+ if (!System.getProperty("os.name").startsWith("Mac")) {
+ String name = EgressInterfaceFinder.randomRootServerName();
+ InetSocketAddress address = new InetSocketAddress(name, 53);
+ finder.fromRemoteDatagramSocketConnection(address);
+ }
+ }
+
+ public void testFromRemoteSocketConnection() throws Exception {
+ String name = EgressInterfaceFinder.randomRootServerName();
+ InetSocketAddress address = new InetSocketAddress(name, 53);
+ finder.fromRemoteSocketConnection(DEFAULT_TIMEOUT_MILLIS, address);
+ }
+
+ public void testFromRemoteConnection() throws Exception {
+ String name = EgressInterfaceFinder.randomRootServerName();
+ InetSocketAddress address = new InetSocketAddress(name, 53);
+ finder.fromRemoteConnection(DEFAULT_TIMEOUT_MILLIS, address);
+ }
+
+ public void testFromRootNameServerConnection() throws Exception {
+ finder.fromRootNameserverConnection(DEFAULT_TIMEOUT_MILLIS);
+ }
+
+ public void testAggregateExceptions() {
+ EgressResolutionException ex = null;
+ final int[] counter = {0};
+ Finder aFinder = new Finder() {
+ @Override
+ public NetworkInterface egressInterface()
+ throws EgressResolutionException {
+ throw new EgressResolutionException(
+ String.format("exception %d", ++counter[0]),
+ new Exception("test exception"));
+ }
+ };
+ try {
+ finder.fromAggregate(new Finder[] { aFinder, aFinder, aFinder});
+ } catch (EgressResolutionException e) {
+ ex = e;
+ }
+ assertNotNull("EgressResolutionException was not thrown", ex);
+ assertEquals(9, ex.getMessages().size());
+ }
+
+ public void testDefaultMechanisms() throws Exception {
+ try {
+ finder.egressInterface();
+ } catch (EgressResolutionException e) {
+ e.report();
+ throw e;
+ }
+ }
+}
diff --git a/src/test/java/com/fasterxml/uuid/EthernetAddressTest.java b/src/test/java/com/fasterxml/uuid/EthernetAddressTest.java
index 498d28c..1695b32 100644
--- a/src/test/java/com/fasterxml/uuid/EthernetAddressTest.java
+++ b/src/test/java/com/fasterxml/uuid/EthernetAddressTest.java
@@ -17,16 +17,16 @@
package com.fasterxml.uuid;
-import java.util.Arrays;
-import java.util.Random;
-
import com.fasterxml.uuid.impl.TimeBasedGenerator;
-
+import java.net.InetSocketAddress;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import junit.textui.TestRunner;
+import java.util.Arrays;
+import java.util.Random;
+
/**
* JUnit Test class for the com.fasterxml.uuid.EthernetAddress class.
*
@@ -1309,6 +1309,21 @@ public void testFromInterface() throws Exception
assertNotNull(addr.toString());
}
+ public void testFromEgressInterface() {
+ EthernetAddress ifAddr = EthernetAddress.fromEgressInterface();
+ assertNotNull(ifAddr);
+ assertNotNull(ifAddr.toString());
+ }
+
+ public void testDefaultTimeBasedGenerator()
+ {
+ TimeBasedGenerator generator = Generators.defaultTimeBasedGenerator();
+ assertNotNull(generator);
+ EthernetAddress ifAddr = generator.getEthernetAddress();
+ assertNotNull(ifAddr);
+ assertNotNull(ifAddr.toString());
+ }
+
public void testBogus() throws Exception
{
// First, two using pseudo-random; verify they are different