Improve clock sequence range configuration
[lgpl/argeo-commons.git] / org.argeo.api.uuid / src / org / argeo / api / uuid / AbstractAsyncUuidFactory.java
index f57462388959f2fb9e64c8dd55c0da38cdaf8bd0..52becc85ac0946a018e692b1065e3b20c999c83a 100644 (file)
@@ -1,6 +1,9 @@
 package org.argeo.api.uuid;
 
+import java.security.DrbgParameters;
+import java.security.DrbgParameters.Capability;
 import java.security.SecureRandom;
+import java.security.SecureRandomParameters;
 import java.util.UUID;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CompletableFuture;
@@ -23,16 +26,17 @@ public abstract class AbstractAsyncUuidFactory extends AbstractUuidFactory imple
        protected ConcurrentTimeUuidState timeUuidState;
 
        private NodeIdSupplier nodeIdSupplier;
+       private long currentClockSequenceRange = 0;
 
        public AbstractAsyncUuidFactory() {
-               secureRandom = newSecureRandom();
+               secureRandom = createSecureRandom();
                timeUuidState = new ConcurrentTimeUuidState(secureRandom, null);
        }
        /*
         * ABSTRACT METHODS
         */
 
-       protected abstract SecureRandom newSecureRandom();
+       protected abstract SecureRandom createSecureRandom();
 
        /*
         * STATE
@@ -41,7 +45,7 @@ public abstract class AbstractAsyncUuidFactory extends AbstractUuidFactory imple
                if (nodeIdSupplier == null)
                        throw new IllegalStateException("No node id supplier available");
                long nodeIdBase = nodeIdSupplier.get();
-               timeUuidState.reset(nodeIdBase);
+               timeUuidState.reset(nodeIdBase, currentClockSequenceRange);
        }
 
        public void setNodeIdSupplier(NodeIdSupplier nodeIdSupplier) {
@@ -49,29 +53,54 @@ public abstract class AbstractAsyncUuidFactory extends AbstractUuidFactory imple
                reset();
        }
 
+       public void setNodeIdSupplier(NodeIdSupplier nodeIdSupplier, long range) {
+               this.currentClockSequenceRange = range >= 0 ? range & 0x3F00 : range;
+               setNodeIdSupplier(nodeIdSupplier);
+       }
+
+       protected NodeIdSupplier getNodeIdSupplier() {
+               return nodeIdSupplier;
+       }
+
+       /**
+        * If positive, only clock_hi is taken from the argument (range & 0x3F00), if
+        * negative, the full range of possible values is used.
+        */
+       public void setCurrentClockSequenceRange(long range) {
+               this.currentClockSequenceRange = range >= 0 ? range & 0x3F00 : range;
+               reset();
+       }
+
        /*
         * SYNC OPERATIONS
         */
-       protected UUID newRandomUUIDStrong() {
-               return newRandomUUID(secureRandom);
+       protected UUID createRandomUUIDStrong() {
+               SecureRandomParameters parameters = secureRandom.getParameters();
+               if (parameters != null) {
+                       if (parameters instanceof DrbgParameters.Instantiation) {
+                               Capability capability = ((DrbgParameters.Instantiation) parameters).getCapability();
+                               if (capability.equals(DrbgParameters.Capability.PR_AND_RESEED)
+                                               || capability.equals(DrbgParameters.Capability.RESEED_ONLY)) {
+                                       secureRandom.reseed();
+                               }
+                       }
+               }
+               return createRandomUUID(secureRandom);
        }
 
        public UUID randomUUIDWeak() {
-               return newRandomUUID(ThreadLocalRandom.current());
+               return createRandomUUID(ThreadLocalRandom.current());
        }
 
-       protected UUID newTimeUUID() {
+       protected UUID createTimeUUID() {
                if (nodeIdSupplier == null)
                        throw new IllegalStateException("No node id supplier available");
                UUID uuid = new UUID(timeUuidState.getMostSignificantBits(), timeUuidState.getLeastSignificantBits());
-               long clockSequence = timeUuidState.getClockSequence(); 
-               long timestamp = timeUuidState.getLastTimestamp(); 
-               // assert uuid.node() == longFromBytes(node);
-               assert uuid.timestamp() == timestamp;
-               assert uuid.clockSequence() == clockSequence
-                               : "uuid.clockSequence()=" + uuid.clockSequence() + " clockSequence=" + clockSequence;
+
                assert uuid.version() == 1;
                assert uuid.variant() == 2;
+               assert uuid.timestamp() == timeUuidState.getLastTimestamp();
+               assert uuid.clockSequence() == timeUuidState.getClockSequence();
 
                return uuid;
        }
@@ -112,21 +141,21 @@ public abstract class AbstractAsyncUuidFactory extends AbstractUuidFactory imple
 
        @Override
        public ForkJoinTask<UUID> futureNameUUIDv5(UUID namespace, byte[] data) {
-               return submit(() -> newNameUUIDv5(namespace, data));
+               return submit(() -> createNameUUIDv5(namespace, data));
        }
 
        @Override
        public ForkJoinTask<UUID> futureNameUUIDv3(UUID namespace, byte[] data) {
-               return submit(() -> newNameUUIDv3(namespace, data));
+               return submit(() -> createNameUUIDv3(namespace, data));
        }
 
        @Override
        public ForkJoinTask<UUID> futureRandomUUIDStrong() {
-               return submit(this::newRandomUUIDStrong);
+               return submit(this::createRandomUUIDStrong);
        }
 
        @Override
        public ForkJoinTask<UUID> futureTimeUUID() {
-               return submit(this::newTimeUUID);
+               return submit(this::createTimeUUID);
        }
 }