1 package org
.argeo
.api
.uuid
;
3 import static java
.lang
.System
.Logger
.Level
.DEBUG
;
4 import static java
.lang
.System
.Logger
.Level
.INFO
;
5 import static java
.lang
.System
.Logger
.Level
.WARNING
;
7 import java
.lang
.System
.Logger
;
8 import java
.net
.Inet4Address
;
9 import java
.net
.Inet6Address
;
10 import java
.net
.InetAddress
;
11 import java
.net
.InterfaceAddress
;
12 import java
.net
.NetworkInterface
;
13 import java
.net
.SocketException
;
14 import java
.security
.DrbgParameters
;
15 import java
.security
.NoSuchAlgorithmException
;
16 import java
.security
.SecureRandom
;
17 import java
.util
.BitSet
;
18 import java
.util
.Enumeration
;
19 import java
.util
.Objects
;
20 import java
.util
.UUID
;
23 * A configurable implementation of an {@link AsyncUuidFactory}, which can be
24 * used as a base class for more optimised implementations.
26 * @see https://datatracker.ietf.org/doc/html/rfc4122
28 public class ConcurrentUuidFactory
extends AbstractAsyncUuidFactory
{
29 private final static Logger logger
= System
.getLogger(ConcurrentUuidFactory
.class.getName());
31 private Long nodeIdBase
;
33 public ConcurrentUuidFactory(byte[] nodeId
, int offset
) {
34 Objects
.requireNonNull(nodeId
);
35 if (offset
+ 6 > nodeId
.length
)
36 throw new IllegalArgumentException("Offset too big: " + offset
);
37 byte[] defaultNodeId
= toNodeIdBytes(nodeId
, offset
);
38 nodeIdBase
= NodeIdSupplier
.toNodeIdBase(defaultNodeId
);
39 setNodeIdSupplier(() -> nodeIdBase
);
40 assert newTimeUUID().node() == BitSet
.valueOf(defaultNodeId
).toLongArray()[0];
43 public ConcurrentUuidFactory() {
44 byte[] defaultNodeId
= getIpBytes();
45 nodeIdBase
= NodeIdSupplier
.toNodeIdBase(defaultNodeId
);
46 setNodeIdSupplier(() -> nodeIdBase
);
47 assert newTimeUUID().node() == BitSet
.valueOf(defaultNodeId
).toLongArray()[0];
54 * The default {@link UUID} to provide. This implementations returns
55 * {@link #timeUUID()} because it is fast and uses few resources.
63 protected SecureRandom
newSecureRandom() {
64 SecureRandom secureRandom
;
66 secureRandom
= SecureRandom
.getInstance("DRBG",
67 DrbgParameters
.instantiation(256, DrbgParameters
.Capability
.PR_AND_RESEED
, "UUID".getBytes()));
68 } catch (NoSuchAlgorithmException e
) {
70 logger
.log(DEBUG
, "DRBG secure random not found, using strong");
71 secureRandom
= SecureRandom
.getInstanceStrong();
72 } catch (NoSuchAlgorithmException e1
) {
73 logger
.log(WARNING
, "No strong secure random was found, using default");
74 secureRandom
= new SecureRandom();
80 /** Returns an SHA1 digest of one of the IP addresses. */
81 protected byte[] getIpBytes() {
82 Enumeration
<NetworkInterface
> netInterfaces
= null;
84 netInterfaces
= NetworkInterface
.getNetworkInterfaces();
85 } catch (SocketException e
) {
86 throw new IllegalStateException(e
);
88 if (netInterfaces
== null)
89 throw new IllegalStateException("No interfaces");
91 InetAddress selectedIpv6
= null;
92 InetAddress selectedIpv4
= null;
93 netInterfaces
: while (netInterfaces
.hasMoreElements()) {
94 NetworkInterface netInterface
= netInterfaces
.nextElement();
95 byte[] hardwareAddress
= null;
97 hardwareAddress
= netInterface
.getHardwareAddress();
98 if (hardwareAddress
!= null) {
100 addr
: for (InterfaceAddress addr
: netInterface
.getInterfaceAddresses()) {
101 InetAddress ip
= addr
.getAddress();
102 if (ip
instanceof Inet6Address
) {
103 Inet6Address ipv6
= (Inet6Address
) ip
;
104 if (ipv6
.isAnyLocalAddress() || ipv6
.isLinkLocalAddress() || ipv6
.isLoopbackAddress())
112 addr
: for (InterfaceAddress addr
: netInterface
.getInterfaceAddresses()) {
113 InetAddress ip
= addr
.getAddress();
114 if (ip
instanceof Inet4Address
) {
115 Inet4Address ipv4
= (Inet4Address
) ip
;
116 if (ipv4
.isAnyLocalAddress() || ipv4
.isLinkLocalAddress() || ipv4
.isLoopbackAddress())
123 } catch (SocketException e
) {
124 throw new IllegalStateException(e
);
127 InetAddress selectedIp
= selectedIpv6
!= null ? selectedIpv6
: selectedIpv4
;
128 if (selectedIp
== null)
129 throw new IllegalStateException("No IP address found");
130 byte[] digest
= sha1(selectedIp
.getAddress());
131 logger
.log(INFO
, "Use IP " + selectedIp
+ " hashed as " + toHexString(digest
) + " as node id");
132 byte[] nodeId
= toNodeIdBytes(digest
, 0);
133 // marks that this is not based on MAC address
134 forceToNoMacAddress(nodeId
, 0);