cowtowncoder / java-uuid-generator Goto Github PK
View Code? Open in Web Editor NEWJava Uuid Generator (JUG) is a Java library for generating all standard UUID versions (v1 - v7)
License: Apache License 2.0
Java Uuid Generator (JUG) is a Java library for generating all standard UUID versions (v1 - v7)
License: Apache License 2.0
(see https://reproducible-builds.org/docs/jvm/ for details)
Similar to latest (2.14) Jackson builds, it seems reasonable to enable reproducible builds for JUG.
This should be as simple as adding
<project.build.outputTimestamp>
in pom.xml
(and ensuring reasonable recent oss-parent
parent pom).
Packaging type should also be changed back to jar to get good timestamps in jar classes.
So as per:
https://www.ietf.org/id/draft-peabody-dispatch-new-uuid-format-03.html
there are 3 types; this is for implementing variant 7, time-based variant where timestamp is from Unix epoch (january 1st, 1970 UTC).
Support should include both reading and generating such UUIDs.
Hi
For some legacy stuff that I'm working on, I needed to be able to create UUID's for a given timestamp, so I created the following utility class create time based UUID's and convert them back to Instant:
public class UUIDUtil {
/**
* Converts a time based (type 1) uuid into a epoch nanos.
* Inspiration stolen from https://stackoverflow.com/questions/15179428/how-do-i-extract-a-date-from-a-uuid-using-java
*/
public static long toEpocNano(UUID uuid) {
long ts = uuid.timestamp();
return ((ts / 10l) - 12_219_292_800_000_000l) * 1000;
}
/**
* Converts a time based (type 1) uuid into an Instant.
*/
public static Instant toInstant(UUID uuid) {
return Instant.ofEpochSecond(0, toEpocNano(uuid));
}
/**
* Generate a time based UUID that matches the given instant time wise.
*/
public static UUID timeBased(Instant forInstant) {
try {
UUIDTimer timer = new UUIDTimer(new Random(), null, new FixedUUIDClock(forInstant));
return new TimeBasedGenerator(null, timer).generate();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@RequiredArgsConstructor
private static class FixedUUIDClock extends UUIDClock {
final Instant i;
@Override
public long currentTimeMillis() {
return i.toEpochMilli();
}
}
}
But while doing some testing of it I discovered that the generated UUID (when converved back to an Instant) has micro/nano-second precision.
Following unit test fails because of the 22 micros:
@Test
public void testNanos() {
Instant i = Instant.parse("2022-12-21T12:11:16.979Z");
UUID uuid = UUIDUtil.timeBased(i);
// 2022-12-21T12:11:16.979Z -> 2022-12-21T12:11:16.979022Z
assertThat(UUIDUtil.toInstant(uuid)).isEqualTo(i);
}
Am I doing something wrong here or should the generated UUID not have the same precision as the UUIDClock?
It seems that adding the FileBasedTimestampSynchronizer makes generating UUIDs a lot slower...I know intuitively this is kinda expected, but it's over an order of magnitude slower.
I copied the source and slapped some debugging around, it seems that it's not actually running the file sync very much at all, instead it seems to run slowDown() a lot because actDiff is always > 20000 everytime it hits that if block.
I don't really understand the system well enough to understand why adding the time sync would cause this.
in my test. Performace of this generator and JDK UUID is almost the same. So, do bother to use this for performace consideration.
`public class UtilsTest {
@Test
public void uuidPerformanceCompare() throws Exception {
uuidGeneratorPerformanceTest();
javaUuidGeneratorPerformanceTest();
}
public void uuidGeneratorPerformanceTest() throws Exception {
final int THREAD_COUNT = 100;
final CountDownLatch startLatch = new CountDownLatch(THREAD_COUNT + 1);
final AtomicBoolean stop = new AtomicBoolean(false);
final AtomicInteger generatedCount = new AtomicInteger(0);
for (int i = 0; i < THREAD_COUNT; ++i) {
new Thread(new Runnable() {
@Override
public void run() {
startLatch.countDown();
try {
startLatch.await();
while (!stop.get()) {
Utils.generateId();
generatedCount.incrementAndGet();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
Thread.sleep(100);
startLatch.countDown();
long startMs = System.currentTimeMillis();
System.out.println("starting generating");
TimeUnit.SECONDS.sleep(5);
stop.compareAndSet(false, true);
System.out.println(String.format("java-uuid-generator generate %s in %sms",
generatedCount.get(), System.currentTimeMillis() - startMs));
}
public void javaUuidGeneratorPerformanceTest() throws Exception {
final int THREAD_COUNT = 100;
final CountDownLatch startLatch = new CountDownLatch(THREAD_COUNT + 1);
final AtomicBoolean stop = new AtomicBoolean(false);
final AtomicInteger generatedCount = new AtomicInteger(0);
for (int i = 0; i < THREAD_COUNT; ++i) {
new Thread(new Runnable() {
@Override
public void run() {
startLatch.countDown();
try {
startLatch.await();
while (!stop.get()) {
Utils.sanitizeId(UUID.randomUUID().toString());
generatedCount.incrementAndGet();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
Thread.sleep(100);
startLatch.countDown();
long startMs = System.currentTimeMillis();
System.out.println("starting generating");
TimeUnit.SECONDS.sleep(5);
stop.compareAndSet(false, true);
System.out.println(String.format("java-sdk-UUID generate %s in %sms",
generatedCount.get(), System.currentTimeMillis() - startMs));
}
}`
test result:
starting generating java-uuid-generator generate 1010994 in 5004ms starting generating java-sdk-UUID generate 1097096 in 5004ms
This is a suggestion to add some documentation around the history of this library. I'm currently working on a project that uses v 2.0.0 of org.safehaus.jug. Is that an earlier version of this same library? Is this an improved version of that or is this a fork with some different motivation? There is a credits page mentioning some contributors but no mention of safehaus.
Also, the http://wiki.fasterxml.com/JugHome link doesn't work...
java.util.UUID#timestamp() only works for version 1 UUIDs.
It would be nice to have similar functionality for version 6 UUIDs.
E.g. a static method in TimeBasedReorderedGenerator, something along the lines of
public static long timestamp(UUID uuid)
{
assert(uuid.version()==6);
return ((uuid.getMostSignificantBits()>>>4) & ~0xFFF) | (uuid.getMostSignificantBits()&0xFFF);
}
Better yet, a static method in UUIDUtil that returns the number of millis since unix epoch for versions 1, 6 and 7 UUIDs.
thank you very much
Gabriel
Would you take a pull request for an additional generator implementation that generates v1 UUIDs but with the node portion replaced with random bits as per section 4.5 of the RFC?
Although class Jug
has main()
method and was intended as CLI tool, looks like jar metadata is missing the Main-Class
.
So let's add that in.
The below comment is not strictly true, is it? Because of the wonders of hotspot reordering etc, there is the possibility that a thread will see a non-null _sharedRandom that won't be fully constructed. Would it not be better to use volatile (if you want to maintain the side effects), or an AtomicReference (my preference). Both of these will only work in a 1.5+ JVM, 1.4 and below sync is your only choice.
/* Could be synchronized, but since side effects are trivial
* (ie. possibility of generating more than one SecureRandom,
* of which all but one are dumped) let's not add synchronization
* overhead.
*/
rnd = _sharedRandom;
if (rnd == null) {
_sharedRandom = rnd = new SecureRandom();
}
Are there any consequences to taking the absolute value of:
Generators.timeBasedGenerator().generate()?
It would be useful if we could instead pass in an instance of a Clock
so unit testing would be easier.
If your machine has a standard IP networking setup, the Generators.defaultTimeBasedGenerator
factory method will try to determine which network interface corresponds to the default route for all outgoing network traffic, and use that for creating a time based generator.
This is likely a good choice for common usage scenarios if you want a version 1 UUID generator.
Couldn't find any JavaDoc or examples. Can they be found somewhere? Or are they missing?
I tried running the following code to ensure that the ordering of the UUID v7 is correct:
val test = (0..20).map { Generators.timeBasedEpochGenerator().generate() }
val sorted = test.sorted()
test.forEachIndexed { index, uuid ->
println( uuid == sorted[index])
}
expect the equality check returns false for almost all entries. I have tried to run the same test with the library com.github.f4b6a3:uuid-creator:5.2.0
and there the checks always return true using the following code:
val test = (0..20).map { UuidCreator.getTimeOrderedEpoch() }
val sorted = test.sorted()
test.forEachIndexed { index, uuid ->
println( uuid == sorted[index])
}
Is this a mistake in the implementation?
I suggest to add a static method like the following to UUIDUtil (or any other appropriate class):
public static UUID generateThreadLocalRandomUUID() {
ThreadLocalRandom rnd = ThreadLocalRandom.current();
return UUIDUtil.constructUUID(UUIDType.RANDOM_BASED, rnd.nextLong(), rnd.nextLong());
}
Using a generator created by Generators.randomBasedGenerator(ThreadLocalRandom.current()) is probably not a good idea when working in an environment with thread pools (like Java/Jakarta EE), so i came to this solution.
Best regards, Robert
Calling Generators.randomBasedGenerator().generate() in version 3.0 or 3.1 gives the following stacktrace:
java.lang.NullPointerException
at com.fasterxml.uuid.impl.RandomBasedGenerator.generate(RandomBasedGenerator.java:93)
...
The documentation in the README and the code is using the word "variant" where "version" should be used. For example, the README has the following:
UUID uuid = Generators.timeBasedGenerator().generate(); // Variant 1
UUID uuid = Generators.randomBasedGenerator().generate(); // Variant 4
UUID uuid = Generators.nameBasedgenerator().generate("string to hash"); // Variant 5
// With JUG 4.1+: support for https://github.com/uuid6/uuid6-ietf-draft variants 6 and 7:
UUID uuid = Generators.timeBasedReorderedGenerator().generate(); // Variant 6
UUID uuid = Generators.timeBasedEpochGenerator().generate(); // Variant 7
These are referring to the UUID Versions (See: https://www.rfc-editor.org/rfc/rfc4122#section-4.1.3). The Variant of a UUID is distinct from the Version, and is tracked separately (See: https://www.rfc-editor.org/rfc/rfc4122#section-4.1.1). Also see https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-04.html#name-variant-and-version-fields.
POM comment says log4j is optional.
If that's really the case please set it scope to "provided" so that people don't have automatically added to their projects
It has been a while since I increased the JDK baseline and I think it would be reasonable to finally move to Java 8.
This would make it a little bit easier to maintain things (for example, able to build on JDK 17 which does not support Java 6 target).
This would be doable for version 4.1: I don't think a major version upgrade is warranted, although I am open to being convinced otherwise.
Feel free to add comments, especially if you have anything AGAINST proposal.
In some rare situations TimeBasedGenerator may produce UUIDs that are not increasing.
Consider following sample program:
TimeBasedGenerator generator = new TimeBasedGenerator(new EthernetAddress(0L), new UUIDTimer(new Random(), null));
UUID previous = generator.generate();
while (true) {
Thread.sleep(1000);
UUID current = generator.generate();
System.out.println(current + ", compareTo=" + current.compareTo(previous));
previous = current;
}
And sample output that shows the problem:
7ef7c38a-bb6e-11e3-9e8f-000000000000, compareTo=1
7f905a0b-bb6e-11e3-9e8f-000000000000, compareTo=1
8028f08c-bb6e-11e3-9e8f-000000000000, compareTo=-1 <------- problem is here
80c1870d-bb6e-11e3-9e8f-000000000000, compareTo=1
815a1d8e-bb6e-11e3-9e8f-000000000000, compareTo=1
As you can see when we hit 63bit boundary for mostSigBits in UUID we get instance that is actually smaller than previous instance.
It's necessary to add LICENSE information (separate file?), and also copyright notices, to make it easier for corporate entities to (re)use JUG.
Contrary to TimeBasedGenerator
, it looks like TimeBasedEpochGenerator
can't be provided with a UUIDTimer
.
I might be mistaken, but as far as I can tell this means it's impossible to generate a UUIDv7 with a particular timestamp.
Basically a v1 w/ some bytes swapped for more efficient indexing in a database.
edit: RFC for types 6 - 8 is here: https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format
Ok, I finally realized that java.util.UUID sucks: there are no methods to work with it as a byte sequence of 16 bytes. The only there are are string conversions and use of 2 longs.
So: to improve on this, let's ensure that JUG has appropriate additional conversion methods available (via UUIDUtil, I guess).
Thank you very much for providing java-uuid-generator
!
I tried to update to version 4.0
, but it seems like there is a problem with this version range:
org.slf4j;version="[1.7.29,)"
in MANIFEST.MF
.
When using org.apache.felix.framework
version 5.6.10
this version can not be parsed:
java.util.NoSuchElementException
at java.base/java.util.StringTokenizer.nextToken(StringTokenizer.java:349)
at org.osgi.framework.Version.(Version.java:126)
at org.apache.felix.framework.util.VersionRange.parse(VersionRange.java:93)
at org.apache.felix.framework.util.manifestparser.ManifestParser.normalizeImportClauses(ManifestParser.java:329)
at org.apache.felix.framework.util.manifestparser.ManifestParser.(ManifestParser.java:181)
at org.apache.felix.framework.BundleRevisionImpl.(BundleRevisionImpl.java:117)
at org.apache.felix.framework.BundleImpl.createRevision(BundleImpl.java:1282)
at org.apache.felix.framework.BundleImpl.(BundleImpl.java:113)
at org.apache.felix.framework.Felix.installBundle(Felix.java:3042)
at org.apache.felix.framework.BundleContextImpl.installBundle(BundleContextImpl.java:167)
at org.apache.felix.framework.BundleContextImpl.installBundle(BundleContextImpl.java:140)
I may be wrong, but I think the OSGI spec does not allow a such a version range (see: https://osgi.org/download/r5/osgi.core-5.0.0.pdf, page 27).
When I modify the java-uuid-generator
jar and change the version range to org.slf4j;version="[1.7.29,2)"
everything works fine.
Having this depend specifically on Log4J can produce a weird situation when used in projects that use other logging frameworks (which is the problem SLF4J, or depending only on API jars, tackle).
As far as I could see, there isn't a lot of complex things going on here that would require direct access to some Log4J-specific features, so I would like to know if you'd be willing to migrate this to SLF4J (or Log4J2-API).
I can port it myself, but I wanted to know your positioning on the matter before forking and creating a PR
So, since world is moving to JDK9+ versions that benefit (or even require) module-info
, let's bake that stuff in using Moditect plugin.
So as per:
there are 3 types; this is for implementing variant 8, DIY variant where actual definition of structure is not specified by spec; this can be used for fully custom uuids where semantics are specific to use case / context.
It may be that this just means ability to accept such uuids, or something more, depending on what makes most sense.
To get "new" version of project started, need to bring over sources.
(note: related to Draft-04 spec at: https://uuid6.github.io/uuid6-ietf-draft/ section 5.4)
So, looks like there will be the counterpart for "null UUID" (all zeroes) wherein all 128 bits are 1s, so-called "Max UUID".
This is specific in section 5.4 of the "new UUID" draft spec (draft-04):
https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-04.html
It should probably be defined similar to the existing null UUID (details to be filled)
I may be wrong, but according to my reading of RFC4122, the following lines of code in TimeBasedGenerator.java appear to be storing the clock id in a reversed format. The following code in the TimeBasedGenerator constructor:
uuidBytes[UUIDUtil.BYTE_OFFSET_CLOCK_SEQUENCE] = (byte) clockSeq;
uuidBytes[UUIDUtil.BYTE_OFFSET_CLOCK_SEQUENCE+1] = (byte) (clockSeq >> 8);
should probably look like this:
uuidBytes[UUIDUtil.BYTE_OFFSET_CLOCK_SEQUENCE + 1] = (byte) clockSeq;
uuidBytes[UUIDUtil.BYTE_OFFSET_CLOCK_SEQUENCE] = (byte) (clockSeq >> 8);
UUIDTimer is doing the same thing. Also, in UUIDTimer, the clock sequence should only be 14 bits, so it should look like this:
public int getClockSequence() {
return (_clockSequence & 0x3FFF);
}
Edit: I know the clock sequence is currently randomly generated. I was experimenting with it not being a random value.
UUIDTimer
has synchronized
on getTimestamp()
, which we want to eliminate.
On the same time, getTimestamp()
has a comment:
/**
* Method that constructs unique timestamp suitable for use for
* constructing UUIDs. Default implementation is fully synchronized;
* sub-classes may choose to implemented alternate strategies
*
* @return 64-bit timestamp to use for constructing UUID
*/
So, the comment tells something about possible subclasses with relaxed strategy.
On the same time, the whole UUIDTimer class is final and all main methods are final.
On a Win7 64bit system, EthernetAddress.fromInterface() is returning the address of a WAN Miniport instead of the NIC address.
This particular system has 20 entries returned by the NetworkInterface.getNetworkInterfaces() method. Entry 6 is "name:eth0 (WAN Miniport (IPv6))" and has an address of [-22, 50, 32, 82, 65, 83] in the byte array (ea:32:20:52:41:53). Having a length of 6, this is returning from the fromInterface() method as the MAC address of the machine. Entry 10 in the network interface enumeration is for the actual NIC, which never gets evaluated.
Running the msinfo32 utility on this machine and navigating to System Summary > Components > Network > Adapter shows the list of most of the network interface entries returned by the getNetworkInterfaces() method, with the Wan Miniport IPv6 entry above the actual NIC, though the MAC address for the WAN Miniport entry in that utility shows up as "Not Available".
As a current work-around I'm using the following code to get the correct MAC address before handing it to EthernetAddress:
InetAddress address = InetAddress.getLocalHost();
NetworkInterface nwi = NetworkInterface.getByInetAddress(address);
byte mac[] = nwi.getHardwareAddress();
@cowtowncoder , I used the following code Generators.timeBasedGenerator().generate()
to generate time based UUID. On the generated UUID, I found the timestamp using Java's UUID.timestamp()
, I see the timestamp is not current time . Is there a way to generate UUID with current time stamp?
We are using type 1 UUID timestamp based generator - TimeBasedGenerator
- which has getTimestamp() method which is synchronized. This is causing some performance bottleneck in our code.
My question is, can we instead use TimeBasedEpochGenerator
and still get the same functionality (uniqueness) of TimeBasedGenerator
UUID?
After enabling JaCoCo based code coverage, CC badge is finally working. Displayed level, 46%, is on low side of usual things so would be good to spend some time increasing code coverage -- I think addition of new UUID versions may have caused coverage to have decreased. Either way, getting to above 50% seems like a useful short-term thing to achieve.
EDIT:
I used the following dependency in my project
<dependency>
<groupId>com.fasterxml.uuid</groupId>
<artifactId>java-uuid-generator</artifactId>
<version>3.1.3</version>
</dependency>
After compiling the project , I could find the jar also in following location
.m2\repository\com\fasterxml\uuid\java-uuid-generator\3.1.3
But, When I try to use the command, I get "Generators cannot be resolved"
UUID uuiD= Generators.timeBasedGenerator().generate();
Am I missing Something?
The readme is very clear:
Generators are fully thread-safe, so a single instance may be shared among multiple threads.
That being said, I find this comment rather confusing:
/* As timer is not synchronized (nor _uuidBytes), need to sync; but most
* importantly, synchronize on timer which may also be shared between
* multiple instances
since there's no obvious synchronization in that method, it made me think the comment was meant for the caller.
I know this is not a javadoc comment, but still ...
Noticed that the code coverage badge is not yet working, should be.
Hello,
The wiki is void and the links inside are broken.
The LICENSE file says:
This copy of Java ClassMate library is licensed under Apache (Software) License,
while I believe that it should read:
This copy of Java UUID Generator (JUG) library is licensed under Apache (Software) License,
I assume that this is a mistake and the license has been copied and pasted from the Java ClassMate library. I appreciate that the README, POM and release notes all say that JUG is licensed under Apache License, Version 2.0, but for the avoidance of doubt, can you confirm this is the case?
I noticed that TimeBasedEpochGenerator may throw
throw new IllegalStateException("overflow on same millisecond");
I'm not clear on how applications should brace for this situation.
(This is more of a theoretical question, I haven't been able to actually trigger it in practice).
I looked at https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-04.html#name-uuid-version-7 a bit, and it sure looks like -in general- some corner cases should be handled by the application.
I believe that some comments would be helpful here.
thank you very much
Gabriel Balan
๐ I see it was merged in July but still isn't released.
Any chance to get a release in near future?
"multicase" should be "multicast"
As far as I can see I cannot generate UUID for different times. E.g. I would like to use time based uuid to keep track of all changes in our database, but I also would like to fill values for changes which happened until this introduction.
Currently (released version 3.1.1) the OSGi exports doesn't come with any version information. Therefore the framework falls back to the default export version, which is "0.0.0".
This could introduce problems in several cases and is not recommended.
Output from a Felix Shell:
java-uuid-generator [372] exports packages:
-------------------------------------------
com.fasterxml.uuid; version=0.0.0 UNUSED
com.fasterxml.uuid.ext; version=0.0.0 UNUSED
com.fasterxml.uuid.impl; version=0.0.0 UNUSED
It appears your build generates something like the following...
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
module com.fasterxml.uuid {
requires java.logging;
requires log4j;
exports com.fasterxml.uuid;
exports com.fasterxml.uuid.impl;
}
However, there's no log4j release that exports "log4j" (that I can find)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.