1 package org
.argeo
.api
.uuid
;
3 import java
.security
.DrbgParameters
;
4 import java
.security
.DrbgParameters
.Capability
;
5 import java
.security
.SecureRandom
;
6 import java
.security
.SecureRandomParameters
;
8 import java
.util
.concurrent
.Callable
;
9 import java
.util
.concurrent
.CompletableFuture
;
10 import java
.util
.concurrent
.CompletionStage
;
11 import java
.util
.concurrent
.ForkJoinPool
;
12 import java
.util
.concurrent
.ForkJoinTask
;
13 import java
.util
.concurrent
.ThreadLocalRandom
;
16 * Execute {@link UUID} creations in {@link ForkJoinPool#commonPool()}. The goal
17 * is to provide good performance while staying within the parallelism defined
18 * for the system, so as to overwhelm it if many UUIDs are requested.
19 * Additionally, with regard to time based UUIDs, since we use
20 * {@link ConcurrentTimeUuidState}, which maintains one "clock sequence" per
21 * thread, we want to limit the number of threads accessing the actual
24 public abstract class AbstractAsyncUuidFactory
extends AbstractUuidFactory
implements AsyncUuidFactory
{
25 private SecureRandom secureRandom
;
26 protected ConcurrentTimeUuidState timeUuidState
;
28 private NodeIdSupplier nodeIdSupplier
;
29 private long currentClockSequenceRange
= 0;
31 public AbstractAsyncUuidFactory() {
32 secureRandom
= createSecureRandom();
33 timeUuidState
= new ConcurrentTimeUuidState(secureRandom
, null);
39 protected abstract SecureRandom
createSecureRandom();
45 if (nodeIdSupplier
== null)
46 throw new IllegalStateException("No node id supplier available");
47 long nodeIdBase
= nodeIdSupplier
.get();
48 timeUuidState
.reset(nodeIdBase
, currentClockSequenceRange
);
51 public void setNodeIdSupplier(NodeIdSupplier nodeIdSupplier
) {
52 this.nodeIdSupplier
= nodeIdSupplier
;
56 public void setNodeIdSupplier(NodeIdSupplier nodeIdSupplier
, long range
) {
57 this.currentClockSequenceRange
= range
>= 0 ? range
& 0x3F00 : range
;
58 setNodeIdSupplier(nodeIdSupplier
);
61 protected NodeIdSupplier
getNodeIdSupplier() {
62 return nodeIdSupplier
;
66 * If positive, only clock_hi is taken from the argument (range amp; 0x3F00), if
67 * negative, the full range of possible values is used.
69 public void setCurrentClockSequenceRange(long range
) {
70 this.currentClockSequenceRange
= range
>= 0 ? range
& 0x3F00 : range
;
77 protected UUID
createRandomUUIDStrong() {
78 SecureRandomParameters parameters
= secureRandom
.getParameters();
79 if (parameters
!= null) {
80 if (parameters
instanceof DrbgParameters
.Instantiation
) {
81 Capability capability
= ((DrbgParameters
.Instantiation
) parameters
).getCapability();
82 if (capability
.equals(DrbgParameters
.Capability
.PR_AND_RESEED
)
83 || capability
.equals(DrbgParameters
.Capability
.RESEED_ONLY
)) {
84 secureRandom
.reseed();
88 return createRandomUUID(secureRandom
);
91 public UUID
randomUUIDWeak() {
92 return createRandomUUID(ThreadLocalRandom
.current());
95 protected UUID
createTimeUUID() {
96 if (nodeIdSupplier
== null)
97 throw new IllegalStateException("No node id supplier available");
98 UUID uuid
= new UUID(timeUuidState
.getMostSignificantBits(), timeUuidState
.getLeastSignificantBits());
100 assert uuid
.version() == 1;
101 assert uuid
.variant() == 2;
102 assert uuid
.timestamp() == timeUuidState
.getLastTimestamp();
103 assert uuid
.clockSequence() == timeUuidState
.getClockSequence();
109 * ASYNC OPERATIONS (heavy)
111 protected CompletionStage
<UUID
> request(ForkJoinTask
<UUID
> newUuid
) {
112 return CompletableFuture
.supplyAsync(newUuid
::invoke
).minimalCompletionStage();
116 public CompletionStage
<UUID
> requestNameUUIDv5(UUID namespace
, byte[] data
) {
117 return request(futureNameUUIDv5(namespace
, data
));
121 public CompletionStage
<UUID
> requestNameUUIDv3(UUID namespace
, byte[] data
) {
122 return request(futureNameUUIDv3(namespace
, data
));
126 public CompletionStage
<UUID
> requestRandomUUIDStrong() {
127 return request(futureRandomUUIDStrong());
131 public CompletionStage
<UUID
> requestTimeUUID() {
132 return request(futureTimeUUID());
136 * ASYNC OPERATIONS (light)
138 protected ForkJoinTask
<UUID
> submit(Callable
<UUID
> newUuid
) {
139 return ForkJoinTask
.adapt(newUuid
);
143 public ForkJoinTask
<UUID
> futureNameUUIDv5(UUID namespace
, byte[] data
) {
144 return submit(() -> createNameUUIDv5(namespace
, data
));
148 public ForkJoinTask
<UUID
> futureNameUUIDv3(UUID namespace
, byte[] data
) {
149 return submit(() -> createNameUUIDv3(namespace
, data
));
153 public ForkJoinTask
<UUID
> futureRandomUUIDStrong() {
154 return submit(this::createRandomUUIDStrong
);
158 public ForkJoinTask
<UUID
> futureTimeUUID() {
159 return submit(this::createTimeUUID
);