package org.argeo.api.uuid; import java.security.SecureRandom; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; import java.util.concurrent.ThreadLocalRandom; /** * Execute {@link UUID} creations in {@link ForkJoinPool#commonPool()}. The goal * is to provide good performance while staying within the parallelism defined * for the system, so as to overwhelm it if many UUIDs are requested. * Additionally, with regard to time based UUIDs, since we use * {@link ConcurrentTimeUuidState}, which maintains one "clock sequence" per * thread, we want to limit the number of threads accessing the actual * generation method. */ public abstract class AbstractAsyncUuidFactory extends AbstractUuidFactory implements AsyncUuidFactory { private SecureRandom secureRandom; protected ConcurrentTimeUuidState timeUuidState; private NodeIdSupplier nodeIdSupplier; public AbstractAsyncUuidFactory() { secureRandom = newSecureRandom(); timeUuidState = new ConcurrentTimeUuidState(secureRandom, null); } /* * ABSTRACT METHODS */ protected abstract SecureRandom newSecureRandom(); /* * STATE */ public void reset() { if (nodeIdSupplier == null) throw new IllegalStateException("No node id supplier available"); long nodeIdBase = nodeIdSupplier.get(); timeUuidState.reset(nodeIdBase); } public void setNodeIdSupplier(NodeIdSupplier nodeIdSupplier) { this.nodeIdSupplier = nodeIdSupplier; reset(); } /* * SYNC OPERATIONS */ protected UUID newRandomUUIDStrong() { return newRandomUUID(secureRandom); } public UUID randomUUIDWeak() { return newRandomUUID(ThreadLocalRandom.current()); } protected UUID newTimeUUID() { if (nodeIdSupplier == null) throw new IllegalStateException("No node id supplier available"); UUID uuid = new UUID(timeUuidState.getMostSignificantBits(), timeUuidState.getLeastSignificantBits()); assert uuid.version() == 1; assert uuid.variant() == 2; assert uuid.timestamp() == timeUuidState.getLastTimestamp(); assert uuid.clockSequence() == timeUuidState.getClockSequence(); return uuid; } /* * ASYNC OPERATIONS (heavy) */ protected CompletionStage request(ForkJoinTask newUuid) { return CompletableFuture.supplyAsync(newUuid::invoke).minimalCompletionStage(); } @Override public CompletionStage requestNameUUIDv5(UUID namespace, byte[] data) { return request(futureNameUUIDv5(namespace, data)); } @Override public CompletionStage requestNameUUIDv3(UUID namespace, byte[] data) { return request(futureNameUUIDv3(namespace, data)); } @Override public CompletionStage requestRandomUUIDStrong() { return request(futureRandomUUIDStrong()); } @Override public CompletionStage requestTimeUUID() { return request(futureTimeUUID()); } /* * ASYNC OPERATIONS (light) */ protected ForkJoinTask submit(Callable newUuid) { return ForkJoinTask.adapt(newUuid); } @Override public ForkJoinTask futureNameUUIDv5(UUID namespace, byte[] data) { return submit(() -> newNameUUIDv5(namespace, data)); } @Override public ForkJoinTask futureNameUUIDv3(UUID namespace, byte[] data) { return submit(() -> newNameUUIDv3(namespace, data)); } @Override public ForkJoinTask futureRandomUUIDStrong() { return submit(this::newRandomUUIDStrong); } @Override public ForkJoinTask futureTimeUUID() { return submit(this::newTimeUUID); } }