diff --git a/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidVersion6Strategy.java b/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidVersion6Strategy.java index abbd139459c9..9cfe46d728cf 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidVersion6Strategy.java +++ b/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidVersion6Strategy.java @@ -34,7 +34,8 @@ public class UuidVersion6Strategy implements UUIDGenerationStrategy, UuidValueGenerator { public static final UuidVersion6Strategy INSTANCE = new UuidVersion6Strategy(); - private static class Holder { + @Internal + public static class Holder { static final SecureRandom numberGenerator = new SecureRandom(); static final long EPOCH_1582_SECONDS = LocalDate.of( 1582, 10, 15 ) .atStartOfDay( ZoneId.of( "UTC" ) ) diff --git a/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidVersion7Strategy.java b/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidVersion7Strategy.java index f6e220c625f7..8687a1eab39c 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidVersion7Strategy.java +++ b/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidVersion7Strategy.java @@ -39,7 +39,8 @@ public class UuidVersion7Strategy implements UUIDGenerationStrategy, UuidValueGe public static final UuidVersion7Strategy INSTANCE = new UuidVersion7Strategy(); - private static class Holder { + @Internal + public static class Holder { private static final SecureRandom numberGenerator = new SecureRandom(); } @@ -82,7 +83,7 @@ private static long randomSequence() { @Internal public UuidVersion7Strategy() { - this( Instant.EPOCH, Holder.numberGenerator.nextLong( MAX_RANDOM_SEQUENCE ) ); + this( Instant.EPOCH, Long.MIN_VALUE ); } @Internal diff --git a/hibernate-graalvm/src/main/java/org/hibernate/graalvm/internal/GraalVMStaticFeature.java b/hibernate-graalvm/src/main/java/org/hibernate/graalvm/internal/GraalVMStaticFeature.java index 7c72790e74d0..4e39c08ad3cc 100644 --- a/hibernate-graalvm/src/main/java/org/hibernate/graalvm/internal/GraalVMStaticFeature.java +++ b/hibernate-graalvm/src/main/java/org/hibernate/graalvm/internal/GraalVMStaticFeature.java @@ -8,6 +8,7 @@ import java.lang.reflect.Executable; import java.util.ArrayList; +import org.graalvm.nativeimage.hosted.RuntimeClassInitialization; import org.hibernate.internal.util.ReflectHelper; import org.graalvm.nativeimage.hosted.Feature; @@ -36,6 +37,7 @@ public class GraalVMStaticFeature implements Feature { public void beforeAnalysis(Feature.BeforeAnalysisAccess before) { final Class[] needsHavingSimpleConstructors = StaticClassLists.typesNeedingDefaultConstructorAccessible(); final Class[] needingAllConstructorsAccessible = StaticClassLists.typesNeedingAllConstructorsAccessible(); + final Class[] typesNeedingRuntimeInitialization = StaticClassLists.typesNeedingRuntimeInitialization(); //Size formula is just a reasonable guess: ArrayList executables = new ArrayList<>( needsHavingSimpleConstructors.length + needingAllConstructorsAccessible.length * 3 ); for ( Class c : needsHavingSimpleConstructors ) { @@ -50,6 +52,8 @@ public void beforeAnalysis(Feature.BeforeAnalysisAccess before) { RuntimeReflection.register( needingAllConstructorsAccessible ); RuntimeReflection.register( StaticClassLists.typesNeedingArrayCopy() ); RuntimeReflection.register( executables.toArray(new Executable[0]) ); + + RuntimeClassInitialization.initializeAtRunTime( typesNeedingRuntimeInitialization ); } @Override diff --git a/hibernate-graalvm/src/main/java/org/hibernate/graalvm/internal/StaticClassLists.java b/hibernate-graalvm/src/main/java/org/hibernate/graalvm/internal/StaticClassLists.java index ab59caae374d..e34cbdc50bd6 100644 --- a/hibernate-graalvm/src/main/java/org/hibernate/graalvm/internal/StaticClassLists.java +++ b/hibernate-graalvm/src/main/java/org/hibernate/graalvm/internal/StaticClassLists.java @@ -76,4 +76,14 @@ public static Class[] typesNeedingArrayCopy() { }; } + /** + * The classes listed below use a SecureRandom. We need to avoid static initialization at build time of these, + * for it will trigger an error in GraalVM native images. + */ + public static Class[] typesNeedingRuntimeInitialization() { + return new Class[] { + org.hibernate.id.uuid.UuidVersion6Strategy.Holder.class, + org.hibernate.id.uuid.UuidVersion7Strategy.Holder.class, + }; + } } diff --git a/hibernate-graalvm/src/test/java/org/hibernate/graalvm/internal/StaticClassListsTest.java b/hibernate-graalvm/src/test/java/org/hibernate/graalvm/internal/StaticClassListsTest.java index cd95e366deb9..c76e646c29f7 100644 --- a/hibernate-graalvm/src/test/java/org/hibernate/graalvm/internal/StaticClassListsTest.java +++ b/hibernate-graalvm/src/test/java/org/hibernate/graalvm/internal/StaticClassListsTest.java @@ -16,6 +16,8 @@ import org.hibernate.Session; import org.hibernate.event.spi.EventType; +import org.hibernate.id.uuid.UuidVersion6Strategy; +import org.hibernate.id.uuid.UuidVersion7Strategy; import org.hibernate.internal.util.ReflectHelper; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; @@ -37,6 +39,38 @@ public static void index() throws IOException { hibernateIndex = JandexTestUtils.indexJar( Session.class ); } + @Nested + // Related Jira issue: https://hibernate.atlassian.net/browse/HHH-18974 + class TypesNeedingRuntimeInitialization { + @ParameterizedTest + @EnumSource(TypesNeedingRuntimeInitialization_Category.class) + void containsAllExpectedClasses(TypesNeedingRuntimeInitialization_Category category) { + assertThat( StaticClassLists.typesNeedingRuntimeInitialization() ) + .containsAll( category.classes().collect( Collectors.toSet() ) ); + } + + @Test + void meta_noMissingTestCategory() { + assertThat( Arrays.stream( TypesNeedingRuntimeInitialization_Category.values() ).flatMap( TypesNeedingRuntimeInitialization_Category::classes ) ) + .as( "If this fails, a category is missing in " + TypesNeedingRuntimeInitialization_Category.class ) + .contains( StaticClassLists.typesNeedingRuntimeInitialization() ); + } + } + + enum TypesNeedingRuntimeInitialization_Category { + UUID_STRATGY_HOLDERS_USING_SECURE_RANDOM { + @Override + Stream> classes() { + return Stream.of( + UuidVersion6Strategy.Holder.class, + UuidVersion7Strategy.Holder.class + ); + } + }; + + abstract Stream> classes(); + } + @Nested class TypesNeedingAllConstructorsAccessible { @ParameterizedTest