Make MAC address UUID more robust
[lgpl/argeo-commons.git] / org.argeo.api.uuid / src / org / argeo / api / uuid / MacAddressUuidFactory.java
index d1ce6ded851ded57825c20be3158498f32281f3e..6cbe98178123bb59b7a4342a87efdd7a260869f9 100644 (file)
@@ -1,15 +1,71 @@
 package org.argeo.api.uuid;
 
-public class MacAddressUuidFactory extends ConcurrentUuidFactory {
-       public final static UuidFactory DEFAULT = new MacAddressUuidFactory();
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Enumeration;
+import java.util.UUID;
 
+/**
+ * An {@link UUID} factory whose node id (for time based UUIDs) is the hardware
+ * MAC address as specified in RFC4122.
+ * 
+ * @see "https://datatracker.ietf.org/doc/html/rfc4122.html#section-4.1.6"
+ */
+public class MacAddressUuidFactory extends ConcurrentUuidFactory {
        public MacAddressUuidFactory() {
-               setNodeIdSupplier(() -> {
-                       byte[] hardwareAddress = getHardwareAddress();
-                       byte[] macAddressNodeId = toNodeIdBytes(hardwareAddress, 0);
-                       long nodeIdBase = NodeIdSupplier.toNodeIdBase(macAddressNodeId);
-                       return nodeIdBase;
-               });
+               this(0, localHardwareAddressAsNodeId());
+       }
+
+       public MacAddressUuidFactory(long initialClockRange) {
+               this(initialClockRange, localHardwareAddressAsNodeId());
+       }
+
+       public MacAddressUuidFactory(byte[] hardwareAddress) {
+               this(0, hardwareAddress);
+       }
+
+       public MacAddressUuidFactory(long initialClockRange, byte[] hardwareAddress) {
+               super(initialClockRange, hardwareAddress);
+       }
+
+       private static byte[] localHardwareAddressAsNodeId() {
+               InetAddress localHost;
+               try {
+                       localHost = InetAddress.getLocalHost();
+                       NetworkInterface nic = NetworkInterface.getByInetAddress(localHost);
+                       if (nic != null && nic.getHardwareAddress() != null)
+                               return hardwareAddressToNodeId(nic);
+                       Enumeration<NetworkInterface> netInterfaces = null;
+                       netInterfaces = NetworkInterface.getNetworkInterfaces();
+                       if (netInterfaces == null || !netInterfaces.hasMoreElements())
+                               throw new IllegalStateException("No interfaces");
+                       while (netInterfaces.hasMoreElements()) {
+                               // TODO find out public/physical interfaces
+                               nic = netInterfaces.nextElement();
+                               if (nic.getHardwareAddress() != null)
+                                       return hardwareAddressToNodeId(nic);
+                       }
+                       throw new IllegalStateException("No interfaces with a MAC address");
+               } catch (UnknownHostException | SocketException e) {
+                       throw new IllegalStateException(e);
+               }
+
+       }
+
+       public static byte[] hardwareAddressToNodeId(NetworkInterface nic) throws IllegalStateException {
+               try {
+                       byte[] hardwareAddress = nic.getHardwareAddress();
+                       final int length = 6;
+                       byte[] arr = new byte[length];
+                       for (int i = 0; i < length; i++) {
+                               arr[i] = hardwareAddress[length - 1 - i];
+                       }
+                       return arr;
+               } catch (SocketException e) {
+                       throw new IllegalStateException("Cannot retrieve hardware address from NIC", e);
+               }
        }
 
 }