]> git.argeo.org Git - lgpl/argeo-commons.git/blob - AbstractAsyncUuidFactory.java
1948eafbd8e82de76afe00e21d4d7620b9259d83
[lgpl/argeo-commons.git] / AbstractAsyncUuidFactory.java
1 package org.argeo.api.uuid;
2
3 import java.security.DrbgParameters;
4 import java.security.DrbgParameters.Capability;
5 import java.security.SecureRandom;
6 import java.security.SecureRandomParameters;
7 import java.util.UUID;
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;
14
15 /**
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
22 * generation method.
23 */
24 public abstract class AbstractAsyncUuidFactory extends AbstractUuidFactory implements AsyncUuidFactory {
25 private SecureRandom secureRandom;
26 protected ConcurrentTimeUuidState timeUuidState;
27
28 private NodeIdSupplier nodeIdSupplier;
29
30 public AbstractAsyncUuidFactory() {
31 secureRandom = newSecureRandom();
32 timeUuidState = new ConcurrentTimeUuidState(secureRandom, null);
33 }
34 /*
35 * ABSTRACT METHODS
36 */
37
38 protected abstract SecureRandom newSecureRandom();
39
40 /*
41 * STATE
42 */
43 public void reset() {
44 if (nodeIdSupplier == null)
45 throw new IllegalStateException("No node id supplier available");
46 long nodeIdBase = nodeIdSupplier.get();
47 timeUuidState.reset(nodeIdBase);
48 }
49
50 public void setNodeIdSupplier(NodeIdSupplier nodeIdSupplier) {
51 this.nodeIdSupplier = nodeIdSupplier;
52 reset();
53 }
54
55 /*
56 * SYNC OPERATIONS
57 */
58 protected UUID newRandomUUIDStrong() {
59 SecureRandomParameters parameters = secureRandom.getParameters();
60 if (parameters != null) {
61 if (parameters instanceof DrbgParameters.Instantiation) {
62 Capability capability = ((DrbgParameters.Instantiation) parameters).getCapability();
63 if (capability.equals(DrbgParameters.Capability.PR_AND_RESEED)
64 || capability.equals(DrbgParameters.Capability.RESEED_ONLY)) {
65 secureRandom.reseed();
66 }
67 }
68 }
69 return newRandomUUID(secureRandom);
70 }
71
72 public UUID randomUUIDWeak() {
73 return newRandomUUID(ThreadLocalRandom.current());
74 }
75
76 protected UUID newTimeUUID() {
77 if (nodeIdSupplier == null)
78 throw new IllegalStateException("No node id supplier available");
79 UUID uuid = new UUID(timeUuidState.getMostSignificantBits(), timeUuidState.getLeastSignificantBits());
80
81 assert uuid.version() == 1;
82 assert uuid.variant() == 2;
83 assert uuid.timestamp() == timeUuidState.getLastTimestamp();
84 assert uuid.clockSequence() == timeUuidState.getClockSequence();
85
86 return uuid;
87 }
88
89 /*
90 * ASYNC OPERATIONS (heavy)
91 */
92 protected CompletionStage<UUID> request(ForkJoinTask<UUID> newUuid) {
93 return CompletableFuture.supplyAsync(newUuid::invoke).minimalCompletionStage();
94 }
95
96 @Override
97 public CompletionStage<UUID> requestNameUUIDv5(UUID namespace, byte[] data) {
98 return request(futureNameUUIDv5(namespace, data));
99 }
100
101 @Override
102 public CompletionStage<UUID> requestNameUUIDv3(UUID namespace, byte[] data) {
103 return request(futureNameUUIDv3(namespace, data));
104 }
105
106 @Override
107 public CompletionStage<UUID> requestRandomUUIDStrong() {
108 return request(futureRandomUUIDStrong());
109 }
110
111 @Override
112 public CompletionStage<UUID> requestTimeUUID() {
113 return request(futureTimeUUID());
114 }
115
116 /*
117 * ASYNC OPERATIONS (light)
118 */
119 protected ForkJoinTask<UUID> submit(Callable<UUID> newUuid) {
120 return ForkJoinTask.adapt(newUuid);
121 }
122
123 @Override
124 public ForkJoinTask<UUID> futureNameUUIDv5(UUID namespace, byte[] data) {
125 return submit(() -> newNameUUIDv5(namespace, data));
126 }
127
128 @Override
129 public ForkJoinTask<UUID> futureNameUUIDv3(UUID namespace, byte[] data) {
130 return submit(() -> newNameUUIDv3(namespace, data));
131 }
132
133 @Override
134 public ForkJoinTask<UUID> futureRandomUUIDStrong() {
135 return submit(this::newRandomUUIDStrong);
136 }
137
138 @Override
139 public ForkJoinTask<UUID> futureTimeUUID() {
140 return submit(this::newTimeUUID);
141 }
142 }