Skip to content

Commit 01e0fb4

Browse files
authored
Fix #78: allow configuring UUIDClock to use for epoch-based UUID generation (#80)
1 parent a98db80 commit 01e0fb4

File tree

7 files changed

+93
-8
lines changed

7 files changed

+93
-8
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
-->
1616
<packaging>bundle</packaging>
1717
<name>Java UUID Generator</name>
18-
<version>4.2.1-SNAPSHOT</version>
18+
<version>4.3.0-SNAPSHOT</version>
1919
<description>
2020
Java UUID Generator (JUG) is a Java library for generating
2121
Universally Unique IDentifiers, UUIDs (see http://en.wikipedia.org/wiki/UUID).

release-notes/VERSION

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ Project: java-uuid-generator
44
Releases
55
============================================================================
66

7+
4.3.0 (23-Aug-2023)
8+
9+
#78: TimeBasedEpochGenerator (UUIDv7) can't be provided a `UUIDClock`
10+
(reported by @Frozenlock)
11+
712
4.2.0 (14-May-2023)
813

914
#73: Add `Generators.defaultTimeBasedGenerator()` to use "default" interface

src/main/java/com/fasterxml/uuid/Generators.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,23 @@ public static TimeBasedEpochGenerator timeBasedEpochGenerator(Random random)
141141
{
142142
return new TimeBasedEpochGenerator(random);
143143
}
144-
144+
145+
/**
146+
* Factory method for constructing UUID generator that generates UUID using
147+
* version 7 (time+random based), using specified Ethernet address
148+
* as the location part of UUID.
149+
* Timestamp to use is access using specified {@link UUIDClock}
150+
*
151+
* No additional external synchronization is used.
152+
*
153+
* @since 4.3
154+
*/
155+
public static TimeBasedEpochGenerator timeBasedEpochGenerator(Random random,
156+
UUIDClock clock)
157+
{
158+
return new TimeBasedEpochGenerator(random, clock);
159+
}
160+
145161
// // Time+location-based generation
146162

147163
/**

src/main/java/com/fasterxml/uuid/UUIDClock.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@
2626
*/
2727
public class UUIDClock
2828
{
29+
private final static UUIDClock DEFAULT = new UUIDClock();
30+
31+
/**
32+
* @since 4.3
33+
*/
34+
public final static UUIDClock systemTimeClock() {
35+
return DEFAULT;
36+
}
37+
2938
/**
3039
* Returns the current time in milliseconds.
3140
*/

src/main/java/com/fasterxml/uuid/UUIDTimer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ public class UUIDTimer
169169

170170
public UUIDTimer(Random rnd, TimestampSynchronizer sync) throws IOException
171171
{
172-
this(rnd, sync, new UUIDClock());
172+
this(rnd, sync, UUIDClock.systemTimeClock());
173173
}
174174

175175
/**

src/main/java/com/fasterxml/uuid/impl/TimeBasedEpochGenerator.java

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.util.concurrent.locks.ReentrantLock;
88

99
import com.fasterxml.uuid.NoArgGenerator;
10+
import com.fasterxml.uuid.UUIDClock;
1011
import com.fasterxml.uuid.UUIDType;
1112

1213
/**
@@ -37,6 +38,15 @@ public class TimeBasedEpochGenerator extends NoArgGenerator
3738
* Random number generator that this generator uses.
3839
*/
3940
protected final Random _random;
41+
42+
/**
43+
* Underlying {@link UUIDClock} used for accessing current time, to use for
44+
* generation.
45+
*
46+
* @since 4.3
47+
*/
48+
protected final UUIDClock _clock;
49+
4050
private long _lastTimestamp = -1;
4151
private final byte[] _lastEntropy = new byte[ENTROPY_BYTE_LENGTH];
4252
private final Lock lock = new ReentrantLock();
@@ -53,13 +63,24 @@ public class TimeBasedEpochGenerator extends NoArgGenerator
5363
* use a <b>good</b> (pseudo) random number generator; for example, JDK's
5464
* {@link SecureRandom}.
5565
*/
56-
57-
public TimeBasedEpochGenerator(Random rnd)
66+
public TimeBasedEpochGenerator(Random rnd) {
67+
this(rnd, UUIDClock.systemTimeClock());
68+
}
69+
70+
/**
71+
* @param rnd Random number generator to use for generating UUIDs; if null,
72+
* shared default generator is used. Note that it is strongly recommend to
73+
* use a <b>good</b> (pseudo) random number generator; for example, JDK's
74+
* {@link SecureRandom}.
75+
* @param clock clock Object used for accessing current time to use for generation
76+
*/
77+
public TimeBasedEpochGenerator(Random rnd, UUIDClock clock)
5878
{
5979
if (rnd == null) {
6080
rnd = LazyRandom.sharedSecureRandom();
6181
}
6282
_random = rnd;
83+
_clock = clock;
6384
}
6485

6586
/*
@@ -82,7 +103,7 @@ public UUID generate()
82103
{
83104
lock.lock();
84105
try {
85-
long rawTimestamp = System.currentTimeMillis();
106+
long rawTimestamp = _clock.currentTimeMillis();
86107
if (rawTimestamp == _lastTimestamp) {
87108
boolean c = true;
88109
for (int i = ENTROPY_BYTE_LENGTH - 1; i >= 0; i--) {

src/test/java/com/fasterxml/uuid/UUIDGeneratorTest.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
package com.fasterxml.uuid;
1919

20+
import static org.junit.Assert.assertNotEquals;
21+
2022
import java.nio.ByteBuffer;
2123
import java.security.MessageDigest;
2224
import java.util.*;
@@ -26,7 +28,6 @@
2628
import junit.framework.TestSuite;
2729
import junit.textui.TestRunner;
2830

29-
3031
import com.fasterxml.uuid.impl.UUIDUtil;
3132
import com.fasterxml.uuid.impl.NameBasedGenerator;
3233
import com.fasterxml.uuid.impl.RandomBasedGenerator;
@@ -38,6 +39,7 @@
3839
* JUnit Test class for the com.fasterxml.uuid.UUIDGenerator class.
3940
*
4041
* @author Eric Bie
42+
* @author Tatu Saloranta
4143
*/
4244
public class UUIDGeneratorTest extends TestCase
4345
{
@@ -254,7 +256,7 @@ public void testGenerateTimeBasedEpochUUID() throws Exception
254256
{
255257
// this test will attempt to check for reasonable behavior of the
256258
// generateTimeBasedUUID method
257-
259+
258260
Random entropy = new Random(0x666);
259261

260262
// we need a instance to use
@@ -296,6 +298,38 @@ public void testGenerateTimeBasedEpochUUID() throws Exception
296298
// check that all uuids have timestamps between the start and end time
297299
checkUUIDArrayForCorrectCreationTimeEpoch(uuid_array, start_time, end_time);
298300
}
301+
302+
// [#70]: allow use of custom UUIDClock
303+
public void testGenerateTimeBasedEpochUUIDWithFixedClock() throws Exception
304+
{
305+
final UUIDClock fixedClock = new UUIDClock() {
306+
@Override
307+
public long currentTimeMillis() {
308+
return 123L;
309+
}
310+
};
311+
// we need a instance to use
312+
TimeBasedEpochGenerator gen = Generators.timeBasedEpochGenerator(new Random(123),
313+
fixedClock);
314+
315+
UUID uuid1 = gen.generate();
316+
UUID uuid2 = gen.generate();
317+
UUID uuid3 = gen.generate();
318+
319+
// Alas! Was thinking of comparing fixed value, but even Epoch-based generator
320+
// forces uniqueness by default. So instead will only test that generation
321+
// works and produces unique instances
322+
323+
// First: should be unique (diff contents)
324+
assertNotEquals(uuid1, uuid2);
325+
assertNotEquals(uuid2, uuid3);
326+
assertNotEquals(uuid3, uuid1);
327+
328+
// Second: should not be same instances either:
329+
assertNotSame(uuid1, uuid2);
330+
assertNotSame(uuid2, uuid3);
331+
assertNotSame(uuid3, uuid1);
332+
}
299333

300334
/**
301335
* Test of generateNameBasedUUID(UUID, String)

0 commit comments

Comments
 (0)