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: + *

+ * + * @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