From 33df7a7e8ed9bd71df200654a05a2d9071df003b Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 24 Oct 2023 20:42:32 -0700 Subject: [PATCH 01/58] Fix #53: update JDK baseline to Java 8; misc CI improvements --- .github/workflows/main.yml | 10 +++++----- pom.xml | 10 +++++----- release-notes/VERSION | 4 ++++ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4a1c29d..92beba2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,6 +4,7 @@ on: branches: - master - "4.3" + - "3.0" paths-ignore: - "README.md" - "release-notes/*" @@ -18,15 +19,14 @@ jobs: strategy: fail-fast: false matrix: - # Alas, JDK14 can't be yet used as JUG builds for Java 6 - java_version: ['8', '11'] + java_version: ['8', '11', '17', '21'] os: ['ubuntu-20.04'] env: JAVA_OPTS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" steps: - - uses: actions/checkout@v3.5.3 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0 with: distribution: "temurin" java-version: ${{ matrix.java_version }} @@ -38,7 +38,7 @@ jobs: run: ./mvnw -B -q -ff -ntp test - name: Publish code coverage if: github.event_name != 'pull_request' && matrix.java_version == '8' - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4 with: token: ${{ secrets.CODECOV_TOKEN }} file: ./target/site/jacoco/jacoco.xml diff --git a/pom.xml b/pom.xml index d00b3dc..6e38afb 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.fasterxml oss-parent - 54 + 55 com.fasterxml.uuid java-uuid-generator @@ -15,7 +15,7 @@ --> bundle Java UUID Generator - 4.3.1-SNAPSHOT + 5.0.0-SNAPSHOT Java UUID Generator (JUG) is a Java library for generating Universally Unique IDentifiers, UUIDs (see http://en.wikipedia.org/wiki/UUID). @@ -28,7 +28,7 @@ JUG supports all 3 official UUID generation methods. scm:git:git://github.com/cowtowncoder/java-uuid-generator.git https://github.com/cowtowncoder/java-uuid-generator scm:git:git@github.com:cowtowncoder/java-uuid-generator.git - java-uuid-generator-4.3.0 + HEAD @@ -91,8 +91,8 @@ JUG supports all 3 official UUID generation methods. maven-compiler-plugin ${version.plugin.compiler} - 1.6 - 1.6 + 1.8 + 1.8 diff --git a/release-notes/VERSION b/release-notes/VERSION index ddaf53f..9b4d337 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -4,6 +4,10 @@ Project: java-uuid-generator Releases ============================================================================ +5.0.0 (not yet released) + +#53: Increase JDK baseline to JDK 8 + 4.3.0 (12-Sep-2023) #78: TimeBasedEpochGenerator (UUIDv7) can't be provided a `UUIDClock` From 196b138ac48728826d4caade78199fde23d63de7 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 24 Oct 2023 20:43:28 -0700 Subject: [PATCH 02/58] Update Dependabot settings --- .github/dependabot.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index daec318..f6faee6 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,6 +1,10 @@ version: 2 updates: - - package-ecosystem: "maven" + - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "daily" + interval: "weekly" + groups: + github-actions: + patterns: + - "*" From a892069c3e2684ed54beb019a5f598a7fcad6f44 Mon Sep 17 00:00:00 2001 From: Maia Everett Date: Wed, 25 Oct 2023 06:50:29 +0300 Subject: [PATCH 03/58] Use in-code lazy initialization for LazyRandom (for compatibility with native code generation tools) (#85) --- .../com/fasterxml/uuid/impl/LazyRandom.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/fasterxml/uuid/impl/LazyRandom.java b/src/main/java/com/fasterxml/uuid/impl/LazyRandom.java index 5d53b0f..5e13e41 100644 --- a/src/main/java/com/fasterxml/uuid/impl/LazyRandom.java +++ b/src/main/java/com/fasterxml/uuid/impl/LazyRandom.java @@ -9,9 +9,26 @@ */ public final class LazyRandom { - private final static SecureRandom shared = new SecureRandom(); + private static final Object lock = new Object(); + private static volatile SecureRandom shared; public static SecureRandom sharedSecureRandom() { - return shared; + // Double check lazy initialization idiom (Effective Java 3rd edition item 11.6) + // Use so that native code generation tools do not detect a SecureRandom instance in a static final field. + SecureRandom result = shared; + + if (result != null) { + return result; + } + + synchronized (lock) { + result = shared; + + if (result == null) { + result = shared = new SecureRandom(); + } + + return result; + } } } \ No newline at end of file From 1a9936b7b44f59f47f7d892acf7c2c8bb90fe07c Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 24 Oct 2023 20:54:18 -0700 Subject: [PATCH 04/58] Post-merge tweaks to #85, update release notes --- release-notes/CREDITS | 4 ++++ release-notes/VERSION | 2 ++ .../java/com/fasterxml/uuid/impl/LazyRandom.java | 16 +++++----------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/release-notes/CREDITS b/release-notes/CREDITS index ecaf1c1..e4315fc 100644 --- a/release-notes/CREDITS +++ b/release-notes/CREDITS @@ -131,3 +131,7 @@ Paul Galbraith (pgalbraith@github) * Contributed #73: Add `Generators.defaultTimeBasedGenerator()` to use "default" interface address for time/based UUIDs [4.2.0] + +Maia Everett (Maia-Everett@github) + * Contributed #85: Fix `LazyRandom` for native code generation tools + [5.0.0] diff --git a/release-notes/VERSION b/release-notes/VERSION index 9b4d337..64add4f 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -7,6 +7,8 @@ Releases 5.0.0 (not yet released) #53: Increase JDK baseline to JDK 8 +#85: Fix `LazyRandom` for native code generation tools + (contributed by @Maia-Everett) 4.3.0 (12-Sep-2023) diff --git a/src/main/java/com/fasterxml/uuid/impl/LazyRandom.java b/src/main/java/com/fasterxml/uuid/impl/LazyRandom.java index 5e13e41..b9424d7 100644 --- a/src/main/java/com/fasterxml/uuid/impl/LazyRandom.java +++ b/src/main/java/com/fasterxml/uuid/impl/LazyRandom.java @@ -6,6 +6,9 @@ * Trivial helper class that uses class loading as synchronization * mechanism for lazy instantiation of the shared secure random * instance. + *

