package org.argeo.api.uuid;
import static java.lang.System.Logger.Level.DEBUG;
+import static java.lang.System.Logger.Level.INFO;
import static java.lang.System.Logger.Level.WARNING;
import java.lang.System.Logger;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InterfaceAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
import java.security.DrbgParameters;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
+import java.util.BitSet;
+import java.util.Enumeration;
import java.util.Objects;
+import java.util.UUID;
/**
* A configurable implementation of an {@link AsyncUuidFactory}, which can be
public class ConcurrentUuidFactory extends AbstractAsyncUuidFactory {
private final static Logger logger = System.getLogger(ConcurrentUuidFactory.class.getName());
-// private byte[] defaultNodeId;
-
private Long nodeIdBase;
public ConcurrentUuidFactory(byte[] nodeId, int offset) {
byte[] defaultNodeId = toNodeIdBytes(nodeId, offset);
nodeIdBase = NodeIdSupplier.toNodeIdBase(defaultNodeId);
setNodeIdSupplier(() -> nodeIdBase);
+ assert newTimeUUID().node() == BitSet.valueOf(defaultNodeId).toLongArray()[0];
}
- protected ConcurrentUuidFactory() {
+ public ConcurrentUuidFactory() {
+ byte[] defaultNodeId = getIpBytes();
+ nodeIdBase = NodeIdSupplier.toNodeIdBase(defaultNodeId);
+ setNodeIdSupplier(() -> nodeIdBase);
+ assert newTimeUUID().node() == BitSet.valueOf(defaultNodeId).toLongArray()[0];
+ }
+ /*
+ * DEFAULT
+ */
+ /**
+ * The default {@link UUID} to provide. This implementations returns
+ * {@link #timeUUID()} because it is fast and uses few resources.
+ */
+ @Override
+ public UUID get() {
+ return timeUUID();
}
@Override
return secureRandom;
}
- /*
- * TIME-BASED (version 1)
- */
-//
-// @Override
-// public UUID newTimeUUID() {
-// return newTimeUUID(timeUuidState.useTimestamp(), timeUuidState.getClockSequence(), defaultNodeId, 0);
-// }
-}
+ /** Returns an SHA1 digest of one of the IP addresses. */
+ protected byte[] getIpBytes() {
+ Enumeration<NetworkInterface> netInterfaces = null;
+ try {
+ netInterfaces = NetworkInterface.getNetworkInterfaces();
+ } catch (SocketException e) {
+ throw new IllegalStateException(e);
+ }
+ if (netInterfaces == null)
+ throw new IllegalStateException("No interfaces");
+
+ InetAddress selectedIpv6 = null;
+ InetAddress selectedIpv4 = null;
+ netInterfaces: while (netInterfaces.hasMoreElements()) {
+ NetworkInterface netInterface = netInterfaces.nextElement();
+ byte[] hardwareAddress = null;
+ try {
+ hardwareAddress = netInterface.getHardwareAddress();
+ if (hardwareAddress != null) {
+ // first IPv6
+ addr: for (InterfaceAddress addr : netInterface.getInterfaceAddresses()) {
+ InetAddress ip = addr.getAddress();
+ if (ip instanceof Inet6Address) {
+ Inet6Address ipv6 = (Inet6Address) ip;
+ if (ipv6.isAnyLocalAddress() || ipv6.isLinkLocalAddress() || ipv6.isLoopbackAddress())
+ continue addr;
+ selectedIpv6 = ipv6;
+ break netInterfaces;
+ }
+
+ }
+ // then IPv4
+ addr: for (InterfaceAddress addr : netInterface.getInterfaceAddresses()) {
+ InetAddress ip = addr.getAddress();
+ if (ip instanceof Inet4Address) {
+ Inet4Address ipv4 = (Inet4Address) ip;
+ if (ipv4.isAnyLocalAddress() || ipv4.isLinkLocalAddress() || ipv4.isLoopbackAddress())
+ continue addr;
+ selectedIpv4 = ipv4;
+ }
+
+ }
+ }
+ } catch (SocketException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ InetAddress selectedIp = selectedIpv6 != null ? selectedIpv6 : selectedIpv4;
+ if (selectedIp == null)
+ throw new IllegalStateException("No IP address found");
+ byte[] digest = sha1(selectedIp.getAddress());
+ logger.log(INFO, "Use IP " + selectedIp + " hashed as " + toHexString(digest) + " as node id");
+ byte[] nodeId = toNodeIdBytes(digest, 0);
+ // marks that this is not based on MAC address
+ forceToNoMacAddress(nodeId, 0);
+ return nodeId;
+ }
+}
\ No newline at end of file