Repository navigation
Maintenance: Modernize GraalVM native-image test infrastructure #2416
Description
Summary
The✅generate-graalvm-filesandgraalvm-nativeMaven profiles can be consolidated into a single profile using the native-maven-plugin's built-in<agent>configuration.- GraalVM 21.0.10 introduced stricter
unsafeAllocatedenforcement inreflect-config.json. Mockito uses Objenesis →Unsafe.allocateInstance()which the tracing agent does not detect. This brokepowertools-cloudformationnative tests (PR chore: bump com.fasterxml.jackson:jackson-bom from 2.20.1 to 2.21.1 #2403). CI was pinned to GraalVM 21.0.8 as a quick fix.
Why is this needed?
- The two-profile approach requires two Maven invocations per module in CI, adding complexity and build time.
- Pinning to GraalVM 21.0.8 is a temporary workaround.
Which area does this relate to?
Governance
Action Items
1. Modernize to single profile with -Dagent=true ✅
-Dagent=trueConsolidate into a single graalvm-native profile across all 17+ modules.
Current approach (two profiles, two Maven invocations):
mvn -Pgenerate-graalvm-files clean test
mvn -Pgraalvm-native testTarget approach (single profile):
mvn -Pgraalvm-native -Dagent=true clean test
mvn -Pgraalvm-native native:metadata-copy
mvn -Pgraalvm-native testPlugin configuration (replaces generate-graalvm-files profile):
<configuration>
<agent>
<enabled>true</enabled>
<options>
<enableExperimentalPredefinedClasses>true</enableExperimentalPredefinedClasses>
<enableExperimentalUnsafeAllocationTracing>true</enableExperimentalUnsafeAllocationTracing>
</options>
<metadataCopy>
<disabledStages>
<stage>main</stage>
</disabledStages>
<merge>true</merge>
<outputDirectory>src/main/resources/META-INF/native-image/software.amazon.lambda/MODULE_NAME</outputDirectory>
</metadataCopy>
</agent>
</configuration>After migration:
- Remove
generate-graalvm-filesprofile from all modules - Simplify
check-build.yml - Update
GraalVM.md
2. Investigate and fix the unsafeAllocated issue
GraalVM 21.0.10+ requires "unsafeAllocated": true in reflect-config.json for classes instantiated via Unsafe.allocateInstance(). Mockito uses Objenesis which relies on this, and the tracing agent does not detect it.
Current workaround: CI pinned to GraalVM 21.0.8.
Solutions (in order of preference):
-
Investigate
enableExperimentalUnsafeAllocationTracing. The native-build-tools plugin has had this option for ~3 years (source). It maps toexperimental-unsafe-allocation-supportin the agent. Investigate if this can be leveraged to fix the issue. This is the most preferred option. -
Post-processing script. A
jq-based script that patchesreflect-config.jsonafter agent generation. Adds CI complexity, not recommended. -
Replace mocks with test doubles. Last resort. Requires rewriting tests across all affected modules.
References
- Native Build Tools Maven plugin: Agent configuration
- AgentConfiguration.java source
- PR chore: bump com.fasterxml.jackson:jackson-bom from 2.20.1 to 2.21.1 #2403
Acknowledgment
- This request meets Powertools for AWS Lambda (Java) Tenets
- Should this be considered in other Powertools for AWS Lambda languages?
Metadata
Metadata
Assignees
Type
Projects
- StatusShow more project fieldsWorking on it
Activity
phipag commentedon Mar 18, 2026
Created #2419 to address action item 1.
phipag commentedon Mar 18, 2026
For action item 2 we should also evaluate upgrading to Java 25 for Graal and dropping support for 21. For this project it makes sense because users can deploy to the al2023.provided Lambda runtime. It doesn't matter anymore which Java version is used to compile the native image as long as it works. This can also reduce our operational burden to support multiple graalvm versions at a time.
phipag commentedon Oct 7, 2026
Update: temporary JUnit 6 workaround and native-maven-plugin 1.x blocker
Two new items depend on this issue.
1. Temporary profile for JUnit 6 on GraalVM 21 (#2618)
The upgrade to JUnit 6.1.3 broke all native test builds on GraalVM 21.0.8 with
Classes that should be initialized at run time got initialized during image building.Root cause: On GraalVM for JDK 21 and earlier, the JUnit feature in native-maven-plugin initializes a hardcoded list of JUnit classes at build time. The list in 0.11.5 does not contain two classes that JUnit 5.14.1 and 6.x use during test discovery:
org.junit.jupiter.engine.discovery.MethodSegmentResolverandorg.junit.platform.launcher.core.DiscoveryIssueReportingDiscoveryListener. See graalvm/native-build-tools#794 and junit-team/junit-framework#5134.Workaround: #2618 adds the profile
native-junit6-jdk21to the rootpom.xml. It activates on JDK 21 and earlier and appends--initialize-at-build-timefor both classes to the native-maven-pluginbuildArgs. All 18 native modules pass with this change.Action item: Remove the
native-junit6-jdk21profile after the GraalVM move and the native-maven-plugin 1.x upgrade. The comment above the profile links back to this issue.2. native-maven-plugin 1.x is blocked on GraalVM 21 (#2661)
native-maven-plugin 1.x requires the 1.0 reachability metadata repository. That repository uses the unified
reachability-metadata.jsonformat, which targets GraalVM 25. GraalVM 21.0.8 cannot use it:{"bundle": ...}entries in theresourcesarray. This affects the netty-common 5.0.0.Alpha2 metadata, which the plugin selects for netty 4.1.137, and the metadata for jackson-databind and aspectjweaver.Could not initialize class org.mockito.Mockito. The new format no longer grants reflective queries on Byte Buddy'sInvoker. GraalVM 25 grants them implicitly, but GraalVM 21 does not.Unsupported class file major version 69from classes that Byte Buddy generates.Action items for this issue:
native-junit6-jdk21profile from the rootpom.xml.chore(ci): pin GraalVM native tests to GraalVM 25.4.4.1.1
chore(ci): run GraalVM native tests on GraalVM 25 innovation release
build(deps): upgrade native-maven-plugin to 1.1.14
chore(ci): pin GraalVM native tests to GraalVM 25.4.4.1.1