+ * Since 5.0 has been lazily created to avoid issues with native-generation + * tools like Graal. */ public final class LazyRandom { @@ -13,19 +16,10 @@ public final class LazyRandom private static volatile SecureRandom shared; public static SecureRandom sharedSecureRandom() { - // Double check lazy initialization idiom (Effective Java 3rd edition item 11.6) - // Use so that native code generation tools do not detect a SecureRandom instance in a static final field. - SecureRandom result = shared; - - if (result != null) { - return result; - } - synchronized (lock) { - result = shared; - + SecureRandom result = shared; if (result == null) { - result = shared = new SecureRandom(); + shared = result = new SecureRandom(); } return result; From c16974c5307090c1788aeaf9c1bc0e952811b69f Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 24 Oct 2023 20:56:01 -0700 Subject: [PATCH 05/58] Update latest version ref'd in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 83e1eed..6ce06a4 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Maven coordinates are: com.fasterxml.uuid java-uuid-generator - 4.1.1 + 4.3.0 ``` From 861fd3da0bd5f03e37ada3a82b2fab47fc6faf7b Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 24 Oct 2023 20:59:55 -0700 Subject: [PATCH 06/58] Minor improvement to project desc --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6e38afb..97af4ac 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ Java UUID Generator (JUG) is a Java library for generating Universally Unique IDentifiers, UUIDs (see http://en.wikipedia.org/wiki/UUID). It can be used either as a component in a bigger application, or as a standalone command line tool. JUG generates UUIDs according to the IETF UUID draft specification. -JUG supports all 3 official UUID generation methods. +JUG supports 3 original official UUID generation methods as well as later additions (v6, v7) https://github.com/cowtowncoder/java-uuid-generator From 1e40e100b723877c77f5d2ddef99b0827165495b Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 3 Nov 2023 16:14:02 -0700 Subject: [PATCH 07/58] Trivial simplification wrt UTF-8 Charset access --- .../java/com/fasterxml/uuid/impl/NameBasedGenerator.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/fasterxml/uuid/impl/NameBasedGenerator.java b/src/main/java/com/fasterxml/uuid/impl/NameBasedGenerator.java index 9e76a56..332bc0c 100644 --- a/src/main/java/com/fasterxml/uuid/impl/NameBasedGenerator.java +++ b/src/main/java/com/fasterxml/uuid/impl/NameBasedGenerator.java @@ -1,6 +1,7 @@ package com.fasterxml.uuid.impl; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.UUID; @@ -18,10 +19,7 @@ */ public class NameBasedGenerator extends StringArgGenerator { - public final static Charset _utf8; - static { - _utf8 = Charset.forName("UTF-8"); - } + public final static Charset _utf8 = StandardCharsets.UTF_8; private final LoggerFacade _logger = LoggerFacade.getLogger(getClass()); From 3c01a59720d5d8fa1b31a25b913047bf19d8cbd7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 12:33:39 -0800 Subject: [PATCH 08/58] Bump the github-actions group with 1 update (#89) --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 92beba2..fc09c7f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,7 +26,7 @@ jobs: steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Set up JDK - uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0 + uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0 with: distribution: "temurin" java-version: ${{ matrix.java_version }} From e8ee886e29284be81c01a0097878aa363fa4e76e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 12:54:57 -0800 Subject: [PATCH 09/58] Bump the github-actions group with 1 update (#92) --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fc09c7f..e50eb1f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -38,7 +38,7 @@ jobs: run: ./mvnw -B -q -ff -ntp test - name: Publish code coverage if: github.event_name != 'pull_request' && matrix.java_version == '8' - uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4 + uses: codecov/codecov-action@4fe8c5f003fae66aa5ebb77cfd3e7bfbbda0b6b0 # v3.1.5 with: token: ${{ secrets.CODECOV_TOKEN }} file: ./target/site/jacoco/jacoco.xml From 31408f5c088d27766269f905896efe383b38a46e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 13:59:32 -0800 Subject: [PATCH 10/58] Bump the github-actions group with 1 update (#93) --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e50eb1f..91c9d5d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -38,7 +38,7 @@ jobs: run: ./mvnw -B -q -ff -ntp test - name: Publish code coverage if: github.event_name != 'pull_request' && matrix.java_version == '8' - uses: codecov/codecov-action@4fe8c5f003fae66aa5ebb77cfd3e7bfbbda0b6b0 # v3.1.5 + uses: codecov/codecov-action@e0b68c6749509c5f83f984dd99a76a1c1a231044 # v4.0.1 with: token: ${{ secrets.CODECOV_TOKEN }} file: ./target/site/jacoco/jacoco.xml From 191f37f2cc02ce95e8e4669004eac2f761dab76d Mon Sep 17 00:00:00 2001 From: Pavel Date: Tue, 20 Feb 2024 00:38:50 +0300 Subject: [PATCH 11/58] Adds utility method to get timestamp from time-based UUIDs (#95) --- release-notes/VERSION | 4 ++ .../com/fasterxml/uuid/impl/UUIDUtil.java | 62 +++++++++++++++++++ .../com/fasterxml/uuid/impl/UUIDUtilTest.java | 41 ++++++++++++ 3 files changed, 107 insertions(+) diff --git a/release-notes/VERSION b/release-notes/VERSION index 64add4f..f621b53 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -7,6 +7,10 @@ Releases 5.0.0 (not yet released) #53: Increase JDK baseline to JDK 8 +#81: Add `UUIDUtil.extractTimestamp()` for extracting 64-bit timestamp for + all timestamp-based versions + (requested by @gabrielbalan) + (contributed by @magdel) #85: Fix `LazyRandom` for native code generation tools (contributed by @Maia-Everett) diff --git a/src/main/java/com/fasterxml/uuid/impl/UUIDUtil.java b/src/main/java/com/fasterxml/uuid/impl/UUIDUtil.java index 41c8984..60ce671 100644 --- a/src/main/java/com/fasterxml/uuid/impl/UUIDUtil.java +++ b/src/main/java/com/fasterxml/uuid/impl/UUIDUtil.java @@ -353,4 +353,66 @@ private final static void _checkUUIDByteArray(byte[] bytes, int offset) throw new IllegalArgumentException("Invalid offset ("+offset+") passed: not enough room in byte array (need 16 bytes)"); } } + + /** + * Extract 64-bit timestamp from time-based UUIDs (if time-based type); + * returns 0 for other types. + * + * @param uuid uuid timestamp to extract from + * + * @return timestamp in milliseconds (since Epoch), or 0 if type does not support timestamps + * + * @since 5.0.0 + */ + public static long extractTimestamp(UUID uuid) + { + UUIDType type = typeOf(uuid); + if (type == null) { + // Likely null UUID: + return 0L; + } + switch (type) { + case NAME_BASED_SHA1: + case UNKNOWN: + case DCE: + case RANDOM_BASED: + case FREE_FORM: + case NAME_BASED_MD5: + return 0L; + case TIME_BASED: + return _getTimestampFromUuidV1(uuid); + case TIME_BASED_REORDERED: + return _getTimestampFromUuidV6(uuid); + case TIME_BASED_EPOCH: + return _getTimestampFromUuidV7(uuid); + default: + throw new IllegalArgumentException("Invalid `UUID`: unexpected type " + type); + } + } + + private static long _getTimestampFromUuidV1(UUID uuid) { + long mostSignificantBits = uuid.getMostSignificantBits(); + mostSignificantBits = mostSignificantBits & 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1110_1111_1111_1111L; + long low = mostSignificantBits >>> 32; + long lowOfHigher = mostSignificantBits & 0xFFFF0000L; + lowOfHigher = lowOfHigher >>> 16; + long highOfHigher = mostSignificantBits & 0xFFFFL; + return highOfHigher << 48 | lowOfHigher << 32 | low; + } + + private static long _getTimestampFromUuidV6(UUID uuid) { + long mostSignificantBits = uuid.getMostSignificantBits(); + mostSignificantBits = mostSignificantBits & 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1001_1111_1111_1111L; + long lowL = mostSignificantBits & 0xFFFL; + long lowH = mostSignificantBits & 0xFFFF0000L; + lowH = lowH >>> 16; + long high = mostSignificantBits & 0xFFFFFFFF00000000L; + return high >>> 4 | lowH << 12 | lowL; + } + + private static long _getTimestampFromUuidV7(UUID uuid) { + long mostSignificantBits = uuid.getMostSignificantBits(); + mostSignificantBits = mostSignificantBits & 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1001_1111_1111_1111L; + return mostSignificantBits >>> 16; + } } diff --git a/src/test/java/com/fasterxml/uuid/impl/UUIDUtilTest.java b/src/test/java/com/fasterxml/uuid/impl/UUIDUtilTest.java index 2e43517..bcf972f 100644 --- a/src/test/java/com/fasterxml/uuid/impl/UUIDUtilTest.java +++ b/src/test/java/com/fasterxml/uuid/impl/UUIDUtilTest.java @@ -1,7 +1,9 @@ package com.fasterxml.uuid.impl; +import java.util.Random; import java.util.UUID; +import com.fasterxml.uuid.Generators; import junit.framework.TestCase; /** @@ -13,6 +15,8 @@ */ public class UUIDUtilTest extends TestCase { + final static int TEST_REPS = 1_000_000; + public void testNilUUID() { UUID nil = UUIDUtil.nilUUID(); // Should be all zeroes: @@ -26,4 +30,41 @@ public void testMaxUUID() { assertEquals(~0, max.getMostSignificantBits()); assertEquals(~0, max.getLeastSignificantBits()); } + + public void testExtractTimestampUUIDTimeBased() { + TimeBasedGenerator generator = Generators.timeBasedGenerator(); + final Random rnd = new Random(1); + for (int i = 0; i < TEST_REPS; i++) { + long rawTimestamp = rnd.nextLong() >>> 4; + UUID uuid = generator.construct(rawTimestamp); + assertEquals(rawTimestamp, UUIDUtil.extractTimestamp(uuid)); + } + } + + public void testExtractTimestampUUIDTimeBasedReordered() { + TimeBasedReorderedGenerator generator = Generators.timeBasedReorderedGenerator(); + final Random rnd = new Random(2); + for (int i = 0; i < TEST_REPS; i++) { + long rawTimestamp = rnd.nextLong() >>> 4; + UUID uuid = generator.construct(rawTimestamp); + assertEquals(rawTimestamp, UUIDUtil.extractTimestamp(uuid)); + } + } + + public void testExtractTimestampUUIDEpochBased() { + TimeBasedEpochGenerator generator = Generators.timeBasedEpochGenerator(); + final Random rnd = new Random(3); + for (int i = 0; i < TEST_REPS; i++) { + long rawTimestamp = rnd.nextLong() >>> 16; + UUID uuid = generator.construct(rawTimestamp); + assertEquals(rawTimestamp, UUIDUtil.extractTimestamp(uuid)); + } + } + + public void testExtractTimestampUUIDOnOtherValues() { + assertEquals(0L, UUIDUtil.extractTimestamp(null)); + assertEquals(0L, UUIDUtil.extractTimestamp(UUID.fromString("00000000-0000-0000-0000-000000000000"))); + assertEquals(0L, UUIDUtil.extractTimestamp(UUIDUtil.nilUUID())); + assertEquals(0L, UUIDUtil.extractTimestamp(UUIDUtil.maxUUID())); + } } From cd9bf67566de3b58394c23b01eb6fd2223c662b0 Mon Sep 17 00:00:00 2001 From: Pavel Date: Wed, 21 Feb 2024 07:39:52 +0300 Subject: [PATCH 12/58] Add alternate version to UUIDv7 generator that uses random values on every call (not just for different timestamp) (#94) --- README.md | 1 + release-notes/VERSION | 3 + .../java/com/fasterxml/uuid/Generators.java | 50 ++++++- src/main/java/com/fasterxml/uuid/Jug.java | 11 ++ .../com/fasterxml/uuid/UUIDGenerator.java | 28 ++++ .../uuid/impl/NameBasedGenerator.java | 2 +- .../uuid/impl/RandomBasedGenerator.java | 22 --- .../uuid/impl/TimeBasedEpochGenerator.java | 28 ---- .../impl/TimeBasedEpochRandomGenerator.java | 129 ++++++++++++++++++ .../com/fasterxml/uuid/impl/UUIDUtil.java | 2 +- .../fasterxml/uuid/UUIDComparatorTest.java | 1 + .../com/fasterxml/uuid/UUIDGeneratorTest.java | 73 +++++++++- 12 files changed, 289 insertions(+), 61 deletions(-) create mode 100644 src/main/java/com/fasterxml/uuid/impl/TimeBasedEpochRandomGenerator.java diff --git a/README.md b/README.md index 6ce06a4..e84d56b 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ UUID uuid = Generators.nameBasedgenerator().generate("string to hash"); // Versi // With JUG 4.1+: support for https://github.com/uuid6/uuid6-ietf-draft versions 6 and 7: UUID uuid = Generators.timeBasedReorderedGenerator().generate(); // Version 6 UUID uuid = Generators.timeBasedEpochGenerator().generate(); // Version 7 +UUID uuid = Generators.timeBasedEpochRandomGenerator().generate(); // Version 7 with per-call random values ``` If you want customize generators, you may also just want to hold on to generator instance: diff --git a/release-notes/VERSION b/release-notes/VERSION index f621b53..d7db8cd 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -13,6 +13,9 @@ Releases (contributed by @magdel) #85: Fix `LazyRandom` for native code generation tools (contributed by @Maia-Everett) +#94: Add alternate version to UUIDv7 generator that uses random values on every + call (not just for different timestamp) + (contributed by @magdel) 4.3.0 (12-Sep-2023) diff --git a/src/main/java/com/fasterxml/uuid/Generators.java b/src/main/java/com/fasterxml/uuid/Generators.java index 1c4fe6b..9a17506 100644 --- a/src/main/java/com/fasterxml/uuid/Generators.java +++ b/src/main/java/com/fasterxml/uuid/Generators.java @@ -23,6 +23,7 @@ import com.fasterxml.uuid.impl.NameBasedGenerator; import com.fasterxml.uuid.impl.RandomBasedGenerator; import com.fasterxml.uuid.impl.TimeBasedEpochGenerator; +import com.fasterxml.uuid.impl.TimeBasedEpochRandomGenerator; import com.fasterxml.uuid.impl.TimeBasedReorderedGenerator; import com.fasterxml.uuid.impl.TimeBasedGenerator; @@ -135,6 +136,10 @@ public static TimeBasedEpochGenerator timeBasedEpochGenerator() * Factory method for constructing UUID generator that generates UUID using * version 7 (Unix Epoch time+random based), using specified {@link Random} * number generator. + *

+ * NOTE: calls within same millisecond produce very similar values; this may be + * unsafe in some environments. + *

* No additional external synchronization is used. */ public static TimeBasedEpochGenerator timeBasedEpochGenerator(Random random) @@ -145,9 +150,12 @@ public static TimeBasedEpochGenerator timeBasedEpochGenerator(Random random) /** * Factory method for constructing UUID generator that generates UUID using * version 7 (Unix Epoch time+random based), using specified {@link Random} - * number generato. - * Timestamp to use is accessed using specified {@link UUIDClock} - * + * number generator. + * Timestamp to use is accessed using specified {@link UUIDClock}. + *

+ * NOTE: calls within same millisecond produce very similar values; this may be + * unsafe in some environments. + *

* No additional external synchronization is used. * * @since 4.3 @@ -158,6 +166,42 @@ public static TimeBasedEpochGenerator timeBasedEpochGenerator(Random random, return new TimeBasedEpochGenerator(random, clock); } + /** + * Factory method for constructing UUID generator that generates UUID using + * version 7 (Unix Epoch time+random based), using specified {@link Random} + * number generator. + *

+ * Calls within same millisecond use additional per-call randomness to try to create + * more distinct values, compared to {@link #timeBasedEpochGenerator(Random)} + *

+ * No additional external synchronization is used. + * + * @since 5.0 + */ + public static TimeBasedEpochRandomGenerator timeBasedEpochRandomGenerator(Random random) + { + return new TimeBasedEpochRandomGenerator(random); + } + + /** + * Factory method for constructing UUID generator that generates UUID using + * version 7 (Unix Epoch time+random based), using specified {@link Random} + * number generator. + * Timestamp to use is accessed using specified {@link UUIDClock} + *

+ * Calls within same millisecond use additional per-call randomness to try to create + * more distinct values, compared to {@link #timeBasedEpochGenerator(Random)} + *

+ * No additional external synchronization is used. + * + * @since 5.0 + */ + public static TimeBasedEpochRandomGenerator timeBasedEpochRandomGenerator(Random random, + UUIDClock clock) + { + return new TimeBasedEpochRandomGenerator(random, clock); + } + // // Time+location-based generation /** diff --git a/src/main/java/com/fasterxml/uuid/Jug.java b/src/main/java/com/fasterxml/uuid/Jug.java index 08aac47..3b9a71f 100644 --- a/src/main/java/com/fasterxml/uuid/Jug.java +++ b/src/main/java/com/fasterxml/uuid/Jug.java @@ -33,6 +33,7 @@ public class Jug TYPES.put("name-based", "n"); TYPES.put("reordered-time-based", "o"); // Version 6 TYPES.put("epoch-time-based", "e"); // Version 7 + TYPES.put("random-epoch-time-based", "m"); // Version 7 but more random } protected final static HashMap OPTIONS = new HashMap(); @@ -266,6 +267,16 @@ public static void main(String[] args) noArgGenerator = Generators.timeBasedEpochGenerator(r); } break; + case 'm': // random-epoch-time-based + usesRnd = true; + { + SecureRandom r = new SecureRandom(); + if (verbose) { + System.out.print("(using secure random generator, info = '"+r.getProvider().getInfo()+"')"); + } + noArgGenerator = Generators.timeBasedEpochRandomGenerator(r); + } + break; case 'n': // name-based if (nameSpace == null) { System.err.println("--name-space (-s) - argument missing when using method that requires it, exiting."); diff --git a/src/main/java/com/fasterxml/uuid/UUIDGenerator.java b/src/main/java/com/fasterxml/uuid/UUIDGenerator.java index aa81034..ebd1ec3 100644 --- a/src/main/java/com/fasterxml/uuid/UUIDGenerator.java +++ b/src/main/java/com/fasterxml/uuid/UUIDGenerator.java @@ -46,4 +46,32 @@ protected UUIDGenerator() { } * generator instance will produce. */ public abstract UUIDType getType(); + + /* + /********************************************************** + /* Helper methods for implementations + /********************************************************** + */ + + protected final static long _toLong(byte[] buffer, int offset) + { + long l1 = _toInt(buffer, offset); + long l2 = _toInt(buffer, offset+4); + long l = (l1 << 32) + ((l2 << 32) >>> 32); + return l; + } + + protected final static long _toInt(byte[] buffer, int offset) + { + return (buffer[offset] << 24) + + ((buffer[++offset] & 0xFF) << 16) + + ((buffer[++offset] & 0xFF) << 8) + + (buffer[++offset] & 0xFF); + } + + protected final static long _toShort(byte[] buffer, int offset) + { + return ((buffer[offset] & 0xFF) << 8) + + (buffer[++offset] & 0xFF); + } } diff --git a/src/main/java/com/fasterxml/uuid/impl/NameBasedGenerator.java b/src/main/java/com/fasterxml/uuid/impl/NameBasedGenerator.java index 332bc0c..096d32f 100644 --- a/src/main/java/com/fasterxml/uuid/impl/NameBasedGenerator.java +++ b/src/main/java/com/fasterxml/uuid/impl/NameBasedGenerator.java @@ -116,7 +116,7 @@ public NameBasedGenerator(UUID namespace, MessageDigest digester, UUIDType type) @Override public UUID generate(String name) { - // !!! TODO: 14-Oct-2010, tatu: can repurpose faster UTF-8 encoding from Jackson + // !!! TODO: 14-Oct-2010, tatu: could re-purpose faster UTF-8 encoding from Jackson return generate(name.getBytes(_utf8)); } diff --git a/src/main/java/com/fasterxml/uuid/impl/RandomBasedGenerator.java b/src/main/java/com/fasterxml/uuid/impl/RandomBasedGenerator.java index 776ea4b..ecdfe5d 100644 --- a/src/main/java/com/fasterxml/uuid/impl/RandomBasedGenerator.java +++ b/src/main/java/com/fasterxml/uuid/impl/RandomBasedGenerator.java @@ -92,26 +92,4 @@ public UUID generate() } return UUIDUtil.constructUUID(UUIDType.RANDOM_BASED, r1, r2); } - - /* - /********************************************************************** - /* Internal methods - /********************************************************************** - */ - - protected final static long _toLong(byte[] buffer, int offset) - { - long l1 = _toInt(buffer, offset); - long l2 = _toInt(buffer, offset+4); - long l = (l1 << 32) + ((l2 << 32) >>> 32); - return l; - } - - private final static long _toInt(byte[] buffer, int offset) - { - return (buffer[offset] << 24) - + ((buffer[++offset] & 0xFF) << 16) - + ((buffer[++offset] & 0xFF) << 8) - + (buffer[++offset] & 0xFF); - } } diff --git a/src/main/java/com/fasterxml/uuid/impl/TimeBasedEpochGenerator.java b/src/main/java/com/fasterxml/uuid/impl/TimeBasedEpochGenerator.java index b835492..0e01195 100644 --- a/src/main/java/com/fasterxml/uuid/impl/TimeBasedEpochGenerator.java +++ b/src/main/java/com/fasterxml/uuid/impl/TimeBasedEpochGenerator.java @@ -144,32 +144,4 @@ public UUID construct(long rawTimestamp) lock.unlock(); } } - - /* - /********************************************************************** - /* Internal methods - /********************************************************************** - */ - - protected final static long _toLong(byte[] buffer, int offset) - { - long l1 = _toInt(buffer, offset); - long l2 = _toInt(buffer, offset+4); - long l = (l1 << 32) + ((l2 << 32) >>> 32); - return l; - } - - private final static long _toInt(byte[] buffer, int offset) - { - return (buffer[offset] << 24) - + ((buffer[++offset] & 0xFF) << 16) - + ((buffer[++offset] & 0xFF) << 8) - + (buffer[++offset] & 0xFF); - } - - private final static long _toShort(byte[] buffer, int offset) - { - return ((buffer[offset] & 0xFF) << 8) - + (buffer[++offset] & 0xFF); - } } diff --git a/src/main/java/com/fasterxml/uuid/impl/TimeBasedEpochRandomGenerator.java b/src/main/java/com/fasterxml/uuid/impl/TimeBasedEpochRandomGenerator.java new file mode 100644 index 0000000..53b9cbc --- /dev/null +++ b/src/main/java/com/fasterxml/uuid/impl/TimeBasedEpochRandomGenerator.java @@ -0,0 +1,129 @@ +package com.fasterxml.uuid.impl; + +import com.fasterxml.uuid.NoArgGenerator; +import com.fasterxml.uuid.UUIDClock; +import com.fasterxml.uuid.UUIDType; + +import java.security.SecureRandom; +import java.util.Random; +import java.util.UUID; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Implementation of UUID generator that uses time/location based generation + * method field from the Unix Epoch timestamp source - the number of + * milliseconds seconds since midnight 1 Jan 1970 UTC, leap seconds excluded. + * This is usually referred to as "Version 7". + * In addition to that random part is regenerated for every new UUID. + * This removes possibilities to have almost similar UUID, when calls + * to generate are made within same millisecond. + *

+ * As all JUG provided implementations, this generator is fully thread-safe. + * Additionally it can also be made externally synchronized with other instances + * (even ones running on other JVMs); to do this, use + * {@link com.fasterxml.uuid.ext.FileBasedTimestampSynchronizer} (or + * equivalent). + * + * @since 5.0 + */ +public class TimeBasedEpochRandomGenerator extends NoArgGenerator +{ + private static final int ENTROPY_BYTE_LENGTH = 10; + + /* + /********************************************************************** + /* Configuration + /********************************************************************** + */ + + /** + * Random number generator that this generator uses. + */ + protected final Random _random; + + /** + * Underlying {@link UUIDClock} used for accessing current time, to use for + * generation. + */ + protected final UUIDClock _clock; + + private final byte[] _lastEntropy = new byte[ENTROPY_BYTE_LENGTH]; + private final Lock lock = new ReentrantLock(); + + /* + /********************************************************************** + /* Construction + /********************************************************************** + */ + + /** + * @param rnd Random number generator to use for generating UUIDs; if null, + * shared default generator is used. Note that it is strongly recommend to + * use a good (pseudo) random number generator; for example, JDK's + * {@link SecureRandom}. + */ + public TimeBasedEpochRandomGenerator(Random rnd) { + this(rnd, UUIDClock.systemTimeClock()); + } + + /** + * @param rnd Random number generator to use for generating UUIDs; if null, + * shared default generator is used. Note that it is strongly recommend to + * use a good (pseudo) random number generator; for example, JDK's + * {@link SecureRandom}. + * @param clock clock Object used for accessing current time to use for generation + */ + public TimeBasedEpochRandomGenerator(Random rnd, UUIDClock clock) + { + if (rnd == null) { + rnd = LazyRandom.sharedSecureRandom(); + } + _random = rnd; + _clock = clock; + } + + /* + /********************************************************************** + /* Access to config + /********************************************************************** + */ + + @Override + public UUIDType getType() { return UUIDType.TIME_BASED_EPOCH; } + + /* + /********************************************************************** + /* UUID generation + /********************************************************************** + */ + + @Override + public UUID generate() + { + return construct(_clock.currentTimeMillis()); + } + + /** + * Method that will construct actual {@link UUID} instance for given + * unix epoch timestamp: called by {@link #generate()} but may alternatively be + * called directly to construct an instance with known timestamp. + * NOTE: calling this method directly produces somewhat distinct UUIDs as + * "entropy" value is still generated as necessary to avoid producing same + * {@link UUID} even if same timestamp is being passed. + * + * @param rawTimestamp unix epoch millis + * + * @return unix epoch time based UUID + */ + public UUID construct(long rawTimestamp) + { + lock.lock(); + try { + _random.nextBytes(_lastEntropy); + return UUIDUtil.constructUUID(UUIDType.TIME_BASED_EPOCH, (rawTimestamp << 16) | _toShort(_lastEntropy, 0), _toLong(_lastEntropy, 2)); + } finally { + lock.unlock(); + } + } +} diff --git a/src/main/java/com/fasterxml/uuid/impl/UUIDUtil.java b/src/main/java/com/fasterxml/uuid/impl/UUIDUtil.java index 60ce671..fffea93 100644 --- a/src/main/java/com/fasterxml/uuid/impl/UUIDUtil.java +++ b/src/main/java/com/fasterxml/uuid/impl/UUIDUtil.java @@ -362,7 +362,7 @@ private final static void _checkUUIDByteArray(byte[] bytes, int offset) * * @return timestamp in milliseconds (since Epoch), or 0 if type does not support timestamps * - * @since 5.0.0 + * @since 5.0 */ public static long extractTimestamp(UUID uuid) { diff --git a/src/test/java/com/fasterxml/uuid/UUIDComparatorTest.java b/src/test/java/com/fasterxml/uuid/UUIDComparatorTest.java index 6103a66..3d79ec9 100644 --- a/src/test/java/com/fasterxml/uuid/UUIDComparatorTest.java +++ b/src/test/java/com/fasterxml/uuid/UUIDComparatorTest.java @@ -22,6 +22,7 @@ import com.fasterxml.uuid.impl.TimeBasedEpochGenerator; +import com.fasterxml.uuid.impl.TimeBasedEpochRandomGenerator; import junit.framework.TestCase; public class UUIDComparatorTest diff --git a/src/test/java/com/fasterxml/uuid/UUIDGeneratorTest.java b/src/test/java/com/fasterxml/uuid/UUIDGeneratorTest.java index 59407ff..788c68b 100644 --- a/src/test/java/com/fasterxml/uuid/UUIDGeneratorTest.java +++ b/src/test/java/com/fasterxml/uuid/UUIDGeneratorTest.java @@ -32,6 +32,7 @@ import com.fasterxml.uuid.impl.NameBasedGenerator; import com.fasterxml.uuid.impl.RandomBasedGenerator; import com.fasterxml.uuid.impl.TimeBasedEpochGenerator; +import com.fasterxml.uuid.impl.TimeBasedEpochRandomGenerator; import com.fasterxml.uuid.impl.TimeBasedReorderedGenerator; import com.fasterxml.uuid.impl.TimeBasedGenerator; @@ -119,7 +120,8 @@ public void testGenerateRandomBasedUUID() // we need a instance to use RandomBasedGenerator uuid_gen = Generators.randomBasedGenerator(); - + assertEquals(uuid_gen.getType(), UUIDType.RANDOM_BASED); + // for the random UUID generator, we will generate a bunch of // random UUIDs UUID uuid_array[] = new UUID[SIZE_OF_TEST_ARRAY]; @@ -153,7 +155,8 @@ public void testGenerateTimeBasedUUID() // we need a instance to use TimeBasedGenerator uuid_gen = Generators.timeBasedGenerator(); - + assertEquals(uuid_gen.getType(), UUIDType.TIME_BASED); + // first check that given a number of calls to generateTimeBasedUUID, // all returned UUIDs order after the last returned UUID // we'll check this by generating the UUIDs into one array and sorting @@ -202,7 +205,8 @@ public void testGenerateTimeBasedUUIDWithEthernetAddress() // we need a instance to use TimeBasedGenerator uuid_gen = Generators.timeBasedGenerator(ethernet_address); - + assertEquals(uuid_gen.getType(), UUIDType.TIME_BASED); + // check that given a number of calls to generateTimeBasedUUID, // all returned UUIDs order after the last returned UUID // we'll check this by generating the UUIDs into one array and sorting @@ -261,6 +265,7 @@ public void testGenerateTimeBasedEpochUUID() throws Exception // we need a instance to use TimeBasedEpochGenerator uuid_gen = Generators.timeBasedEpochGenerator(entropy); + assertEquals(uuid_gen.getType(), UUIDType.TIME_BASED_EPOCH); // first check that given a number of calls to generateTimeBasedEpochUUID, // all returned UUIDs order after the last returned UUID @@ -299,6 +304,58 @@ public void testGenerateTimeBasedEpochUUID() throws Exception checkUUIDArrayForCorrectCreationTimeEpoch(uuid_array, start_time, end_time); } + /** + * Test of generateTimeBasedEpochRandomUUID() method, + * of class com.fasterxml.uuid.UUIDGenerator. + */ + public void testGenerateTimeBasedEpochRandomUUID() throws Exception + { + // this test will attempt to check for reasonable behavior of the + // generateTimeBasedRandomUUID method + + Random entropy = new Random(0x666); + + // we need a instance to use + TimeBasedEpochRandomGenerator uuid_gen = Generators.timeBasedEpochRandomGenerator(entropy); + + assertEquals(uuid_gen.getType(), UUIDType.TIME_BASED_EPOCH); + // first check that given a number of calls to generateTimeBasedEpochUUID, + // all returned UUIDs order after the last returned UUID + // we'll check this by generating the UUIDs into one array and sorting + // then in another and checking the order of the two match + // change the number in the array statement if you want more or less + // UUIDs to be generated and tested + UUID uuid_array[] = new UUID[SIZE_OF_TEST_ARRAY]; + + // before we generate all the uuids, lets get the start time + long start_time = System.currentTimeMillis(); + Thread.sleep(2); // Clean start time + + // now create the array of uuids + for (int i = 0; i < uuid_array.length; i++) { + uuid_array[i] = uuid_gen.generate(); + } + + // now capture the end time + long end_time = System.currentTimeMillis(); + Thread.sleep(2); // Clean end time + + // check that none of the UUIDs are null + checkUUIDArrayForNonNullUUIDs(uuid_array); + + // check that all the uuids were correct variant and version (type-1) + checkUUIDArrayForCorrectVariantAndVersion(uuid_array, UUIDType.TIME_BASED_EPOCH); + + // check that all uuids were unique + // NOTE: technically, this test 'could' fail, but statistically + // speaking it should be extremely unlikely unless the implementation + // of (Secure)Random is bad + checkUUIDArrayForUniqueness(uuid_array); + + // check that all uuids have timestamps between the start and end time + checkUUIDArrayForCorrectCreationTimeEpoch(uuid_array, start_time, end_time); + } + // [#70]: allow use of custom UUIDClock public void testGenerateTimeBasedEpochUUIDWithFixedClock() throws Exception { @@ -342,7 +399,8 @@ public void testGenerateNameBasedUUIDNameSpaceAndName() // we need a instance to use NameBasedGenerator uuid_gen = Generators.nameBasedGenerator(NameBasedGenerator.NAMESPACE_URL); - + assertEquals(uuid_gen.getType(), UUIDType.NAME_BASED_SHA1); + UUID uuid_array[] = new UUID[SIZE_OF_TEST_ARRAY]; // now create the array of uuids @@ -426,6 +484,7 @@ public void testGenerateNameBasedUUIDNameSpaceNameAndMessageDigest() // generateNameBasedUUID method NameBasedGenerator uuid_gen = Generators.nameBasedGenerator(NameBasedGenerator.NAMESPACE_URL, MESSAGE_DIGEST); + assertEquals(uuid_gen.getType(), UUIDType.NAME_BASED_MD5); UUID uuid_array[] = new UUID[SIZE_OF_TEST_ARRAY]; // now create the array of uuids @@ -502,7 +561,8 @@ public void testGenerateTimeBasedReorderedUUID() // we need a instance to use TimeBasedReorderedGenerator uuid_gen = Generators.timeBasedReorderedGenerator(); - + assertEquals(uuid_gen.getType(), UUIDType.TIME_BASED_REORDERED); + // first check that given a number of calls to generateTimeBasedReorderedUUID, // all returned UUIDs order after the last returned UUID // we'll check this by generating the UUIDs into one array and sorting @@ -551,7 +611,8 @@ public void testGenerateTimeBasedReorderedUUIDWithEthernetAddress() // we need a instance to use TimeBasedReorderedGenerator uuid_gen = Generators.timeBasedReorderedGenerator(ethernet_address); - + assertEquals(uuid_gen.getType(), UUIDType.TIME_BASED_REORDERED); + // check that given a number of calls to generateTimeBasedReorderedUUID, // all returned UUIDs order after the last returned UUID // we'll check this by generating the UUIDs into one array and sorting From 4ceeddbb4f0e49ca1cfb19df6e7b791020c4a570 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 20 Feb 2024 20:44:57 -0800 Subject: [PATCH 13/58] Update README with latest changes --- README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e84d56b..d99c525 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ UUID uuid = Generators.nameBasedgenerator().generate("string to hash"); // Versi // With JUG 4.1+: support for https://github.com/uuid6/uuid6-ietf-draft versions 6 and 7: UUID uuid = Generators.timeBasedReorderedGenerator().generate(); // Version 6 UUID uuid = Generators.timeBasedEpochGenerator().generate(); // Version 7 +// With JUG 5.0 added variation: UUID uuid = Generators.timeBasedEpochRandomGenerator().generate(); // Version 7 with per-call random values ``` @@ -136,31 +137,33 @@ it is rather slower than JUG version: for more information, read JUG jar built under `target/`: ``` -target/java-uuid-generator-4.1.2-SNAPSHOT.jar +target/java-uuid-generator-5.0.0-SNAPSHOT.jar ``` can also be used as a simple Command-line UUID generation tool. To see usage you can do something like: - java -jar target/java-uuid-generator-4.1.2-SNAPSHOT.jar + java -jar target/java-uuid-generator-5.0.0-SNAPSHOT.jar and get full instructions, but to generate 5 Random-based UUIDs, you would use: - java -jar target/java-uuid-generator-4.1.2-SNAPSHOT.jar -c 5 r + java -jar target/java-uuid-generator-5.0.0-SNAPSHOT.jar -c 5 r (where `-c` (or `--count`) means number of UUIDs to generate, and `r` means Random-based version) NOTE: this functionality is included as of JUG 4.1 -- with earlier versions you would need a bit longer invocation as Jar metadata did not specify "Main-Class". If so, you would need to use - java -cp target/java-uuid-generator-4.1.2-SNAPSHOT.jar com.fasterxml.uuid.Jug -c 5 r + java -cp target/java-uuid-generator-5.0.0-SNAPSHOT.jar com.fasterxml.uuid.Jug -c 5 r ## Compatibility JUG versions 3.1 and later require JDK 1.6 to work, mostly to be able to access local Ethernet MAC address. Earlier versions (3.0 and before) worked on 1.4 (which introduced `java.util.UUID`). +JUG versions 5.0 and later require JDK 8 to work. + ## Known Issues JDK's `java.util.UUID` has flawed implementation of `compareTo()`, which uses naive comparison From 5b05b646924e293d19de2915127f26ba5e9879ed Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Thu, 22 Feb 2024 18:41:35 -0800 Subject: [PATCH 14/58] Minor dead code elimination (from #96) --- .../com/fasterxml/uuid/impl/RandomBasedGenerator.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/main/java/com/fasterxml/uuid/impl/RandomBasedGenerator.java b/src/main/java/com/fasterxml/uuid/impl/RandomBasedGenerator.java index ecdfe5d..241b2ed 100644 --- a/src/main/java/com/fasterxml/uuid/impl/RandomBasedGenerator.java +++ b/src/main/java/com/fasterxml/uuid/impl/RandomBasedGenerator.java @@ -23,12 +23,6 @@ */ public class RandomBasedGenerator extends NoArgGenerator { - /** - * Default shared random number generator, used if no random number generator - * is explicitly specified for instance - */ - protected static Random _sharedRandom = null; - /** * Random number generator that this generator uses. */ @@ -51,10 +45,8 @@ public RandomBasedGenerator(Random rnd) { if (rnd == null) { rnd = LazyRandom.sharedSecureRandom(); - _secureRandom = true; - } else { - _secureRandom = (rnd instanceof SecureRandom); } + _secureRandom = (rnd instanceof SecureRandom); _random = rnd; } From 967baf4020c99e9a09a9b5a64aafb34772daa38c Mon Sep 17 00:00:00 2001 From: Pavel Date: Fri, 23 Feb 2024 05:43:33 +0300 Subject: [PATCH 15/58] #87 Add some tests and code simplicity (#96) --- .../com/fasterxml/uuid/EthernetAddress.java | 2 +- .../java/com/fasterxml/uuid/Generators.java | 2 +- .../java/com/fasterxml/uuid/UUIDClock.java | 4 +- .../java/com/fasterxml/uuid/UUIDTimer.java | 2 +- .../com/fasterxml/uuid/impl/LazyRandom.java | 3 + .../com/fasterxml/uuid/UUIDGeneratorTest.java | 180 ++++++++++++++++++ 6 files changed, 188 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/fasterxml/uuid/EthernetAddress.java b/src/main/java/com/fasterxml/uuid/EthernetAddress.java index 8e61c17..f3222d5 100644 --- a/src/main/java/com/fasterxml/uuid/EthernetAddress.java +++ b/src/main/java/com/fasterxml/uuid/EthernetAddress.java @@ -33,7 +33,7 @@ public class EthernetAddress { private static final long serialVersionUID = 1L; - private final static char[] HEX_CHARS = "0123456789abcdefABCDEF".toCharArray(); + private static final char[] HEX_CHARS = "0123456789abcdefABCDEF".toCharArray(); /** * We may need a random number generator, for creating dummy ethernet diff --git a/src/main/java/com/fasterxml/uuid/Generators.java b/src/main/java/com/fasterxml/uuid/Generators.java index 9a17506..8f63bf4 100644 --- a/src/main/java/com/fasterxml/uuid/Generators.java +++ b/src/main/java/com/fasterxml/uuid/Generators.java @@ -74,7 +74,7 @@ public static RandomBasedGenerator randomBasedGenerator(Random rnd) { * Factory method for constructing UUID generator that uses specified * random number generator for constructing UUIDs according to standard * method number 5, but without using a namespace. - * Digester to use will be SHA-1 as recommened by UUID spec. + * Digester to use will be SHA-1 as recommended by UUID spec. */ public static NameBasedGenerator nameBasedGenerator() { return nameBasedGenerator(null); diff --git a/src/main/java/com/fasterxml/uuid/UUIDClock.java b/src/main/java/com/fasterxml/uuid/UUIDClock.java index ea48b6d..28493c6 100644 --- a/src/main/java/com/fasterxml/uuid/UUIDClock.java +++ b/src/main/java/com/fasterxml/uuid/UUIDClock.java @@ -26,12 +26,12 @@ */ public class UUIDClock { - private final static UUIDClock DEFAULT = new UUIDClock(); + private static final UUIDClock DEFAULT = new UUIDClock(); /** * @since 4.3 */ - public final static UUIDClock systemTimeClock() { + public static UUIDClock systemTimeClock() { return DEFAULT; } diff --git a/src/main/java/com/fasterxml/uuid/UUIDTimer.java b/src/main/java/com/fasterxml/uuid/UUIDTimer.java index 789bcf4..3822c4b 100644 --- a/src/main/java/com/fasterxml/uuid/UUIDTimer.java +++ b/src/main/java/com/fasterxml/uuid/UUIDTimer.java @@ -358,7 +358,7 @@ protected final void getAndSetTimestamp(byte[] uuidBytes) /********************************************************************** */ - private final static int MAX_WAIT_COUNT = 50; + private static final int MAX_WAIT_COUNT = 50; /** * Simple utility method to use to wait for couple of milliseconds, diff --git a/src/main/java/com/fasterxml/uuid/impl/LazyRandom.java b/src/main/java/com/fasterxml/uuid/impl/LazyRandom.java index b9424d7..40a8ba2 100644 --- a/src/main/java/com/fasterxml/uuid/impl/LazyRandom.java +++ b/src/main/java/com/fasterxml/uuid/impl/LazyRandom.java @@ -16,6 +16,9 @@ public final class LazyRandom private static volatile SecureRandom shared; public static SecureRandom sharedSecureRandom() { + if (shared != null) { + return shared; + } synchronized (lock) { SecureRandom result = shared; if (result == null) { diff --git a/src/test/java/com/fasterxml/uuid/UUIDGeneratorTest.java b/src/test/java/com/fasterxml/uuid/UUIDGeneratorTest.java index 788c68b..1b86416 100644 --- a/src/test/java/com/fasterxml/uuid/UUIDGeneratorTest.java +++ b/src/test/java/com/fasterxml/uuid/UUIDGeneratorTest.java @@ -304,6 +304,58 @@ public void testGenerateTimeBasedEpochUUID() throws Exception checkUUIDArrayForCorrectCreationTimeEpoch(uuid_array, start_time, end_time); } + /** + * Test of generateTimeBasedEpochUUID() method with UUIDClock instance, + * of class com.fasterxml.uuid.UUIDGenerator. + */ + public void testGenerateTimeBasedEpochUUIDWithUUIDClock() throws Exception + { + // this test will attempt to check for reasonable behavior of the + // generateTimeBasedUUID method + + Random entropy = new Random(0x666); + + // we need a instance to use + TimeBasedEpochGenerator uuid_gen = Generators.timeBasedEpochGenerator(null, UUIDClock.systemTimeClock()); + assertEquals(uuid_gen.getType(), UUIDType.TIME_BASED_EPOCH); + + // first check that given a number of calls to generateTimeBasedEpochUUID, + // all returned UUIDs order after the last returned UUID + // we'll check this by generating the UUIDs into one array and sorting + // then in another and checking the order of the two match + // change the number in the array statement if you want more or less + // UUIDs to be generated and tested + UUID uuid_array[] = new UUID[SIZE_OF_TEST_ARRAY]; + + // before we generate all the uuids, lets get the start time + long start_time = System.currentTimeMillis(); + Thread.sleep(2); // Clean start time + + // now create the array of uuids + for (int i = 0; i < uuid_array.length; i++) { + uuid_array[i] = uuid_gen.generate(); + } + + // now capture the end time + long end_time = System.currentTimeMillis(); + Thread.sleep(2); // Clean end time + + // check that none of the UUIDs are null + checkUUIDArrayForNonNullUUIDs(uuid_array); + + // check that all the uuids were correct variant and version (type-1) + checkUUIDArrayForCorrectVariantAndVersion(uuid_array, UUIDType.TIME_BASED_EPOCH); + + // check that all the uuids were generated with correct order + checkUUIDArrayForCorrectOrdering(uuid_array); + + // check that all uuids were unique + checkUUIDArrayForUniqueness(uuid_array); + + // check that all uuids have timestamps between the start and end time + checkUUIDArrayForCorrectCreationTimeEpoch(uuid_array, start_time, end_time); + } + /** * Test of generateTimeBasedEpochRandomUUID() method, * of class com.fasterxml.uuid.UUIDGenerator. @@ -356,6 +408,58 @@ public void testGenerateTimeBasedEpochRandomUUID() throws Exception checkUUIDArrayForCorrectCreationTimeEpoch(uuid_array, start_time, end_time); } + /** + * Test of generateTimeBasedEpochRandomUUID() method with UUIDClock instance, + * of class com.fasterxml.uuid.UUIDGenerator. + */ + public void testGenerateTimeBasedEpochRandomUUIDWithUUIDClock() throws Exception + { + // this test will attempt to check for reasonable behavior of the + // generateTimeBasedRandomUUID method + + Random entropy = new Random(0x666); + + // we need a instance to use + TimeBasedEpochRandomGenerator uuid_gen = Generators.timeBasedEpochRandomGenerator(null, UUIDClock.systemTimeClock()); + + assertEquals(uuid_gen.getType(), UUIDType.TIME_BASED_EPOCH); + // first check that given a number of calls to generateTimeBasedEpochUUID, + // all returned UUIDs order after the last returned UUID + // we'll check this by generating the UUIDs into one array and sorting + // then in another and checking the order of the two match + // change the number in the array statement if you want more or less + // UUIDs to be generated and tested + UUID uuid_array[] = new UUID[SIZE_OF_TEST_ARRAY]; + + // before we generate all the uuids, lets get the start time + long start_time = System.currentTimeMillis(); + Thread.sleep(2); // Clean start time + + // now create the array of uuids + for (int i = 0; i < uuid_array.length; i++) { + uuid_array[i] = uuid_gen.generate(); + } + + // now capture the end time + long end_time = System.currentTimeMillis(); + Thread.sleep(2); // Clean end time + + // check that none of the UUIDs are null + checkUUIDArrayForNonNullUUIDs(uuid_array); + + // check that all the uuids were correct variant and version (type-1) + checkUUIDArrayForCorrectVariantAndVersion(uuid_array, UUIDType.TIME_BASED_EPOCH); + + // check that all uuids were unique + // NOTE: technically, this test 'could' fail, but statistically + // speaking it should be extremely unlikely unless the implementation + // of (Secure)Random is bad + checkUUIDArrayForUniqueness(uuid_array); + + // check that all uuids have timestamps between the start and end time + checkUUIDArrayForCorrectCreationTimeEpoch(uuid_array, start_time, end_time); + } + // [#70]: allow use of custom UUIDClock public void testGenerateTimeBasedEpochUUIDWithFixedClock() throws Exception { @@ -550,6 +654,82 @@ public void testGenerateNameBasedUUIDNameSpaceNameAndMessageDigest() Arrays.equals(uuid_array, uuid_array2)); } + /** + * Test of generateNameBasedUUID() + * method, of class com.fasterxml.uuid.UUIDGenerator. + */ + public void testGenerateNameBasedUUIDWithDefaults() + { + // this test will attempt to check for reasonable behavior of the + // generateNameBasedUUID method + + NameBasedGenerator uuid_gen = Generators.nameBasedGenerator(); + assertEquals(uuid_gen.getType(), UUIDType.NAME_BASED_SHA1); + UUID uuid_array[] = new UUID[SIZE_OF_TEST_ARRAY]; + + // now create the array of uuids + for (int i = 0; i < uuid_array.length; i++) + { + uuid_array[i] = uuid_gen.generate("test name"+i); + } + + // check that none of the UUIDs are null + checkUUIDArrayForNonNullUUIDs(uuid_array); + + // check that all the uuids were correct variant and version + checkUUIDArrayForCorrectVariantAndVersion(uuid_array, UUIDType.NAME_BASED_SHA1); + + // check that all uuids were unique + checkUUIDArrayForUniqueness(uuid_array); + + // now create the array of uuids + for (int i = 0; i < uuid_array.length; i++) + { + uuid_array[i] = uuid_gen.generate("test name" + i); + } + + // check that none of the UUIDs are null + checkUUIDArrayForNonNullUUIDs(uuid_array); + + // check that all the uuids were correct variant and version + checkUUIDArrayForCorrectVariantAndVersion(uuid_array, UUIDType.NAME_BASED_SHA1); + + // check that all uuids were unique + checkUUIDArrayForUniqueness(uuid_array); + + // now, lets make sure generating two sets of name based uuid with the + // same args always gives the same result + uuid_array = new UUID[SIZE_OF_TEST_ARRAY]; + + // now create the array of uuids + for (int i = 0; i < uuid_array.length; i++) { + uuid_array[i] = uuid_gen.generate("test name" + i); + } + + UUID uuid_array2[] = new UUID[SIZE_OF_TEST_ARRAY]; + + // now create the array of uuids + for (int i = 0; i < uuid_array2.length; i++) { + uuid_array2[i] = uuid_gen.generate("test name" + i); + } + + // check that none of the UUIDs are null + checkUUIDArrayForNonNullUUIDs(uuid_array); + checkUUIDArrayForNonNullUUIDs(uuid_array2); + + // check that all the uuids were correct variant and version + checkUUIDArrayForCorrectVariantAndVersion(uuid_array, UUIDType.NAME_BASED_SHA1); + checkUUIDArrayForCorrectVariantAndVersion(uuid_array2, UUIDType.NAME_BASED_SHA1); + + // check that all uuids were unique + checkUUIDArrayForUniqueness(uuid_array); + checkUUIDArrayForUniqueness(uuid_array2); + + // check that both arrays are equal to one another + assertTrue("expected both arrays to be equal, they were not!", + Arrays.equals(uuid_array, uuid_array2)); + } + /** * Test of generateTimeBasedReorderedUUID() method, * of class com.fasterxml.uuid.UUIDGenerator. From e7a15a75573cbaaf3aae229508eb32e4525a69a4 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 23 Feb 2024 16:55:34 -0800 Subject: [PATCH 16/58] Prepare for 5.0.0 release --- pom.xml | 2 +- release-notes/VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 97af4ac..54212a5 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.fasterxml oss-parent - 55 + 58 com.fasterxml.uuid java-uuid-generator diff --git a/release-notes/VERSION b/release-notes/VERSION index d7db8cd..89cf95d 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -4,7 +4,7 @@ Project: java-uuid-generator Releases ============================================================================ -5.0.0 (not yet released) +5.0.0 (23-Feb-2024) #53: Increase JDK baseline to JDK 8 #81: Add `UUIDUtil.extractTimestamp()` for extracting 64-bit timestamp for From 6b9e70de9505c5d9c9dfd44433afa150f9aab11b Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 23 Feb 2024 17:04:06 -0800 Subject: [PATCH 17/58] Remove source plugin definition as that causes issues for release --- pom.xml | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index 54212a5..277a0a7 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ JUG supports 3 original official UUID generation methods as well as later additi scm:git:git://github.com/cowtowncoder/java-uuid-generator.git https://github.com/cowtowncoder/java-uuid-generator scm:git:git@github.com:cowtowncoder/java-uuid-generator.git - HEAD + java-uuid-generator-5.0.0 @@ -45,7 +45,7 @@ JUG supports 3 original official UUID generation methods as well as later additi UTF-8 1.7.36 - 2023-09-13T00:45:46Z + 2024-02-24T00:57:26Z @@ -95,18 +95,6 @@ JUG supports 3 original official UUID generation methods as well as later additi 1.8 - - org.apache.maven.plugins - maven-source-plugin - - - attach-sources - - jar - - - - From b39f516bc3ded8a1dc47964a720e3a936aee0180 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 23 Feb 2024 17:04:46 -0800 Subject: [PATCH 18/58] [maven-release-plugin] prepare release java-uuid-generator-5.0.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 277a0a7..ab3a756 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ --> bundle Java UUID Generator - 5.0.0-SNAPSHOT + 5.0.0 Java UUID Generator (JUG) is a Java library for generating Universally Unique IDentifiers, UUIDs (see http://en.wikipedia.org/wiki/UUID). @@ -45,7 +45,7 @@ JUG supports 3 original official UUID generation methods as well as later additi UTF-8 1.7.36 - 2024-02-24T00:57:26Z + 2024-02-24T01:04:32Z From 83eec70753a0a31c44d44bb593fffb213e21b051 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 23 Feb 2024 17:04:48 -0800 Subject: [PATCH 19/58] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ab3a756..f9a734c 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ --> bundle Java UUID Generator - 5.0.0 + 5.0.1-SNAPSHOT Java UUID Generator (JUG) is a Java library for generating Universally Unique IDentifiers, UUIDs (see http://en.wikipedia.org/wiki/UUID). @@ -45,7 +45,7 @@ JUG supports 3 original official UUID generation methods as well as later additi UTF-8 1.7.36 - 2024-02-24T01:04:32Z + 2024-02-24T01:04:48Z From 66644e56aef1957033ea8c1a2853924f43f12b75 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 23 Feb 2024 17:09:18 -0800 Subject: [PATCH 20/58] update README wrt 5.0.0 release --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d99c525..abe8c70 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Maven coordinates are: com.fasterxml.uuid java-uuid-generator - 4.3.0 + 5.0.0 ``` From 203fe3682d408b73d2b29eb8539565ecbccbb8fa Mon Sep 17 00:00:00 2001 From: Pavel Date: Mon, 26 Feb 2024 06:47:29 +0300 Subject: [PATCH 21/58] some credits (#97) --- release-notes/CREDITS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/release-notes/CREDITS b/release-notes/CREDITS index e4315fc..fe14ab6 100644 --- a/release-notes/CREDITS +++ b/release-notes/CREDITS @@ -134,4 +134,7 @@ Paul Galbraith (pgalbraith@github) Maia Everett (Maia-Everett@github) * Contributed #85: Fix `LazyRandom` for native code generation tools +Pavel Raev (magdel@github) + * Contributed #81: Add UUIDUtil.extractTimestamp() for extracting 64-bit timestamp for all timestamp-based versions + * Contributed Add alternate version to UUIDv7 generator that uses random values on every call (not just for different timestamp) [5.0.0] From a33533221c5b2516ab12f8c148be69f4f73232de Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Sun, 25 Feb 2024 19:49:15 -0800 Subject: [PATCH 22/58] ... --- release-notes/CREDITS | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/release-notes/CREDITS b/release-notes/CREDITS index fe14ab6..0cec5aa 100644 --- a/release-notes/CREDITS +++ b/release-notes/CREDITS @@ -132,9 +132,14 @@ Paul Galbraith (pgalbraith@github) interface address for time/based UUIDs [4.2.0] +Pavel Raev (magdel@github) + * Contributed #81: Add UUIDUtil.extractTimestamp() for extracting 64-bit + timestamp for all timestamp-based versions + [5.0.0] + * Contributed #94 Add alternate version to UUIDv7 generator that uses random + values on every call (not just for different timestamp) + [5.0.0] + Maia Everett (Maia-Everett@github) * Contributed #85: Fix `LazyRandom` for native code generation tools -Pavel Raev (magdel@github) - * Contributed #81: Add UUIDUtil.extractTimestamp() for extracting 64-bit timestamp for all timestamp-based versions - * Contributed Add alternate version to UUIDv7 generator that uses random values on every call (not just for different timestamp) - [5.0.0] + [5.0.0] From b9c6190b8fddcc2e8c82dd85bf0fa3aeef2f7320 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 14:05:16 -0800 Subject: [PATCH 23/58] Bump the github-actions group with 1 update (#98) --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 91c9d5d..d7a0a38 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -38,7 +38,7 @@ jobs: run: ./mvnw -B -q -ff -ntp test - name: Publish code coverage if: github.event_name != 'pull_request' && matrix.java_version == '8' - uses: codecov/codecov-action@e0b68c6749509c5f83f984dd99a76a1c1a231044 # v4.0.1 + uses: codecov/codecov-action@54bcd8715eee62d40e33596ef5e8f0f48dbbccab # v4.1.0 with: token: ${{ secrets.CODECOV_TOKEN }} file: ./target/site/jacoco/jacoco.xml From 1dd4f2c74f93f9ec9c404993680eae19add1850a Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 27 Feb 2024 18:21:02 -0800 Subject: [PATCH 24/58] Update pom to 5.1.0-SNAPSHOT --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f9a734c..db3f7dc 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ --> bundle Java UUID Generator - 5.0.1-SNAPSHOT + 5.1.0-SNAPSHOT Java UUID Generator (JUG) is a Java library for generating Universally Unique IDentifiers, UUIDs (see http://en.wikipedia.org/wiki/UUID). @@ -28,7 +28,7 @@ JUG supports 3 original official UUID generation methods as well as later additi scm:git:git://github.com/cowtowncoder/java-uuid-generator.git https://github.com/cowtowncoder/java-uuid-generator scm:git:git@github.com:cowtowncoder/java-uuid-generator.git - java-uuid-generator-5.0.0 + HEAD From aa73bd3fa1c08c896686a81c77f54949f90df905 Mon Sep 17 00:00:00 2001 From: Daniel Albuquerque <1187103+worldtiki@users.noreply.github.com> Date: Wed, 28 Feb 2024 02:29:18 +0000 Subject: [PATCH 25/58] New factory method to create TimeBasedEpochRandomGenerator (#99) --- release-notes/CREDITS | 5 ++++ release-notes/VERSION | 5 ++++ .../java/com/fasterxml/uuid/Generators.java | 23 +++++++++++++++++++ .../com/fasterxml/uuid/impl/UUIDUtilTest.java | 10 ++++++++ 4 files changed, 43 insertions(+) diff --git a/release-notes/CREDITS b/release-notes/CREDITS index 0cec5aa..e55939e 100644 --- a/release-notes/CREDITS +++ b/release-notes/CREDITS @@ -143,3 +143,8 @@ Pavel Raev (magdel@github) Maia Everett (Maia-Everett@github) * Contributed #85: Fix `LazyRandom` for native code generation tools [5.0.0] + +Daniel Albuquerque (worldtiki@github) + * Contributed #99: New factory method to create TimeBasedEpochRandomGenerator + [5.1.0] + diff --git a/release-notes/VERSION b/release-notes/VERSION index 89cf95d..94cd989 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -4,6 +4,11 @@ Project: java-uuid-generator Releases ============================================================================ +5.1.0 (not yet released) + +#99: New factory method to create TimeBasedEpochRandomGenerator + (contributed by Daniel A) + 5.0.0 (23-Feb-2024) #53: Increase JDK baseline to JDK 8 diff --git a/src/main/java/com/fasterxml/uuid/Generators.java b/src/main/java/com/fasterxml/uuid/Generators.java index 8f63bf4..8f2d976 100644 --- a/src/main/java/com/fasterxml/uuid/Generators.java +++ b/src/main/java/com/fasterxml/uuid/Generators.java @@ -126,6 +126,11 @@ public static NameBasedGenerator nameBasedGenerator(UUID namespace, MessageDiges /** * Factory method for constructing UUID generator that generates UUID using * version 7 (Unix Epoch time+random based). + *

+ * NOTE: calls within same millisecond produce very similar values; this may be + * unsafe in some environments. + *

+ * No additional external synchronization is used. */ public static TimeBasedEpochGenerator timeBasedEpochGenerator() { @@ -166,6 +171,24 @@ public static TimeBasedEpochGenerator timeBasedEpochGenerator(Random random, return new TimeBasedEpochGenerator(random, clock); } + // // Epoch Time+random generation + + /** + * Factory method for constructing UUID generator that generates UUID using + * version 7 (Unix Epoch time+random based). + *

+ * Calls within same millisecond use additional per-call randomness to try to create + * more distinct values, compared to {@link #timeBasedEpochGenerator(Random)} + *

+ * No additional external synchronization is used. + * + * @since 5.1 + */ + public static TimeBasedEpochRandomGenerator timeBasedEpochRandomGenerator() + { + return timeBasedEpochRandomGenerator(null); + } + /** * Factory method for constructing UUID generator that generates UUID using * version 7 (Unix Epoch time+random based), using specified {@link Random} diff --git a/src/test/java/com/fasterxml/uuid/impl/UUIDUtilTest.java b/src/test/java/com/fasterxml/uuid/impl/UUIDUtilTest.java index bcf972f..9186e74 100644 --- a/src/test/java/com/fasterxml/uuid/impl/UUIDUtilTest.java +++ b/src/test/java/com/fasterxml/uuid/impl/UUIDUtilTest.java @@ -61,6 +61,16 @@ public void testExtractTimestampUUIDEpochBased() { } } + public void testExtractTimestampUUIDEpochRandomBased() { + TimeBasedEpochRandomGenerator generator = Generators.timeBasedEpochRandomGenerator(); + final Random rnd = new Random(3); + for (int i = 0; i < TEST_REPS; i++) { + long rawTimestamp = rnd.nextLong() >>> 16; + UUID uuid = generator.construct(rawTimestamp); + assertEquals(rawTimestamp, UUIDUtil.extractTimestamp(uuid)); + } + } + public void testExtractTimestampUUIDOnOtherValues() { assertEquals(0L, UUIDUtil.extractTimestamp(null)); assertEquals(0L, UUIDUtil.extractTimestamp(UUID.fromString("00000000-0000-0000-0000-000000000000"))); From 6f44ff58a9ce1383572231cc43a3b99bdc5c21b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 20:57:47 -0800 Subject: [PATCH 26/58] Bump the github-actions group with 1 update (#100) --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d7a0a38..e7aef9a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,7 +26,7 @@ jobs: steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Set up JDK - uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0 + uses: actions/setup-java@9704b39bf258b59bc04b50fa2dd55e9ed76b47a8 # v4.1.0 with: distribution: "temurin" java-version: ${{ matrix.java_version }} From d240125ef11fb3812ef052678f4b9aff23151036 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Thu, 7 Mar 2024 20:33:34 -0800 Subject: [PATCH 27/58] Update README to explicitly indicate support of UUIDv6 and v7 --- README.md | 15 ++++++++++++++- src/main/java/com/fasterxml/uuid/Generators.java | 10 +++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index abe8c70..fe3b069 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,19 @@ In addition, many other individuals have helped fix bugs and implement new featu JUG is licensed under [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0.html). +## Supported UUID versions (1, 3, 4, 5, 6, 7) + +JUG supports both "classic" versions defined in [RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122): + +* `1`: time/location - based +* `3` and `5`: name hash - based +* `4`: random number - based + +and newly (in 2022-2024) proposed (see [uuid6](https://uuid6.github.io/uuid6-ietf-draft/) and [RFC-4122 bis](https://datatracker.ietf.org/doc/draft-ietf-uuidrev-rfc4122bis/)) variants: + +* `6`: reordered variant of version `1` (with lexicographic ordering) +* `7`: Unix-timestamp + random based variant (also with lexicographic ordering) + ## Status | Type | Status | @@ -18,7 +31,7 @@ JUG is licensed under [Apache License 2.0](http://www.apache.org/licenses/LICENS | Artifact | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.fasterxml.uuid/java-uuid-generator/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.fasterxml.uuid/java-uuid-generator/) | | OSS Sponsorship | [![Tidelift](https://tidelift.com/badges/package/maven/com.fasterxml.uuid:java-uuid-generator)](https://tidelift.com/subscription/pkg/maven-com-fasterxml-uuid-java-uuid-generator?utm_source=maven-com-fasterxml-uuid-java-uuid-generator&utm_medium=referral&utm_campaign=readme) | | Javadocs | [![Javadoc](https://javadoc.io/badge/com.fasterxml.uuid/java-uuid-generator.svg)](http://www.javadoc.io/doc/com.fasterxml.uuid/java-uuid-generator) -| Code coverage (4.x) | [![codecov.io](https://codecov.io/github/cowtowncoder/java-uuid-generator/coverage.svg?branch=master)](https://codecov.io/github/cowtowncoder/java-uuid-generator?branch=master) | +| Code coverage (5.x) | [![codecov.io](https://codecov.io/github/cowtowncoder/java-uuid-generator/coverage.svg?branch=master)](https://codecov.io/github/cowtowncoder/java-uuid-generator?branch=master) | | OpenSSF Score | [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/cowtowncoder/java-uuid-generator/badge)](https://securityscorecards.dev/viewer/?uri=github.com/cowtowncoder/java-uuid-generator) | ## Usage diff --git a/src/main/java/com/fasterxml/uuid/Generators.java b/src/main/java/com/fasterxml/uuid/Generators.java index 8f2d976..9ab587a 100644 --- a/src/main/java/com/fasterxml/uuid/Generators.java +++ b/src/main/java/com/fasterxml/uuid/Generators.java @@ -53,7 +53,7 @@ public class Generators /** * Factory method for constructing UUID generator that uses default (shared) * random number generator for constructing UUIDs according to standard - * method number 4. + * version 4. */ public static RandomBasedGenerator randomBasedGenerator() { return randomBasedGenerator(null); @@ -62,7 +62,7 @@ public static RandomBasedGenerator randomBasedGenerator() { /** * Factory method for constructing UUID generator that uses specified * random number generator for constructing UUIDs according to standard - * method number 4. + * version 4. */ public static RandomBasedGenerator randomBasedGenerator(Random rnd) { return new RandomBasedGenerator(rnd); @@ -73,7 +73,7 @@ public static RandomBasedGenerator randomBasedGenerator(Random rnd) { /** * Factory method for constructing UUID generator that uses specified * random number generator for constructing UUIDs according to standard - * method number 5, but without using a namespace. + * version 5, but without using a namespace. * Digester to use will be SHA-1 as recommended by UUID spec. */ public static NameBasedGenerator nameBasedGenerator() { @@ -83,7 +83,7 @@ public static NameBasedGenerator nameBasedGenerator() { /** * Factory method for constructing UUID generator that uses specified * random number generator for constructing UUIDs according to standard - * method number 5, with specified namespace (or without one if null + * version 5, with specified namespace (or without one if null * is specified). * Digester to use will be SHA-1 as recommened by UUID spec. * @@ -98,7 +98,7 @@ public static NameBasedGenerator nameBasedGenerator(UUID namespace) { /** * Factory method for constructing UUID generator that uses specified * random number generator for constructing UUIDs according to standard - * method number 3 or 5, with specified namespace (or without one if null + * version 3 or 5, with specified namespace (or without one if null * is specified), using specified digester. * If digester is passed as null, a SHA-1 digester will be constructed. * From 36839ad4bd6ede42c52e3d624f5529f74ea9db61 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Thu, 7 Mar 2024 20:39:39 -0800 Subject: [PATCH 28/58] ... --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fe3b069..4a31d9b 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ JUG is a set of Java classes for working with UUIDs: generating UUIDs using any of standard methods, outputting efficiently, sorting and so on. It generates UUIDs according to the [UUID specification (RFC-4122)](https://tools.ietf.org/html/rfc4122) -(also see [Wikipedia UUID page](http://en.wikipedia.org/wiki/UUID) for more explanation) +(see [Wikipedia UUID page](http://en.wikipedia.org/wiki/UUID) for more explanation) JUG was written by Tatu Saloranta () originally in 2002 and has been updated over the years. In addition, many other individuals have helped fix bugs and implement new features: please see `release-notes/CREDITS` for the complete list. @@ -12,7 +12,7 @@ JUG is licensed under [Apache License 2.0](http://www.apache.org/licenses/LICENS ## Supported UUID versions (1, 3, 4, 5, 6, 7) -JUG supports both "classic" versions defined in [RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122): +JUG supports both "classic" versions defined in RFC 4122]: * `1`: time/location - based * `3` and `5`: name hash - based From 7724bf1ed211befb79b96308a81b9e09d3399012 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Wed, 20 Mar 2024 18:25:45 -0700 Subject: [PATCH 29/58] Reduce dependabot frequency --- .github/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f6faee6..2390d8c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,7 +3,7 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "weekly" + interval: "monthly" groups: github-actions: patterns: From 0249d2f7cff943a09d87a9a5d3d8835a8247b48c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Mar 2024 20:11:29 -0700 Subject: [PATCH 30/58] Bump the github-actions group with 2 updates (#101) --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e7aef9a..2ad4e84 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,9 +24,9 @@ jobs: env: JAVA_OPTS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - name: Set up JDK - uses: actions/setup-java@9704b39bf258b59bc04b50fa2dd55e9ed76b47a8 # v4.1.0 + uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 with: distribution: "temurin" java-version: ${{ matrix.java_version }} From e29a05efa0458f82837e022cdf329658e2a9b7f1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 15:44:53 -0700 Subject: [PATCH 31/58] Bump the github-actions group with 1 update (#102) --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2ad4e84..d824f6f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -38,7 +38,7 @@ jobs: run: ./mvnw -B -q -ff -ntp test - name: Publish code coverage if: github.event_name != 'pull_request' && matrix.java_version == '8' - uses: codecov/codecov-action@54bcd8715eee62d40e33596ef5e8f0f48dbbccab # v4.1.0 + uses: codecov/codecov-action@c16abc29c95fcf9174b58eb7e1abf4c866893bc8 # v4.1.1 with: token: ${{ secrets.CODECOV_TOKEN }} file: ./target/site/jacoco/jacoco.xml From 4cc4052bd5c21642453b0fe13509e29a8636f68c Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Wed, 1 May 2024 03:38:18 +0900 Subject: [PATCH 32/58] Delete unnecessary condition (#103) --- .../java/com/fasterxml/uuid/impl/TimeBasedEpochGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/fasterxml/uuid/impl/TimeBasedEpochGenerator.java b/src/main/java/com/fasterxml/uuid/impl/TimeBasedEpochGenerator.java index 0e01195..42157ce 100644 --- a/src/main/java/com/fasterxml/uuid/impl/TimeBasedEpochGenerator.java +++ b/src/main/java/com/fasterxml/uuid/impl/TimeBasedEpochGenerator.java @@ -128,7 +128,7 @@ public UUID construct(long rawTimestamp) if (c) { byte temp = _lastEntropy[i]; temp = (byte) (temp + 0x01); - c = _lastEntropy[i] == (byte) 0xff && c; + c = _lastEntropy[i] == (byte) 0xff; _lastEntropy[i] = temp; } } From f59ec83a984a16898cb7b2d426392b5ed4e7d4d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 16:03:53 -0700 Subject: [PATCH 33/58] Bump the github-actions group with 2 updates (#104) --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d824f6f..9dde6b1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,7 +24,7 @@ jobs: env: JAVA_OPTS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Set up JDK uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 with: @@ -38,7 +38,7 @@ jobs: run: ./mvnw -B -q -ff -ntp test - name: Publish code coverage if: github.event_name != 'pull_request' && matrix.java_version == '8' - uses: codecov/codecov-action@c16abc29c95fcf9174b58eb7e1abf4c866893bc8 # v4.1.1 + uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f787459979aebb39be # v4.3.1 with: token: ${{ secrets.CODECOV_TOKEN }} file: ./target/site/jacoco/jacoco.xml From 190040a6ac5c16665c77b2ae666a0d6a43c3e4ef Mon Sep 17 00:00:00 2001 From: Pavel Date: Tue, 21 May 2024 06:37:27 +0300 Subject: [PATCH 34/58] #105 UUIDUtil.extractTimestamp() is broken (#106) --- release-notes/VERSION | 2 ++ .../java/com/fasterxml/uuid/UUIDTimer.java | 14 +++++++++ .../com/fasterxml/uuid/impl/UUIDUtil.java | 31 ++++++++++++++----- .../com/fasterxml/uuid/impl/UUIDUtilTest.java | 28 +++++++++++++++-- 4 files changed, 66 insertions(+), 9 deletions(-) diff --git a/release-notes/VERSION b/release-notes/VERSION index 94cd989..c3f7c47 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -8,6 +8,8 @@ Releases #99: New factory method to create TimeBasedEpochRandomGenerator (contributed by Daniel A) +#105: `UUIDUtil.extractTimestamp()` is broken for versions 1 and 6 + (contributed by @magdel) 5.0.0 (23-Feb-2024) diff --git a/src/main/java/com/fasterxml/uuid/UUIDTimer.java b/src/main/java/com/fasterxml/uuid/UUIDTimer.java index 3822c4b..203f934 100644 --- a/src/main/java/com/fasterxml/uuid/UUIDTimer.java +++ b/src/main/java/com/fasterxml/uuid/UUIDTimer.java @@ -320,6 +320,20 @@ public synchronized long getTimestamp() return systime; } + /** + * Converts a UUID v1 or v6 timestamp (where unit is 100 nanoseconds), + * to Unix epoch timestamp (milliseconds since 01-Jan-1970 UTC) + * + * @param timestamp Timestamp used to create UUID versions 1 and 6 + * + * @return Unix epoch timestamp + * + * @since 5.1 + */ + public static long timestampToEpoch(long timestamp) { + return (timestamp - kClockOffset) / kClockMultiplierL; + } + /* /********************************************************************** /* Test-support methods diff --git a/src/main/java/com/fasterxml/uuid/impl/UUIDUtil.java b/src/main/java/com/fasterxml/uuid/impl/UUIDUtil.java index fffea93..b2d5291 100644 --- a/src/main/java/com/fasterxml/uuid/impl/UUIDUtil.java +++ b/src/main/java/com/fasterxml/uuid/impl/UUIDUtil.java @@ -2,6 +2,7 @@ import java.util.UUID; +import com.fasterxml.uuid.UUIDTimer; import com.fasterxml.uuid.UUIDType; public class UUIDUtil @@ -360,7 +361,7 @@ private final static void _checkUUIDByteArray(byte[] bytes, int offset) * * @param uuid uuid timestamp to extract from * - * @return timestamp in milliseconds (since Epoch), or 0 if type does not support timestamps + * @return Unix timestamp in milliseconds (since Epoch), or 0 if type does not support timestamps * * @since 5.0 */ @@ -380,17 +381,25 @@ public static long extractTimestamp(UUID uuid) case NAME_BASED_MD5: return 0L; case TIME_BASED: - return _getTimestampFromUuidV1(uuid); + return UUIDTimer.timestampToEpoch(_getRawTimestampFromUuidV1(uuid)); case TIME_BASED_REORDERED: - return _getTimestampFromUuidV6(uuid); + return UUIDTimer.timestampToEpoch(_getRawTimestampFromUuidV6(uuid)); case TIME_BASED_EPOCH: - return _getTimestampFromUuidV7(uuid); + return _getRawTimestampFromUuidV7(uuid); default: throw new IllegalArgumentException("Invalid `UUID`: unexpected type " + type); } } - private static long _getTimestampFromUuidV1(UUID uuid) { + /** + * Get raw timestamp, used to create the UUID v1 + *

+ * NOTE: no verification is done to ensure UUID given is of version 1. + * + * @param uuid uuid, to extract timestamp from + * @return timestamp, used to create uuid v1 + */ + static long _getRawTimestampFromUuidV1(UUID uuid) { long mostSignificantBits = uuid.getMostSignificantBits(); mostSignificantBits = mostSignificantBits & 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1110_1111_1111_1111L; long low = mostSignificantBits >>> 32; @@ -400,7 +409,15 @@ private static long _getTimestampFromUuidV1(UUID uuid) { return highOfHigher << 48 | lowOfHigher << 32 | low; } - private static long _getTimestampFromUuidV6(UUID uuid) { + /** + * Get raw timestamp, used to create the UUID v6. + *

+ * NOTE: no verification is done to ensure UUID given is of version 6. + * + * @param uuid uuid, to extract timestamp from + * @return timestamp, used to create uuid v6 + */ + static long _getRawTimestampFromUuidV6(UUID uuid) { long mostSignificantBits = uuid.getMostSignificantBits(); mostSignificantBits = mostSignificantBits & 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1001_1111_1111_1111L; long lowL = mostSignificantBits & 0xFFFL; @@ -410,7 +427,7 @@ private static long _getTimestampFromUuidV6(UUID uuid) { return high >>> 4 | lowH << 12 | lowL; } - private static long _getTimestampFromUuidV7(UUID uuid) { + static long _getRawTimestampFromUuidV7(UUID uuid) { long mostSignificantBits = uuid.getMostSignificantBits(); mostSignificantBits = mostSignificantBits & 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1001_1111_1111_1111L; return mostSignificantBits >>> 16; diff --git a/src/test/java/com/fasterxml/uuid/impl/UUIDUtilTest.java b/src/test/java/com/fasterxml/uuid/impl/UUIDUtilTest.java index 9186e74..b730c00 100644 --- a/src/test/java/com/fasterxml/uuid/impl/UUIDUtilTest.java +++ b/src/test/java/com/fasterxml/uuid/impl/UUIDUtilTest.java @@ -4,6 +4,7 @@ import java.util.UUID; import com.fasterxml.uuid.Generators; +import com.fasterxml.uuid.NoArgGenerator; import junit.framework.TestCase; /** @@ -37,20 +38,35 @@ public void testExtractTimestampUUIDTimeBased() { for (int i = 0; i < TEST_REPS; i++) { long rawTimestamp = rnd.nextLong() >>> 4; UUID uuid = generator.construct(rawTimestamp); - assertEquals(rawTimestamp, UUIDUtil.extractTimestamp(uuid)); + assertEquals(rawTimestamp, UUIDUtil._getRawTimestampFromUuidV1(uuid)); } } + public void testExtractTimestampUUIDTimeBasedCurrentTimemillis() { + TimeBasedGenerator generator = Generators.timeBasedGenerator(); + long time = System.currentTimeMillis(); + UUID uuid2 = generator.generate(); + assertEquals(time, UUIDUtil.extractTimestamp(uuid2)); + } + + public void testExtractTimestampUUIDTimeBasedReordered() { TimeBasedReorderedGenerator generator = Generators.timeBasedReorderedGenerator(); final Random rnd = new Random(2); for (int i = 0; i < TEST_REPS; i++) { long rawTimestamp = rnd.nextLong() >>> 4; UUID uuid = generator.construct(rawTimestamp); - assertEquals(rawTimestamp, UUIDUtil.extractTimestamp(uuid)); + assertEquals(rawTimestamp, UUIDUtil._getRawTimestampFromUuidV6(uuid)); } } + public void testExtractTimestampUUIDTimeBasedReorderedCurrentTimeMillis() { + NoArgGenerator generator = Generators.timeBasedReorderedGenerator(); + long time = System.currentTimeMillis(); + UUID uuid = generator.generate(); + assertEquals(time, UUIDUtil.extractTimestamp(uuid)); + } + public void testExtractTimestampUUIDEpochBased() { TimeBasedEpochGenerator generator = Generators.timeBasedEpochGenerator(); final Random rnd = new Random(3); @@ -61,6 +77,14 @@ public void testExtractTimestampUUIDEpochBased() { } } + public void testExtractTimestampUUIDEpochBasedCurrentTimeMillis() { + NoArgGenerator generator = Generators.timeBasedEpochGenerator(); + long time = System.currentTimeMillis(); + UUID uuid = generator.generate(); + assertEquals(time, UUIDUtil.extractTimestamp(uuid)); + } + + public void testExtractTimestampUUIDEpochRandomBased() { TimeBasedEpochRandomGenerator generator = Generators.timeBasedEpochRandomGenerator(); final Random rnd = new Random(3); From befb02113db73f308fb26538b320bfb50d1e1e0f Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Sun, 2 Jun 2024 16:57:36 -0700 Subject: [PATCH 35/58] Prepare for 5.1.0 release --- release-notes/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-notes/VERSION b/release-notes/VERSION index c3f7c47..6e24d51 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -4,7 +4,7 @@ Project: java-uuid-generator Releases ============================================================================ -5.1.0 (not yet released) +5.1.0 (02-Jun-2024) #99: New factory method to create TimeBasedEpochRandomGenerator (contributed by Daniel A) From a8e7c493dc745369426099842503495f98a4fbeb Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Sun, 2 Jun 2024 16:59:27 -0700 Subject: [PATCH 36/58] [maven-release-plugin] prepare release java-uuid-generator-5.1.0 --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index db3f7dc..b18ebff 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ --> bundle Java UUID Generator - 5.1.0-SNAPSHOT + 5.1.0 Java UUID Generator (JUG) is a Java library for generating Universally Unique IDentifiers, UUIDs (see http://en.wikipedia.org/wiki/UUID). @@ -28,7 +28,7 @@ JUG supports 3 original official UUID generation methods as well as later additi scm:git:git://github.com/cowtowncoder/java-uuid-generator.git https://github.com/cowtowncoder/java-uuid-generator scm:git:git@github.com:cowtowncoder/java-uuid-generator.git - HEAD + java-uuid-generator-5.1.0 @@ -45,7 +45,7 @@ JUG supports 3 original official UUID generation methods as well as later additi UTF-8 1.7.36 - 2024-02-24T01:04:48Z + 2024-06-02T23:58:04Z From 4f7cccc18c391e1434b8cc1ded80509e00c5d884 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Sun, 2 Jun 2024 16:59:30 -0700 Subject: [PATCH 37/58] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b18ebff..c22f798 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ --> bundle Java UUID Generator - 5.1.0 + 5.1.1-SNAPSHOT Java UUID Generator (JUG) is a Java library for generating Universally Unique IDentifiers, UUIDs (see http://en.wikipedia.org/wiki/UUID). @@ -45,7 +45,7 @@ JUG supports 3 original official UUID generation methods as well as later additi UTF-8 1.7.36 - 2024-06-02T23:58:04Z + 2024-06-02T23:59:30Z From 76df173151f59eb8ab1daa478c27dd7e8cde4851 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 2 Jun 2024 17:03:34 -0700 Subject: [PATCH 38/58] Bump the github-actions group with 2 updates (#109) --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9dde6b1..ecd2326 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,7 +24,7 @@ jobs: env: JAVA_OPTS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Set up JDK uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 with: @@ -38,7 +38,7 @@ jobs: run: ./mvnw -B -q -ff -ntp test - name: Publish code coverage if: github.event_name != 'pull_request' && matrix.java_version == '8' - uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f787459979aebb39be # v4.3.1 + uses: codecov/codecov-action@125fc84a9a348dbcf27191600683ec096ec9021c # v4.4.1 with: token: ${{ secrets.CODECOV_TOKEN }} file: ./target/site/jacoco/jacoco.xml From 64297d06803a1dd0a780649bfcc3f72e76a3979b Mon Sep 17 00:00:00 2001 From: SquireOfSoftware Date: Fri, 7 Jun 2024 15:49:55 -0700 Subject: [PATCH 39/58] feat(test): cowtowncoder#87 added a new test for LockedFile (#110) --- .../fasterxml/uuid/ext/LockedFileTest.java | 261 ++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 src/test/java/com/fasterxml/uuid/ext/LockedFileTest.java diff --git a/src/test/java/com/fasterxml/uuid/ext/LockedFileTest.java b/src/test/java/com/fasterxml/uuid/ext/LockedFileTest.java new file mode 100644 index 0000000..cb9db2e --- /dev/null +++ b/src/test/java/com/fasterxml/uuid/ext/LockedFileTest.java @@ -0,0 +1,261 @@ +package com.fasterxml.uuid.ext; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; + +import static com.fasterxml.uuid.ext.LockedFile.READ_ERROR; +import static org.junit.Assert.*; + +public class LockedFileTest +{ + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Test + public void constructor_givenNull_shouldThrowNullPointerException() throws IOException { + try { + new LockedFile(null); + fail("This should have thrown a null pointer exception"); + } catch (NullPointerException nullPointerException) { + ; // good + } + } + + @Test + public void constructor_givenEmptyFile_shouldLeaveFileAsIs() throws IOException { + // given + File emptyFile = temporaryFolder.newFile(); + + // when + new LockedFile(emptyFile); + + // then + assertTrue(emptyFile.exists()); + assertTrue(emptyFile.canRead()); + assertTrue(emptyFile.canWrite()); + } + + @Test + public void constructor_givenNonExistentFile_shouldCreateANewFile() throws IOException { + // given + File blankFile = temporaryFolder.newFile(); + File nonExistentFile = new File(blankFile + ".nonexistent"); + + if (Files.exists(nonExistentFile.toPath())) { + fail("temp file should not exist"); + } + + // when + new LockedFile(nonExistentFile); + + // then - the nonexistent file now exists? + assertTrue(Files.exists(nonExistentFile.toPath())); + assertTrue(nonExistentFile.canRead()); + assertTrue(nonExistentFile.canWrite()); + } + + @Test + public void constructor_canOnlyTakeAFile_shouldThrowFileNotFoundException() throws IOException { + // given + File blankFolder = temporaryFolder.newFolder(); + + // when + try { + new LockedFile(blankFolder); + fail("This should not succeed"); + } catch (FileNotFoundException fileNotFoundException) { + // then + assertEquals( + String.format("%s (Is a directory)", blankFolder.getPath()), + fileNotFoundException.getMessage() + ); + } + } + + @Test + public void readStamp_givenEmptyFile_shouldReturnREADERROR() throws IOException { + // given + File emptyFile = temporaryFolder.newFile(); + + // when + LockedFile lockedFile = new LockedFile(emptyFile); + long stamp = lockedFile.readStamp(); + + // then + assertEquals(READ_ERROR, stamp); + } + + @Test + public void readStamp_givenGibberishFile_shouldReturnREADERROR() throws IOException { + // given + File gibberishFile = temporaryFolder.newFile(); + try(FileWriter fileWriter = new FileWriter(gibberishFile)) { + fileWriter.write(UUID.randomUUID().toString().substring(0, 22)); + fileWriter.flush(); + } + + assertEquals(22, Files.size(gibberishFile.toPath())); + + // when + LockedFile lockedFile = new LockedFile(gibberishFile); + long stamp = lockedFile.readStamp(); + + // then + assertEquals(READ_ERROR, stamp); + } + + @Test + public void readStamp_givenTimestampedFile_shouldReturnValueInside() throws IOException { + // given + File timeStampedFile = temporaryFolder.newFile(); + try(FileWriter fileWriter = new FileWriter(timeStampedFile)) { + // we are faking the timestamp format + fileWriter.write("[0x0000000000000001]"); + fileWriter.flush(); + } + + // when + LockedFile lockedFile = new LockedFile(timeStampedFile); + long stamp = lockedFile.readStamp(); + + // then + long expectedTimestamp = 1; + assertEquals(expectedTimestamp, stamp); + } + + // test for overflows + @Test + public void readStamp_givenOverflowedDigitFile_shouldReturnREADERROR() throws IOException { + // given + File timeStampedFile = temporaryFolder.newFile(); + try(FileWriter fileWriter = new FileWriter(timeStampedFile)) { + // we are faking an overflowed timestamp + fileWriter.write("[0x10000000000000000]"); + fileWriter.flush(); + } + + // when + LockedFile lockedFile = new LockedFile(timeStampedFile); + long stamp = lockedFile.readStamp(); + + // then + assertEquals(READ_ERROR, stamp); + } + + @Test + public void readStamp_givenMaxLongFile_shouldReturnLargeTimestamp() throws IOException { + // given + File timeStampedFile = temporaryFolder.newFile(); + try(FileWriter fileWriter = new FileWriter(timeStampedFile)) { + // we are faking an overflowed timestamp + fileWriter.write("[0x7fffffffffffffff]"); + fileWriter.flush(); + } + + // when + LockedFile lockedFile = new LockedFile(timeStampedFile); + long stamp = lockedFile.readStamp(); + + // then + assertEquals(Long.MAX_VALUE, stamp); + } + + @Test + public void writeStamp_givenNegativeTimestamps_shouldThrowIOException() throws IOException { + // given + File timeStampedFile = temporaryFolder.newFile(); + + // when + LockedFile lockedFile = new LockedFile(timeStampedFile); + try { + lockedFile.writeStamp(Long.MIN_VALUE); + fail("This should throw an exception"); + } catch (IOException ioException) { + // then + assertTrue(ioException.getMessage().contains("trying to overwrite existing value")); + assertTrue(ioException.getMessage().contains("with an earlier timestamp")); + } + } + + @Test + public void writeStamp_givenTimestampedFile_withLowerValue_shouldOverrideValue() throws IOException { + // given + String inputValue = "[0x0000000000000000]"; + long numericInputValue = 0L; + long newTimestamp = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE); + + File timeStampedFile = temporaryFolder.newFile(); + try(FileWriter fileWriter = new FileWriter(timeStampedFile)) { + fileWriter.write(inputValue); + fileWriter.flush(); + } + + // when + LockedFile lockedFile = new LockedFile(timeStampedFile); + + lockedFile.writeStamp(newTimestamp); + long stamp = lockedFile.readStamp(); + + // then + assertNotEquals(numericInputValue, stamp); + assertEquals(newTimestamp, stamp); + } + + @Test + public void writeStamp_givenNewerTimestampedFile_writeNegativeTimestamp_shouldThrowException() throws IOException { + // given + String inputValue = "[0x7fffffffffffffff]"; + long newTimestamp = Long.MIN_VALUE; + + File timeStampedFile = temporaryFolder.newFile(); + try(FileWriter fileWriter = new FileWriter(timeStampedFile)) { + fileWriter.write(inputValue); + fileWriter.flush(); + } + + // when + LockedFile lockedFile = new LockedFile(timeStampedFile); + + try { + lockedFile.writeStamp(newTimestamp); + fail("This should throw an exception"); + } catch (IOException ioException) { + // then + assertTrue(ioException.getMessage().contains("trying to overwrite existing value")); + assertTrue(ioException.getMessage().contains("with an earlier timestamp")); + } + } + + @Test + public void writeStamp_givenTimestampedFile_writeSameTimestamp_shouldLeaveFileAlone() throws IOException { + // given + String inputValue = "[0x7fffffffffffffff]"; + long numericInputValue = Long.MAX_VALUE; + long newTimestamp = Long.MAX_VALUE; + + File timeStampedFile = temporaryFolder.newFile(); + try(FileWriter fileWriter = new FileWriter(timeStampedFile)) { + fileWriter.write(inputValue); + fileWriter.flush(); + } + + // when + LockedFile lockedFile = new LockedFile(timeStampedFile); + + lockedFile.writeStamp(newTimestamp); + long stamp = lockedFile.readStamp(); + + // then + assertEquals(numericInputValue, stamp); + assertEquals(newTimestamp, stamp); + } +} From d42f9076d20ec1399e4c4f586f234a970a42aa3e Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 7 Jun 2024 16:21:04 -0700 Subject: [PATCH 40/58] Bit silly but for good code coverage, need to "test" our manually run perf test class too (#112) --- src/main/java/perf/MeasurePerformance.java | 63 ++++++++++++------- .../java/perf/MeasurePerformanceTest.java | 17 +++++ 2 files changed, 56 insertions(+), 24 deletions(-) create mode 100644 src/test/java/perf/MeasurePerformanceTest.java diff --git a/src/main/java/perf/MeasurePerformance.java b/src/main/java/perf/MeasurePerformance.java index 570bc41..71d1928 100644 --- a/src/main/java/perf/MeasurePerformance.java +++ b/src/main/java/perf/MeasurePerformance.java @@ -1,5 +1,6 @@ package perf; +import java.nio.charset.StandardCharsets; import java.util.UUID; import com.fasterxml.uuid.*; @@ -19,20 +20,26 @@ */ public class MeasurePerformance { - // Let's generate quarter million UUIDs per test - - private static final int ROUNDS = 250; - private static final int COUNT = 1000; - + // also: let's just use a single name for name-based, to avoid extra overhead: - final String NAME = "http://www.cowtowncoder.com/blog/blog.html"; - final byte[] NAME_BYTES; + private final static String NAME_STRING = "http://www.cowtowncoder.com/blog/blog.html"; - public MeasurePerformance() throws java.io.IOException - { - NAME_BYTES = NAME.getBytes("UTF-8"); + private final static byte[] NAME_BYTES = NAME_STRING.getBytes(StandardCharsets.UTF_8); + + // Let's generate 50k UUIDs per test round + private static final int COUNT = 1000; + private static final int DEFAULT_ROUNDS = 50; + + private final int rounds; + private final boolean runForever; + + public MeasurePerformance() { this(DEFAULT_ROUNDS, true); } + + public MeasurePerformance(int rounds, boolean runForever) { + this.rounds = rounds; + this.runForever = runForever; } - + public void test() throws Exception { int i = 0; @@ -53,8 +60,11 @@ public void test() throws Exception new com.fasterxml.uuid.ext.FileBasedTimestampSynchronizer()); final StringArgGenerator nameGen = Generators.nameBasedGenerator(namespaceForNamed); - while (true) { - try { Thread.sleep(100L); } catch (InterruptedException ie) { } + boolean running = true; + final long sleepTime = runForever ? 350L : 1L; + + while (running) { + Thread.sleep(sleepTime); int round = (i++ % 7); long curr = System.currentTimeMillis(); @@ -65,44 +75,49 @@ public void test() throws Exception case 0: msg = "JDK, random"; - testJDK(uuids, ROUNDS); + testJDK(uuids, rounds); break; case 1: msg = "JDK, name"; - testJDKNames(uuids, ROUNDS); + testJDKNames(uuids, rounds); break; case 2: msg = "Jug, time-based (non-sync)"; - testTimeBased(uuids, ROUNDS, timeGenPlain); + testTimeBased(uuids, rounds, timeGenPlain); break; case 3: msg = "Jug, time-based (SYNC)"; - testTimeBased(uuids, ROUNDS, timeGenSynced); + testTimeBased(uuids, rounds, timeGenSynced); break; case 4: msg = "Jug, SecureRandom"; - testRandom(uuids, ROUNDS, secureRandomGen); + testRandom(uuids, rounds, secureRandomGen); break; case 5: msg = "Jug, java.util.Random"; - testRandom(uuids, ROUNDS, utilRandomGen); + testRandom(uuids, rounds, utilRandomGen); break; case 6: msg = "Jug, name-based"; - testNameBased(uuids, ROUNDS, nameGen); + testNameBased(uuids, rounds, nameGen); + + // Last one, quit unless running forever + if (!runForever) { + running = false; + } break; /* case 7: msg = "http://johannburkard.de/software/uuid/"; - testUUID32(uuids, ROUNDS); + testUUID32(uuids, rounds); break; */ @@ -143,7 +158,7 @@ private final void testJDKNames(Object[] uuids, int rounds) throws java.io.IOExc { while (--rounds >= 0) { for (int i = 0, len = uuids.length; i < len; ++i) { - final byte[] nameBytes = NAME.getBytes("UTF-8"); + final byte[] nameBytes = NAME_BYTES; uuids[i] = UUID.nameUUIDFromBytes(nameBytes); } } @@ -171,13 +186,13 @@ private final void testNameBased(Object[] uuids, int rounds, StringArgGenerator { while (--rounds >= 0) { for (int i = 0, len = uuids.length; i < len; ++i) { - uuids[i] = uuidGen.generate(NAME); + uuids[i] = uuidGen.generate(NAME_STRING); } } } public static void main(String[] args) throws Exception { - new MeasurePerformance().test(); + new MeasurePerformance(DEFAULT_ROUNDS, true).test(); } } diff --git a/src/test/java/perf/MeasurePerformanceTest.java b/src/test/java/perf/MeasurePerformanceTest.java new file mode 100644 index 0000000..fdc81c7 --- /dev/null +++ b/src/test/java/perf/MeasurePerformanceTest.java @@ -0,0 +1,17 @@ +package perf; + +import org.junit.Test; + +// Things we do for Code Coverage... altough "perf/MeasurePerformance.java" +// is only to be manually run, it is included in build, so +// we get code coverage whether we want it or not. So let's have +// a silly little driver to exercise it from unit tests and avoid dinging +// overall test coverage +public class MeasurePerformanceTest +{ + @Test + public void runMinimalPerfTest() throws Exception + { + new MeasurePerformance(10, false).test(); + } +} From 4bd4b29c7ff61d7a839ee7b5f5981f8170b04621 Mon Sep 17 00:00:00 2001 From: SquireOfSoftware Date: Sat, 8 Jun 2024 09:23:01 -0700 Subject: [PATCH 41/58] feat(test): cowtowncoder#87 Add tests to jug (#113) --- src/main/java/com/fasterxml/uuid/Jug.java | 226 +++++++++--------- .../java/com/fasterxml/uuid/JugNamedTest.java | 178 ++++++++++++++ .../com/fasterxml/uuid/JugNoArgsTest.java | 217 +++++++++++++++++ 3 files changed, 510 insertions(+), 111 deletions(-) create mode 100644 src/test/java/com/fasterxml/uuid/JugNamedTest.java create mode 100644 src/test/java/com/fasterxml/uuid/JugNoArgsTest.java diff --git a/src/main/java/com/fasterxml/uuid/Jug.java b/src/main/java/com/fasterxml/uuid/Jug.java index 3b9a71f..b019447 100644 --- a/src/main/java/com/fasterxml/uuid/Jug.java +++ b/src/main/java/com/fasterxml/uuid/Jug.java @@ -47,7 +47,7 @@ public class Jug OPTIONS.put("verbose", "v"); } - protected static void printUsage() + protected void printUsage() { String clsName = Jug.class.getName(); System.err.println("Usage: java "+clsName+" [options] type"); @@ -75,7 +75,7 @@ protected static void printUsage() System.err.println(" epoch-based / e: generate UUID based on current time (as 'epoch') and random number"); } - private static void printMap(Map m, PrintStream out, boolean option) + private void printMap(Map m, PrintStream out, boolean option) { int i = 0; int len = m.size(); @@ -102,6 +102,10 @@ private static void printMap(Map m, PrintStream out, boolean opti public static void main(String[] args) { + new Jug().run(args); + } + + public void run(String[] args) { if (args.length == 0) { printUsage(); return; @@ -123,7 +127,7 @@ public static void main(String[] args) if (tmp == null) { if (!TYPES.containsValue(type)) { System.err.println("Unrecognized UUID generation type '"+ - type+"'; currently available ones are:"); + type+"'; currently available ones are:"); printMap(TYPES, System.err, false); System.err.println(); System.exit(1); @@ -136,7 +140,7 @@ public static void main(String[] args) NoArgGenerator noArgGenerator = null; // random- or time-based StringArgGenerator nameArgGenerator = null; // name-based - + for (int i = 0; i < count; ++i) { String opt = args[i]; @@ -170,46 +174,46 @@ public static void main(String[] args) try { String next; switch (option) { - case 'c': - // Need a number now: - next = args[++i]; - try { - genCount = Integer.parseInt(next); - } catch (NumberFormatException nex) { - System.err.println("Invalid number argument for option '"+opt+"', exiting."); - System.exit(1); - } - if (genCount < 1) { - System.err.println("Invalid number argument for option '"+opt+"'; negative numbers not allowed, ignoring (defaults to 1)."); - } - break; - case 'e': - // Need the ethernet address: - next = args[++i]; - try { - addr = EthernetAddress.valueOf(next); - } catch (NumberFormatException nex) { - System.err.println("Invalid ethernet address for option '"+opt+"', error: "+nex.toString()); - System.exit(1); - } - break; - case 'h': - printUsage(); - return; - case 'n': - // Need the name - name = args[++i]; - break; - case 'p': // performance: - performance = true; - break; - case 's': - // Need the namespace id - nameSpace = args[++i]; - break; - case 'v': - verbose = true; - break; + case 'c': + // Need a number now: + next = args[++i]; + try { + genCount = Integer.parseInt(next); + } catch (NumberFormatException nex) { + System.err.println("Invalid number argument for option '"+opt+"', exiting."); + System.exit(1); + } + if (genCount < 1) { + System.err.println("Invalid number argument for option '"+opt+"'; negative numbers not allowed, ignoring (defaults to 1)."); + } + break; + case 'e': + // Need the ethernet address: + next = args[++i]; + try { + addr = EthernetAddress.valueOf(next); + } catch (NumberFormatException nex) { + System.err.println("Invalid ethernet address for option '"+opt+"', error: "+nex.toString()); + System.exit(1); + } + break; + case 'h': + printUsage(); + return; + case 'n': + // Need the name + name = args[++i]; + break; + case 'p': // performance: + performance = true; + break; + case 's': + // Need the namespace id + nameSpace = args[++i]; + break; + case 'v': + verbose = true; + break; } } catch (IndexOutOfBoundsException ie) { // We get here when an arg is missing... @@ -227,80 +231,80 @@ public static void main(String[] args) boolean usesRnd = false; switch (typeC) { - case 't': // time-based - case 'o': // reordered-time-based (Version 6) - // 30-Jun-2022, tatu: Is this true? My former self must have had his - // reasons so leaving as is but... odd. - usesRnd = true; - // No address specified? Need a dummy one... - if (addr == null) { - if (verbose) { - System.out.print("(no address specified, generating dummy address: "); + case 't': // time-based + case 'o': // reordered-time-based (Version 6) + // 30-Jun-2022, tatu: Is this true? My former self must have had his + // reasons so leaving as is but... odd. + usesRnd = true; + // No address specified? Need a dummy one... + if (addr == null) { + if (verbose) { + System.out.print("(no address specified, generating dummy address: "); + } + addr = EthernetAddress.constructMulticastAddress(new java.util.Random(System.currentTimeMillis())); + if (verbose) { + System.out.print(addr.toString()); + System.out.println(")"); + } } - addr = EthernetAddress.constructMulticastAddress(new java.util.Random(System.currentTimeMillis())); - if (verbose) { - System.out.print(addr.toString()); - System.out.println(")"); + noArgGenerator = (typeC == 't') + ? Generators.timeBasedGenerator(addr) + : Generators.timeBasedReorderedGenerator(addr); + break; + case 'r': // random-based + usesRnd = true; + { + SecureRandom r = new SecureRandom(); + if (verbose) { + System.out.print("(using secure random generator, info = '"+r.getProvider().getInfo()+"')"); + } + noArgGenerator = Generators.randomBasedGenerator(r); } - } - noArgGenerator = (typeC == 't') - ? Generators.timeBasedGenerator(addr) - : Generators.timeBasedReorderedGenerator(addr); - break; - case 'r': // random-based - usesRnd = true; - { - SecureRandom r = new SecureRandom(); - if (verbose) { - System.out.print("(using secure random generator, info = '"+r.getProvider().getInfo()+"')"); + break; + case 'e': // epoch-time-based + usesRnd = true; + { + SecureRandom r = new SecureRandom(); + if (verbose) { + System.out.print("(using secure random generator, info = '"+r.getProvider().getInfo()+"')"); + } + noArgGenerator = Generators.timeBasedEpochGenerator(r); } - noArgGenerator = Generators.randomBasedGenerator(r); - } - break; - case 'e': // epoch-time-based - usesRnd = true; - { - SecureRandom r = new SecureRandom(); - if (verbose) { - System.out.print("(using secure random generator, info = '"+r.getProvider().getInfo()+"')"); + break; + case 'm': // random-epoch-time-based + usesRnd = true; + { + SecureRandom r = new SecureRandom(); + if (verbose) { + System.out.print("(using secure random generator, info = '"+r.getProvider().getInfo()+"')"); + } + noArgGenerator = Generators.timeBasedEpochRandomGenerator(r); } - noArgGenerator = Generators.timeBasedEpochGenerator(r); - } - break; - case 'm': // random-epoch-time-based - usesRnd = true; - { - SecureRandom r = new SecureRandom(); - if (verbose) { - System.out.print("(using secure random generator, info = '"+r.getProvider().getInfo()+"')"); + break; + case 'n': // name-based + if (nameSpace == null) { + System.err.println("--name-space (-s) - argument missing when using method that requires it, exiting."); + System.exit(1); } - noArgGenerator = Generators.timeBasedEpochRandomGenerator(r); - } - break; - case 'n': // name-based - if (nameSpace == null) { - System.err.println("--name-space (-s) - argument missing when using method that requires it, exiting."); - System.exit(1); - } - if (name == null) { - System.err.println("--name (-n) - argument missing when using method that requires it, exiting."); - System.exit(1); - } - if (typeC == 'n') { - String orig = nameSpace; - nameSpace = nameSpace.toLowerCase(); - if (nameSpace.equals("url")) { - nsUUID = NameBasedGenerator.NAMESPACE_URL; - } else if (nameSpace.equals("dns")) { - nsUUID = NameBasedGenerator.NAMESPACE_DNS; - } else { - System.err.println("Unrecognized namespace '"+orig - +"'; only DNS and URL allowed for name-based generation."); + if (name == null) { + System.err.println("--name (-n) - argument missing when using method that requires it, exiting."); System.exit(1); } - } - nameArgGenerator = Generators.nameBasedGenerator(nsUUID); - break; + if (typeC == 'n') { + String orig = nameSpace; + nameSpace = nameSpace.toLowerCase(); + if (nameSpace.equals("url")) { + nsUUID = NameBasedGenerator.NAMESPACE_URL; + } else if (nameSpace.equals("dns")) { + nsUUID = NameBasedGenerator.NAMESPACE_DNS; + } else { + System.err.println("Unrecognized namespace '"+orig + +"'; only DNS and URL allowed for name-based generation."); + System.exit(1); + } + } + nameArgGenerator = Generators.nameBasedGenerator(nsUUID); + break; } // And then let's rock: diff --git a/src/test/java/com/fasterxml/uuid/JugNamedTest.java b/src/test/java/com/fasterxml/uuid/JugNamedTest.java new file mode 100644 index 0000000..2352a94 --- /dev/null +++ b/src/test/java/com/fasterxml/uuid/JugNamedTest.java @@ -0,0 +1,178 @@ +package com.fasterxml.uuid; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +import static org.hamcrest.core.StringContains.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +@RunWith(Parameterized.class) +public class JugNamedTest { + @Parameterized.Parameter + public UseCase useCase; + + private PrintStream oldStrOut; + private PrintStream oldStrErr; + + private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + private final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); + private Jug jug_underTest; + + @Before + public void setup() { + jug_underTest = new Jug();oldStrOut = System.out; + oldStrErr = System.err; + PrintStream stubbedStream = new PrintStream(outContent); + System.setOut(stubbedStream); + PrintStream stubbedErrStream = new PrintStream(errContent); + System.setErr(stubbedErrStream); + } + + @After + public void cleanup() { + System.setOut(oldStrOut); + System.setErr(oldStrErr); + } + + @Test + public void run_shouldProduceUUID() { + // given + + // when + List arguments = useCase.getArgs(); + jug_underTest.run(arguments.toArray((String[]) Array.newInstance(String.class, 0))); + + // then - if it is a UUID then we should be able to parse it back out + String actualUuid = outContent.toString(); + assertEquals('\n', actualUuid.charAt(actualUuid.length() - 1)); + + assertEquals(UUID.class, + UUID.fromString(actualUuid.substring(0, actualUuid.length() - 1)).getClass()); + } + + @Test + public void run_givenCount3_shouldProduceUUID() { + // given + + // when + List arguments = useCase.getArgs(); + arguments.add(0, "-c"); + arguments.add(1, "3"); + jug_underTest.run(arguments.toArray((String[]) Array.newInstance(String.class, 0))); + + // then - if it is a UUID then we should be able to parse it back out + String[] actualUuids = outContent.toString().split("\n"); + for(String actualUuid: actualUuids) { + assertEquals(UUID.class, + UUID.fromString(actualUuid).getClass()); + } + } + + @Test + public void run_givenPerformance_shouldProducePerformanceInfo() { + // given + + // when + List arguments = useCase.getArgs(); + arguments.add(0, "-p"); + jug_underTest.run(arguments.toArray((String[]) Array.newInstance(String.class, 0))); + + // then + String actualOutput = outContent.toString(); + + assertThat(actualOutput, containsString("Performance: took")); + } + @Test + public void run_givenHelp_shouldProduceHelpInfo() { + // given + + // when + List arguments = useCase.getArgs(); + arguments.add(0, "-h"); + jug_underTest.run(arguments.toArray((String[]) Array.newInstance(String.class, 0))); + + // then + String actualOutput = errContent.toString(); + + assertThat(actualOutput, containsString("Usage: java")); + } + + @Test + public void run_givenVerbose_shouldProduceExtraInfo() { + // given + + // when + List arguments = useCase.getArgs(); + arguments.add(0, "-v"); + jug_underTest.run(arguments.toArray((String[]) Array.newInstance(String.class, 0))); + + // then + String actualOutput = outContent.toString(); + + assertThat(actualOutput, containsString("Done.")); + } + + @Test + public void run_givenVerboseAndPerformance_shouldProduceExtraInfo() { + // given + + // when + List arguments = useCase.getArgs(); + arguments.add(0, "-v"); + arguments.add(1, "-p"); + jug_underTest.run(arguments.toArray((String[]) Array.newInstance(String.class, 0))); + + // then + String actualOutput = outContent.toString(); + + assertThat(actualOutput, containsString("Done.")); + assertThat(actualOutput, containsString("Performance: took")); + } + + @Parameterized.Parameters(name = "{index} -> {0}") + public static List useCases() { + return Arrays.asList( + new UseCase("n", "-n", "world", "-s", "url"), + new UseCase("n", "-n", "world", "-s", "dns") + ); + } + + private static class UseCase { + private final String type; + private String[] options = new String[]{}; + + public UseCase(String type, String...options) { + this.type = type; + if (options != null) { + this.options = options; + } + } + + public List getArgs() { + List arguments = new ArrayList<>(Arrays.asList(options)); + arguments.add(type); + return arguments; + } + + @Override + public String toString() { + if (options.length == 0) { + return String.format("type: %s, options: no options", type); + } else { + return String.format("type: %s, options: %s", type, String.join(", ", options)); + } + } + } +} \ No newline at end of file diff --git a/src/test/java/com/fasterxml/uuid/JugNoArgsTest.java b/src/test/java/com/fasterxml/uuid/JugNoArgsTest.java new file mode 100644 index 0000000..d000105 --- /dev/null +++ b/src/test/java/com/fasterxml/uuid/JugNoArgsTest.java @@ -0,0 +1,217 @@ +package com.fasterxml.uuid; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.lang.reflect.Array; +import java.util.*; + +import static org.hamcrest.core.StringContains.containsString; +import static org.junit.Assert.*; + +@RunWith(Parameterized.class) +public class JugNoArgsTest { + @Parameterized.Parameter + public String useCase; + + private PrintStream oldStrOut; + private PrintStream oldStrErr; + + private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + private final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); + private Jug jug_underTest; + + @Before + public void setup() { + jug_underTest = new Jug(); + oldStrOut = System.out; + oldStrErr = System.err; + PrintStream stubbedStream = new PrintStream(outContent); + System.setOut(stubbedStream); + PrintStream stubbedErrStream = new PrintStream(errContent); + System.setErr(stubbedErrStream); + } + + @After + public void cleanup() { + System.setOut(oldStrOut); + System.setErr(oldStrErr); + } + + @Test + public void run_givenNoOptions_shouldProduceUUID() { + // given + + // when + jug_underTest.run(new String[]{useCase}); + + // then - if it is a UUID then we should be able to parse it back out + String actualUuid = outContent.toString(); + assertEquals('\n', actualUuid.charAt(actualUuid.length() - 1)); + + assertEquals(UUID.class, + UUID.fromString(actualUuid.substring(0, actualUuid.length() - 1)).getClass()); + } + + @Test + public void run_givenCount1_shouldProduceUUID() { + // given + + // when + List arguments = new ArrayList<>(Arrays.asList("-c", "1")); + arguments.add(useCase); + jug_underTest.run(arguments.toArray((String[]) Array.newInstance(String.class, 0))); + + // then - if it is a UUID then we should be able to parse it back out + String actualUuid = outContent.toString(); + assertEquals('\n', actualUuid.charAt(actualUuid.length() - 1)); + + assertEquals(UUID.class, + UUID.fromString(actualUuid.substring(0, actualUuid.length() - 1)).getClass()); + } + + @Test + public void run_givenCount2_shouldProduce2UUIDs() { + // given + + // when + List arguments = new ArrayList<>(Arrays.asList("-c", "2")); + arguments.add(useCase); + jug_underTest.run(arguments.toArray((String[]) Array.newInstance(String.class, 0))); + + // then - if it is a UUID then we should be able to parse it back out + String[] actualUuids = outContent.toString().split("\n"); + assertEquals(2, actualUuids.length); + + for(String actualUuid: actualUuids) { + assertEquals(UUID.class, + UUID.fromString(actualUuid).getClass()); + } + } + + @Test + public void run_givenEthernet_shouldProduceUUID() { + // given + + // when + List arguments = new ArrayList<>(Arrays.asList("-e", ":::::")); + arguments.add(useCase); + jug_underTest.run(arguments.toArray((String[]) Array.newInstance(String.class, 0))); + + // then - if it is a UUID then we should be able to parse it back out + String actualUuid = outContent.toString(); + assertEquals('\n', actualUuid.charAt(actualUuid.length() - 1)); + + assertEquals(UUID.class, + UUID.fromString(actualUuid.substring(0, actualUuid.length() - 1)).getClass()); + } + + @Test + public void run_givenName_shouldProduceUUID() { + // given + + // when + List arguments = new ArrayList<>(Arrays.asList("-n", "hello")); + arguments.add(useCase); + jug_underTest.run(arguments.toArray((String[]) Array.newInstance(String.class, 0))); + + // then - if it is a UUID then we should be able to parse it back out + String actualUuid = outContent.toString(); + assertEquals('\n', actualUuid.charAt(actualUuid.length() - 1)); + + assertEquals(UUID.class, + UUID.fromString(actualUuid.substring(0, actualUuid.length() - 1)).getClass()); + } + + @Test + public void run_givenDnsNameSpace_shouldProduceUUID() { + // given + + // when + List arguments = new ArrayList<>(Arrays.asList("-s", "dns")); + arguments.add(useCase); + jug_underTest.run(arguments.toArray((String[]) Array.newInstance(String.class, 0))); + + // then - if it is a UUID then we should be able to parse it back out + String actualUuid = outContent.toString(); + assertEquals('\n', actualUuid.charAt(actualUuid.length() - 1)); + + assertEquals(UUID.class, + UUID.fromString(actualUuid.substring(0, actualUuid.length() - 1)).getClass()); + } + + @Test + public void run_givenUrlNameSpace_shouldProduceUUID() { + // given + + // when + List arguments = new ArrayList<>(Arrays.asList("-s", "url")); + arguments.add(useCase); + jug_underTest.run(arguments.toArray((String[]) Array.newInstance(String.class, 0))); + + // then - if it is a UUID then we should be able to parse it back out + String actualUuid = outContent.toString(); + assertEquals('\n', actualUuid.charAt(actualUuid.length() - 1)); + + assertEquals(UUID.class, + UUID.fromString(actualUuid.substring(0, actualUuid.length() - 1)).getClass()); + } + + @Test + public void run_givenPerformance_shouldProducePerformanceInfo() { + // given + + // when + List arguments = Arrays.asList("-p", useCase); + jug_underTest.run(arguments.toArray((String[]) Array.newInstance(String.class, 0))); + + // then + String actualOutput = outContent.toString(); + + assertThat(actualOutput, containsString("Performance: took")); + } + + @Test + public void run_givenHelp_shouldProduceHelpInfo() { + // given + + // when + List arguments = Arrays.asList("-h", useCase); + jug_underTest.run(arguments.toArray((String[]) Array.newInstance(String.class, 0))); + + // then + String actualOutput = errContent.toString(); + + assertThat(actualOutput, containsString("Usage: java")); + } + + @Test + public void run_givenVerbose_shouldProduceExtraInfo() { + // given + + // when + List arguments = Arrays.asList("-v", useCase); + jug_underTest.run(arguments.toArray((String[]) Array.newInstance(String.class, 0))); + + // then + String actualOutput = outContent.toString(); + + assertThat(actualOutput, containsString("Done.")); + } + + @Parameterized.Parameters(name = "{index} -> type: {0}") + public static List useCases() { + return Arrays.asList( + "t", + "o", + "r", + "e", + "m" + ); + } +} \ No newline at end of file From 612a9d45ce1a8559a969355586023a58f5daa706 Mon Sep 17 00:00:00 2001 From: Alexander Ilinykh Date: Wed, 19 Jun 2024 03:58:33 +0500 Subject: [PATCH 42/58] increase version in readme to 5.1.0, add gradle import configuration (#114) --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4a31d9b..1e08098 100644 --- a/README.md +++ b/README.md @@ -47,10 +47,17 @@ Maven coordinates are: com.fasterxml.uuid java-uuid-generator - 5.0.0 + 5.1.0 ``` + +Gradle: + +```groovy +implementation 'com.fasterxml.uuid:java-uuid-generator:5.1.0' +``` + #### Third-party Dependencies by JUG The only dependency for JUG is the logging library: From 093c702bafadd1344e85ad03d2a036a3843a377d Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Sun, 30 Jun 2024 10:11:57 -0700 Subject: [PATCH 43/58] Merge in useful parts from #115 (contributed by @divinenickname) --- README.md | 17 ++++++++++++----- pom.xml | 4 +++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4a31d9b..fba8cd7 100644 --- a/README.md +++ b/README.md @@ -47,10 +47,17 @@ Maven coordinates are: com.fasterxml.uuid java-uuid-generator - 5.0.0 + 5.1.0 ``` + +Gradle: + +```groovy +implementation 'com.fasterxml.uuid:java-uuid-generator:5.1.0' +``` + #### Third-party Dependencies by JUG The only dependency for JUG is the logging library: @@ -150,25 +157,25 @@ it is rather slower than JUG version: for more information, read JUG jar built under `target/`: ``` -target/java-uuid-generator-5.0.0-SNAPSHOT.jar +target/java-uuid-generator-5.1.0-SNAPSHOT.jar ``` can also be used as a simple Command-line UUID generation tool. To see usage you can do something like: - java -jar target/java-uuid-generator-5.0.0-SNAPSHOT.jar + java -jar target/java-uuid-generator-5.1.0-SNAPSHOT.jar and get full instructions, but to generate 5 Random-based UUIDs, you would use: - java -jar target/java-uuid-generator-5.0.0-SNAPSHOT.jar -c 5 r + java -jar target/java-uuid-generator-5.1.0-SNAPSHOT.jar -c 5 r (where `-c` (or `--count`) means number of UUIDs to generate, and `r` means Random-based version) NOTE: this functionality is included as of JUG 4.1 -- with earlier versions you would need a bit longer invocation as Jar metadata did not specify "Main-Class". If so, you would need to use - java -cp target/java-uuid-generator-5.0.0-SNAPSHOT.jar com.fasterxml.uuid.Jug -c 5 r + java -cp target/java-uuid-generator-5.1.0-SNAPSHOT.jar com.fasterxml.uuid.Jug -c 5 r ## Compatibility diff --git a/pom.xml b/pom.xml index c22f798..178b340 100644 --- a/pom.xml +++ b/pom.xml @@ -132,12 +132,14 @@ https://stackoverflow.com/questions/37958104/maven-javadoc-no-source-files-for-p com.fasterxml.uuid;version="${project.version}", com.fasterxml.uuid.ext;version="${project.version}", - com.fasterxml.uuid.impl;version="${project.version}" + com.fasterxml.uuid.impl;version="${project.version}", + com.fasterxml.uuid.jug;version="${project.version}" com.fasterxml.uuid;version="[${project.version},${project.version}]", com.fasterxml.uuid.ext;version="[${project.version},${project.version}]", com.fasterxml.uuid.impl;version="[${project.version},${project.version}]", + com.fasterxml.uuid.jug;version="[${project.version},${project.version}]", org.slf4j;version="[${slf4j.version},2)" From ff14985bb903c8d47b1c15a6acc0d8aa59cdf52d Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Sun, 30 Jun 2024 10:15:28 -0700 Subject: [PATCH 44/58] Add note in CREDITS for contribution via #115 --- release-notes/CREDITS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/release-notes/CREDITS b/release-notes/CREDITS index e55939e..a4819df 100644 --- a/release-notes/CREDITS +++ b/release-notes/CREDITS @@ -148,3 +148,6 @@ Daniel Albuquerque (worldtiki@github) * Contributed #99: New factory method to create TimeBasedEpochRandomGenerator [5.1.0] +Alexander Ilinykh (divinenickname@github) + * Contributed improvements to README.md, pom.xml (OSGi inclusion) + [5.1.1] From 952bac8323aa6e4bd094a6264b2395ba7cc390b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 14:35:35 -0700 Subject: [PATCH 45/58] Bump the github-actions group with 2 updates (#116) --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ecd2326..105b8e3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,7 +24,7 @@ jobs: env: JAVA_OPTS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Set up JDK uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 with: @@ -38,7 +38,7 @@ jobs: run: ./mvnw -B -q -ff -ntp test - name: Publish code coverage if: github.event_name != 'pull_request' && matrix.java_version == '8' - uses: codecov/codecov-action@125fc84a9a348dbcf27191600683ec096ec9021c # v4.4.1 + uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0 with: token: ${{ secrets.CODECOV_TOKEN }} file: ./target/site/jacoco/jacoco.xml From cb445094fa6c39bc5e794e9f2c2c4ebbe13bc786 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 17:36:21 -0700 Subject: [PATCH 46/58] Bump actions/setup-java from 4.2.1 to 4.2.2 in the github-actions group (#118) --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 105b8e3..9c4258a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,7 +26,7 @@ jobs: steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Set up JDK - uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 + uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2 with: distribution: "temurin" java-version: ${{ matrix.java_version }} From b8167d379216bd7ac490297378d4ba9536592321 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Oct 2024 08:28:35 -0700 Subject: [PATCH 47/58] Bump the github-actions group with 3 updates (#119) --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9c4258a..32bfa9e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,9 +24,9 @@ jobs: env: JAVA_OPTS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: Set up JDK - uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2 + uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # v4.4.0 with: distribution: "temurin" java-version: ${{ matrix.java_version }} @@ -38,7 +38,7 @@ jobs: run: ./mvnw -B -q -ff -ntp test - name: Publish code coverage if: github.event_name != 'pull_request' && matrix.java_version == '8' - uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0 + uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0 with: token: ${{ secrets.CODECOV_TOKEN }} file: ./target/site/jacoco/jacoco.xml From 2e8bf01c0a5101dad18965fb6998f296a844059b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 13:32:46 -0700 Subject: [PATCH 48/58] Bump the github-actions group with 2 updates (#120) --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 32bfa9e..1bd5c08 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,9 +24,9 @@ jobs: env: JAVA_OPTS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up JDK - uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # v4.4.0 + uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0 with: distribution: "temurin" java-version: ${{ matrix.java_version }} From 2bc0cf24612c1ff1e641d3f3aea2b87735f099af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Dec 2024 13:35:31 -0800 Subject: [PATCH 49/58] Bump codecov/codecov-action in the github-actions group (#121) --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1bd5c08..a27821f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -38,7 +38,7 @@ jobs: run: ./mvnw -B -q -ff -ntp test - name: Publish code coverage if: github.event_name != 'pull_request' && matrix.java_version == '8' - uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0 + uses: codecov/codecov-action@015f24e6818733317a2da2edd6290ab26238649a # v5.0.7 with: token: ${{ secrets.CODECOV_TOKEN }} file: ./target/site/jacoco/jacoco.xml From 314366d05f6971bbe321ef3731253a537566332d Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 10 Dec 2024 18:03:19 -0800 Subject: [PATCH 50/58] Fix #122: update refs to later RFC (9562) from one it obsoletes (4122) (#123) --- README.md | 6 +++--- release-notes/VERSION | 5 +++++ src/main/java/com/fasterxml/uuid/UUIDType.java | 2 +- src/main/java/com/fasterxml/uuid/impl/UUIDUtil.java | 4 ++-- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index fba8cd7..69d7355 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ JUG is a set of Java classes for working with UUIDs: generating UUIDs using any of standard methods, outputting efficiently, sorting and so on. -It generates UUIDs according to the [UUID specification (RFC-4122)](https://tools.ietf.org/html/rfc4122) +It generates UUIDs according to the [UUID specification (RFC-9562)](https://tools.ietf.org/html/rfc9562) (see [Wikipedia UUID page](http://en.wikipedia.org/wiki/UUID) for more explanation) JUG was written by Tatu Saloranta () originally in 2002 and has been updated over the years. @@ -12,13 +12,13 @@ JUG is licensed under [Apache License 2.0](http://www.apache.org/licenses/LICENS ## Supported UUID versions (1, 3, 4, 5, 6, 7) -JUG supports both "classic" versions defined in RFC 4122]: +JUG supports both "classic" versions defined in [RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122): * `1`: time/location - based * `3` and `5`: name hash - based * `4`: random number - based -and newly (in 2022-2024) proposed (see [uuid6](https://uuid6.github.io/uuid6-ietf-draft/) and [RFC-4122 bis](https://datatracker.ietf.org/doc/draft-ietf-uuidrev-rfc4122bis/)) variants: +and newly (in 2022-) proposed (see [uuid6](https://uuid6.github.io/uuid6-ietf-draft/) and [RFC-9562](https://datatracker.ietf.org/doc/html/rfc9562) variants: * `6`: reordered variant of version `1` (with lexicographic ordering) * `7`: Unix-timestamp + random based variant (also with lexicographic ordering) diff --git a/release-notes/VERSION b/release-notes/VERSION index 6e24d51..63e0c96 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -4,6 +4,11 @@ Project: java-uuid-generator Releases ============================================================================ +(not yet released) + +#122: RFC-4122 Obsoleted by RFC-9562 (document change) + (pointed out by @akefirad) + 5.1.0 (02-Jun-2024) #99: New factory method to create TimeBasedEpochRandomGenerator diff --git a/src/main/java/com/fasterxml/uuid/UUIDType.java b/src/main/java/com/fasterxml/uuid/UUIDType.java index ee33b2f..1794b86 100644 --- a/src/main/java/com/fasterxml/uuid/UUIDType.java +++ b/src/main/java/com/fasterxml/uuid/UUIDType.java @@ -2,7 +2,7 @@ /** * Enumeration of different flavors of UUIDs: 5 specified by specs - * (RFC-4122) + * (RFC-9562) * and one * virtual entry ("UNKNOWN") to represent invalid one that consists of * all zero bites diff --git a/src/main/java/com/fasterxml/uuid/impl/UUIDUtil.java b/src/main/java/com/fasterxml/uuid/impl/UUIDUtil.java index b2d5291..e66041f 100644 --- a/src/main/java/com/fasterxml/uuid/impl/UUIDUtil.java +++ b/src/main/java/com/fasterxml/uuid/impl/UUIDUtil.java @@ -46,7 +46,7 @@ public UUIDUtil() { } /** * Accessor for so-call "Nil UUID" (see - * RFC 4122/4.1.7; + * RFC 9562, #5.9; * one that is all zeroes. * * @since 4.1 @@ -59,7 +59,7 @@ public static UUID nilUUID() { /** * Accessor for so-call "Max UUID" (see - * UUID 6 draft; + * RFC-9562, #5.10); * one that is all one bits * * @since 4.1 From fa3e81e96d88a8be115f56755084e108ef4c4056 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 20:03:17 -0800 Subject: [PATCH 51/58] Bump the github-actions group with 2 updates (#126) --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a27821f..2939614 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,7 +26,7 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up JDK - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0 + uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0 with: distribution: "temurin" java-version: ${{ matrix.java_version }} @@ -38,7 +38,7 @@ jobs: run: ./mvnw -B -q -ff -ntp test - name: Publish code coverage if: github.event_name != 'pull_request' && matrix.java_version == '8' - uses: codecov/codecov-action@015f24e6818733317a2da2edd6290ab26238649a # v5.0.7 + uses: codecov/codecov-action@1e68e06f1dbfde0e4cefc87efeba9e4643565303 # v5.1.2 with: token: ${{ secrets.CODECOV_TOKEN }} file: ./target/site/jacoco/jacoco.xml From 1fc1aaa1ea77f50341fe14e1fccee56c79c08f31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Feb 2025 14:21:01 -0800 Subject: [PATCH 52/58] Bump the github-actions group with 2 updates (#127) --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2939614..e9c205a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,7 +26,7 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up JDK - uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0 + uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 with: distribution: "temurin" java-version: ${{ matrix.java_version }} @@ -38,7 +38,7 @@ jobs: run: ./mvnw -B -q -ff -ntp test - name: Publish code coverage if: github.event_name != 'pull_request' && matrix.java_version == '8' - uses: codecov/codecov-action@1e68e06f1dbfde0e4cefc87efeba9e4643565303 # v5.1.2 + uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 # v5.3.1 with: token: ${{ secrets.CODECOV_TOKEN }} file: ./target/site/jacoco/jacoco.xml From 370333666abee54301bb4072b192e2c4ce314389 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Mar 2025 13:34:32 -0800 Subject: [PATCH 53/58] Bump codecov/codecov-action in the github-actions group (#128) --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 22a2951..029f41b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -37,7 +37,7 @@ jobs: run: ./mvnw -B -q -ff -ntp test - name: Publish code coverage if: github.event_name != 'pull_request' && matrix.java_version == '8' - uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 # v5.3.1 + uses: codecov/codecov-action@0565863a31f2c772f9f0395002a31e3f06189574 # v5.4.0 with: token: ${{ secrets.CODECOV_TOKEN }} file: ./target/site/jacoco/jacoco.xml From 65606e133a2368dbd12aae43b0200a2a450ace55 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 18 Apr 2025 18:43:45 -0400 Subject: [PATCH 54/58] Update the link of Maven Central badge (#129) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 69d7355..26bd017 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ and newly (in 2022-) proposed (see [uuid6](https://uuid6.github.io/uuid6-ietf-dr | Type | Status | | ---- | ------ | | Build (CI) | [![Build (github)](https://github.com/cowtowncoder/java-uuid-generator/actions/workflows/main.yml/badge.svg)](https://github.com/cowtowncoder/java-uuid-generator/actions/workflows/main.yml) | -| Artifact | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.fasterxml.uuid/java-uuid-generator/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.fasterxml.uuid/java-uuid-generator/) | +| Artifact | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.fasterxml.uuid/java-uuid-generator/badge.svg)](https://search.maven.org/artifact/com.fasterxml.uuid/java-uuid-generator) | | OSS Sponsorship | [![Tidelift](https://tidelift.com/badges/package/maven/com.fasterxml.uuid:java-uuid-generator)](https://tidelift.com/subscription/pkg/maven-com-fasterxml-uuid-java-uuid-generator?utm_source=maven-com-fasterxml-uuid-java-uuid-generator&utm_medium=referral&utm_campaign=readme) | | Javadocs | [![Javadoc](https://javadoc.io/badge/com.fasterxml.uuid/java-uuid-generator.svg)](http://www.javadoc.io/doc/com.fasterxml.uuid/java-uuid-generator) | Code coverage (5.x) | [![codecov.io](https://codecov.io/github/cowtowncoder/java-uuid-generator/coverage.svg?branch=master)](https://codecov.io/github/cowtowncoder/java-uuid-generator?branch=master) | From ccaa18e7de716b30c216ad5896229e36d0d7fb71 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 May 2025 14:08:49 -0700 Subject: [PATCH 55/58] Bump the github-actions group with 2 updates (#130) --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 029f41b..c6c0b24 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,7 +25,7 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up JDK - uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: distribution: "temurin" java-version: ${{ matrix.java_version }} @@ -37,7 +37,7 @@ jobs: run: ./mvnw -B -q -ff -ntp test - name: Publish code coverage if: github.event_name != 'pull_request' && matrix.java_version == '8' - uses: codecov/codecov-action@0565863a31f2c772f9f0395002a31e3f06189574 # v5.4.0 + uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d # v5.4.2 with: token: ${{ secrets.CODECOV_TOKEN }} file: ./target/site/jacoco/jacoco.xml From c0377c026d24221b943ccf172e065ef970c3a39a Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Wed, 28 May 2025 18:45:50 -0700 Subject: [PATCH 56/58] Switch publishing to use Central Portal (#131) --- .github/workflows/main.yml | 11 +++++++---- .mvn/wrapper/maven-wrapper.properties | 4 ++-- pom.xml | 7 ++++++- release-notes/VERSION | 1 + 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c6c0b24..f1f3d5e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,7 +15,7 @@ permissions: contents: read jobs: build: - runs-on: 'ubuntu-22.04' + runs-on: 'ubuntu-latest' strategy: fail-fast: false matrix: @@ -30,15 +30,18 @@ jobs: distribution: "temurin" java-version: ${{ matrix.java_version }} cache: 'maven' + server-id: central-snapshots + server-username: CI_DEPLOY_USERNAME + server-password: CI_DEPLOY_PASSWORD - name: Build run: ./mvnw -B -q -ff -ntp verify - name: Generate code coverage - if: github.event_name != 'pull_request' && matrix.java_version == '8' + if: ${{ github.event_name != 'pull_request' && matrix.java_version == '8' }} run: ./mvnw -B -q -ff -ntp test - name: Publish code coverage - if: github.event_name != 'pull_request' && matrix.java_version == '8' + if: ${{ github.event_name != 'pull_request' && matrix.java_version == '8' }} uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d # v5.4.2 with: token: ${{ secrets.CODECOV_TOKEN }} - file: ./target/site/jacoco/jacoco.xml + files: ./target/site/jacoco/jacoco.xml flags: unittests diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 5366408..b9b1153 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -14,5 +14,5 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.3/apache-maven-3.9.3-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar diff --git a/pom.xml b/pom.xml index 178b340..7d2af65 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.fasterxml oss-parent - 58 + 68 com.fasterxml.uuid java-uuid-generator @@ -199,6 +199,11 @@ https://stackoverflow.com/questions/37958104/maven-javadoc-no-source-files-for-p + + + org.sonatype.central + central-publishing-maven-plugin + diff --git a/release-notes/VERSION b/release-notes/VERSION index 63e0c96..07335fb 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -8,6 +8,7 @@ Releases #122: RFC-4122 Obsoleted by RFC-9562 (document change) (pointed out by @akefirad) +- Update to `oss-parent` v68 to switch to Central Portal publishing 5.1.0 (02-Jun-2024) From 9d084352f36ed40a17c3e0d8ca30bb87ea47a4aa Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Wed, 28 May 2025 18:48:17 -0700 Subject: [PATCH 57/58] Udpate release notes wrt "master"->"main" branch renaming --- release-notes/VERSION | 1 + 1 file changed, 1 insertion(+) diff --git a/release-notes/VERSION b/release-notes/VERSION index 07335fb..14a5670 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -9,6 +9,7 @@ Releases #122: RFC-4122 Obsoleted by RFC-9562 (document change) (pointed out by @akefirad) - Update to `oss-parent` v68 to switch to Central Portal publishing +- Branch "master" renamed as "main" 5.1.0 (02-Jun-2024) From 37d4ea4cfed174eecef5594df98918eb6b42b23d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 May 2025 18:51:55 -0700 Subject: [PATCH 58/58] Bump codecov/codecov-action in the github-actions group (#132) --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f1f3d5e..abc6637 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -40,7 +40,7 @@ jobs: run: ./mvnw -B -q -ff -ntp test - name: Publish code coverage if: ${{ github.event_name != 'pull_request' && matrix.java_version == '8' }} - uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d # v5.4.2 + uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3 with: token: ${{ secrets.CODECOV_TOKEN }} files: ./target/site/jacoco/jacoco.xml