Allow UlidFactory.java to accept a LongSupplier as a time source

Using a `java.time.Clock` to get the current time as milliseconds from epoch is inconvenient, as `java.time.Clock` is an abstract class requiring 3 methods to be implemented, which makes interoperability with other JVM languages e.g. Kotlin hard.

By exposing overloaded factory methods that accept a LongSupplier, this problem disappears without breaking backward compatibility.
This commit is contained in:
Michele Sollecito 2023-07-18 09:56:35 +01:00 committed by GitHub
parent a5006743ed
commit 3ecd6c84a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 343 additions and 298 deletions

View File

@ -49,7 +49,7 @@ import java.util.function.LongSupplier;
*/ */
public final class UlidFactory { public final class UlidFactory {
private final Clock clock; // for tests private final LongSupplier timeMillisNow; // for tests
private final LongFunction<Ulid> ulidFunction; private final LongFunction<Ulid> ulidFunction;
// ****************************** // ******************************
@ -64,12 +64,16 @@ public final class UlidFactory {
} }
private UlidFactory(LongFunction<Ulid> ulidFunction) { private UlidFactory(LongFunction<Ulid> ulidFunction) {
this(ulidFunction, null); this(ulidFunction, (LongSupplier) null);
} }
private UlidFactory(LongFunction<Ulid> ulidFunction, Clock clock) { private UlidFactory(LongFunction<Ulid> ulidFunction, Clock clock) {
this(ulidFunction, clock != null ? clock::millis : null);
}
private UlidFactory(LongFunction<Ulid> ulidFunction, LongSupplier timeMillisNow) {
this.ulidFunction = ulidFunction; this.ulidFunction = ulidFunction;
this.clock = clock != null ? clock : Clock.systemUTC(); this.timeMillisNow = timeMillisNow != null ? timeMillisNow : Clock.systemUTC()::millis;
} }
/** /**
@ -136,6 +140,17 @@ public final class UlidFactory {
return new UlidFactory(new MonotonicFunction(IRandom.newInstance(random))); return new UlidFactory(new MonotonicFunction(IRandom.newInstance(random)));
} }
/**
* Returns a new monotonic factory.
*
* @param random a {@link Random} generator
* @param timeMillisNow a function that returns the current time as milliseconds from epoch
* @return {@link UlidFactory}
*/
public static UlidFactory newMonotonicInstance(Random random, LongSupplier timeMillisNow) {
return new UlidFactory(new MonotonicFunction(IRandom.newInstance(random), timeMillisNow), timeMillisNow);
}
/** /**
* Returns a new monotonic factory. * Returns a new monotonic factory.
* <p> * <p>
@ -160,6 +175,19 @@ public final class UlidFactory {
return new UlidFactory(new MonotonicFunction(IRandom.newInstance(randomFunction))); return new UlidFactory(new MonotonicFunction(IRandom.newInstance(randomFunction)));
} }
/**
* Returns a new monotonic factory.
* <p>
* The given random function must return a long value.
*
* @param randomFunction a random function that returns a long value
* @param timeMillisNow a function that returns the current time as milliseconds from epoch
* @return {@link UlidFactory}
*/
public static UlidFactory newMonotonicInstance(LongSupplier randomFunction, LongSupplier timeMillisNow) {
return new UlidFactory(new MonotonicFunction(IRandom.newInstance(randomFunction), timeMillisNow), timeMillisNow);
}
/** /**
* Returns a new monotonic factory. * Returns a new monotonic factory.
* <p> * <p>
@ -170,7 +198,20 @@ public final class UlidFactory {
* @return {@link UlidFactory} * @return {@link UlidFactory}
*/ */
static UlidFactory newMonotonicInstance(LongSupplier randomFunction, Clock clock) { static UlidFactory newMonotonicInstance(LongSupplier randomFunction, Clock clock) {
return new UlidFactory(new MonotonicFunction(IRandom.newInstance(randomFunction), clock), clock); return UlidFactory.newMonotonicInstance(randomFunction, clock::millis);
}
/**
* Returns a new monotonic factory.
* <p>
* The given random function must return a byte array.
*
* @param randomFunction a random function that returns a byte array
* @param timeMillisNow a function that returns the current time as milliseconds from epoch
* @return {@link UlidFactory}
*/
public static UlidFactory newMonotonicInstance(IntFunction<byte[]> randomFunction, LongSupplier timeMillisNow) {
return new UlidFactory(new MonotonicFunction(IRandom.newInstance(randomFunction), timeMillisNow), timeMillisNow);
} }
/** /**
@ -183,7 +224,7 @@ public final class UlidFactory {
* @return {@link UlidFactory} * @return {@link UlidFactory}
*/ */
static UlidFactory newMonotonicInstance(IntFunction<byte[]> randomFunction, Clock clock) { static UlidFactory newMonotonicInstance(IntFunction<byte[]> randomFunction, Clock clock) {
return new UlidFactory(new MonotonicFunction(IRandom.newInstance(randomFunction), clock), clock); return UlidFactory.newMonotonicInstance(randomFunction, clock::millis);
} }
// ****************************** // ******************************
@ -196,7 +237,7 @@ public final class UlidFactory {
* @return a ULID * @return a ULID
*/ */
public synchronized Ulid create() { public synchronized Ulid create() {
return this.ulidFunction.apply(clock.millis()); return this.ulidFunction.apply(timeMillisNow.getAsLong());
} }
/** /**
@ -255,9 +296,13 @@ public final class UlidFactory {
} }
public MonotonicFunction(IRandom random, Clock clock) { public MonotonicFunction(IRandom random, Clock clock) {
this(random, clock::millis);
}
public MonotonicFunction(IRandom random, LongSupplier timeMillisNow) {
this.random = random; this.random = random;
// initialize internal state // initialize internal state
this.lastUlid = new Ulid(clock.millis(), this.random.nextBytes(Ulid.RANDOM_BYTES)); this.lastUlid = new Ulid(timeMillisNow.getAsLong(), this.random.nextBytes(Ulid.RANDOM_BYTES));
} }
@Override @Override