Skip to content

Maintenance: Modernize GraalVM native-image test infrastructure #2416

Description

@phipag

Summary

  1. The generate-graalvm-files and graalvm-native Maven profiles can be consolidated into a single profile using the native-maven-plugin's built-in <agent> configuration. ✅
  2. GraalVM 21.0.10 introduced stricter unsafeAllocated enforcement in reflect-config.json. Mockito uses Objenesis → Unsafe.allocateInstance() which the tracing agent does not detect. This broke powertools-cloudformation native 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 ✅

Consolidate 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 test

Target approach (single profile):

mvn -Pgraalvm-native -Dagent=true clean test
mvn -Pgraalvm-native native:metadata-copy
mvn -Pgraalvm-native test

Plugin 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-files profile 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):

  1. Investigate enableExperimentalUnsafeAllocationTracing. The native-build-tools plugin has had this option for ~3 years (source). It maps to experimental-unsafe-allocation-support in the agent. Investigate if this can be leveraged to fix the issue. This is the most preferred option.

  2. Post-processing script. A jq-based script that patches reflect-config.json after agent generation. Adds CI complexity, not recommended.

  3. Replace mocks with test doubles. Last resort. Requires rewriting tests across all affected modules.

References

Acknowledgment

Activity

  1. linked a pull request that will close this issuechore: modernize native maven profiles #2419on Mar 18, 2026
  2. phipag commented on Mar 18, 2026

    @phipag
    ContributorAuthor

    Created #2419 to address action item 1.

  3. phipag commented on Mar 18, 2026

    @phipag
    ContributorAuthor

    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.

  4. self-assigned this
    on Mar 18, 2026
  5. moved this from Triage to Working on it in Powertools for AWS Lambda (Java)on Mar 18, 2026
  6. reopened this on Mar 18, 2026
  7. phipag commented on Oct 7, 2026

    @phipag
    ContributorAuthor

    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.MethodSegmentResolver and org.junit.platform.launcher.core.DiscoveryIssueReportingDiscoveryListener. See graalvm/native-build-tools#794 and junit-team/junit-framework#5134.

    Workaround: #2618 adds the profile native-junit6-jdk21 to the root pom.xml. It activates on JDK 21 and earlier and appends --initialize-at-build-time for both classes to the native-maven-plugin buildArgs. All 18 native modules pass with this change.

    Action item: Remove the native-junit6-jdk21 profile 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.json format, which targets GraalVM 25. GraalVM 21.0.8 cannot use it:

    • Build fails: GraalVM 21 rejects the {"bundle": ...} entries in the resources array. 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.
    • Excluding that metadata is not enough: after the exclusion, the image builds, but 20 of 138 powertools-metrics tests fail with Could not initialize class org.mockito.Mockito. The new format no longer grants reflective queries on Byte Buddy's Invoker. GraalVM 25 grants them implicitly, but GraalVM 21 does not.
    • The old metadata cannot be pinned: the plugin rejects the 0.11.5 metadata repository.
    • GraalVM 25 alone does not fix it: with GraalVM 25.0.4, powertools-metrics fails with Unsupported class file major version 69 from classes that Byte Buddy generates.

    Action items for this issue:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions