diff --git a/CHANGELOG.md b/CHANGELOG.md index 053edf9764..8b30bf45d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Fixed +- [Core] Lazily start IncrementingUuidGenerator sessions([#2931](https://github.com/cucumber/cucumber-jvm/pull/2931) M.P. Korstanje) ## [7.20.0] - 2024-10-04 ### Added diff --git a/cucumber-core/src/main/java/io/cucumber/core/eventbus/IncrementingUuidGenerator.java b/cucumber-core/src/main/java/io/cucumber/core/eventbus/IncrementingUuidGenerator.java index 04bdd43644..e11d6d07cc 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/eventbus/IncrementingUuidGenerator.java +++ b/cucumber-core/src/main/java/io/cucumber/core/eventbus/IncrementingUuidGenerator.java @@ -68,7 +68,7 @@ public class IncrementingUuidGenerator implements UuidGenerator { /** * Computed UUID MSB value. */ - final long msb; + private long msb; /** * Counter for the UUID LSB. @@ -77,13 +77,13 @@ public class IncrementingUuidGenerator implements UuidGenerator { /** * Defines a new classloaderId for the class. This only affects instances - * created after the call (the instances created before the call keep their - * classloaderId). This method should be called to specify a classloaderId - * if you are using more than one class loader, and you want to guarantee a - * collision-free UUID generation (instead of the default random - * classloaderId which produces about 1% collision rate on the - * classloaderId, and thus can have UUID collision if the epoch-time, - * session counter and counter have the same values). + * created after the first call to {@link #generateId()} (the instances + * created before the call keep their classloaderId). This method should be + * called to specify a {@code classloaderId} if you are using more than one + * class loader, and you want to guarantee a collision-free UUID generation + * (instead of the default random classloaderId which produces about 1% + * collision rate on the classloaderId, and thus can have UUID collision if + * the epoch-time, session counter and counter have the same values). * * @param classloaderId the new classloaderId (only the least significant 12 * bits are used) @@ -94,6 +94,10 @@ public static void setClassloaderId(int classloaderId) { } public IncrementingUuidGenerator() { + + } + + private long initializeMsb() { long sessionId = sessionCounter.incrementAndGet(); if (sessionId == MAX_SESSION_ID) { throw new CucumberException( @@ -103,7 +107,7 @@ public IncrementingUuidGenerator() { } long epochTime = System.currentTimeMillis(); // msb = epochTime | sessionId | version | classloaderId - msb = ((epochTime & MAX_EPOCH_TIME) << 24) | (sessionId << 16) | (8 << 12) | classloaderId; + return ((epochTime & MAX_EPOCH_TIME) << 24) | (sessionId << 16) | (8 << 12) | classloaderId; } /** @@ -114,6 +118,11 @@ public IncrementingUuidGenerator() { */ @Override public UUID generateId() { + if (msb == 0) { + // Lazy init to avoid starting sessions when not used. + msb = initializeMsb(); + } + long counterValue = counter.incrementAndGet(); if (counterValue == MAX_COUNTER_VALUE) { throw new CucumberException( diff --git a/cucumber-core/src/test/java/io/cucumber/core/eventbus/IncrementingUuidGeneratorTest.java b/cucumber-core/src/test/java/io/cucumber/core/eventbus/IncrementingUuidGeneratorTest.java index 8bab8ec08d..540f5e60f4 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/eventbus/IncrementingUuidGeneratorTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/eventbus/IncrementingUuidGeneratorTest.java @@ -1,8 +1,8 @@ package io.cucumber.core.eventbus; import io.cucumber.core.exception.CucumberException; -import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.ThrowingSupplier; import java.io.IOException; import java.lang.reflect.Field; @@ -21,6 +21,8 @@ import java.util.stream.IntStream; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -119,20 +121,33 @@ void raises_exception_when_out_of_range() { // Then assertThat(cucumberException.getMessage(), - Matchers.containsString("Out of IncrementingUuidGenerator capacity")); + containsString("Out of IncrementingUuidGenerator capacity")); } @Test void version_overflow() { // Given + IncrementingUuidGenerator generator = new IncrementingUuidGenerator(); IncrementingUuidGenerator.sessionCounter.set(IncrementingUuidGenerator.MAX_SESSION_ID - 1); // When - CucumberException cucumberException = assertThrows(CucumberException.class, IncrementingUuidGenerator::new); + CucumberException cucumberException = assertThrows(CucumberException.class, generator::generateId); // Then assertThat(cucumberException.getMessage(), - Matchers.containsString("Out of IncrementingUuidGenerator capacity")); + containsString("Out of IncrementingUuidGenerator capacity")); + } + + @Test + void lazy_init() { + // Given + IncrementingUuidGenerator.sessionCounter.set(IncrementingUuidGenerator.MAX_SESSION_ID - 1); + + // When + ThrowingSupplier instantiateGenerator = IncrementingUuidGenerator::new; + + // Then + assertDoesNotThrow(instantiateGenerator); } private static void checkUuidProperties(List uuids) {