diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index d0d45354b..9ba6e2fe9 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -124,9 +124,9 @@ jobs: echo "Changes detected in powertools modules: $CHANGED_FILES" - # Find modules with graalvm-native profile and run tests + # Find modules with native profile and run tests with the tracing agent find . -name "pom.xml" -path "./powertools-*" | while read module; do - if grep -q "graalvm-native" "$module"; then + if grep -q "native" "$module"; then module_dir=$(dirname "$module") module_name=$(basename "$module_dir") @@ -135,11 +135,8 @@ jobs: echo " $CHANGED_FILES " | grep -q " pom.xml " || \ echo "$CHANGED_FILES" | grep -q "powertools-common/"; then echo "::group::Building $module_name with GraalVM" - echo "Changes detected in $module_name - running GraalVM tests" - echo "Regenerating GraalVM metadata for $module_dir" - mvn -B -q -f "$module" -Pgenerate-graalvm-files clean test - echo "Running GraalVM native tests for $module_dir" - mvn -B -q -f "$module" -Pgraalvm-native test + echo "Changes detected in $module_name - running GraalVM native tests" + mvn -B -q -f "$module" -Pnative -Dagent=true clean test echo "::endgroup::" else echo "No changes detected in $module_name - skipping GraalVM tests" diff --git a/GraalVM.md b/GraalVM.md index bbddb5e3b..28004714d 100644 --- a/GraalVM.md +++ b/GraalVM.md @@ -19,26 +19,33 @@ GraalVM native image compilation requires complete knowledge of an application's In order to generate the metadata reachability files for Powertools for Lambda, follow these general steps. -1. **Add Maven Profiles** - - Add profile for generating GraalVM reachability metadata files. You can find an example of this in profile `generate-graalvm-files` of this [pom.xml](powertools-common/pom.xml). - - Add another profile for running the tests in the native image. You can find and example of this in profile `graalvm-native` of this [pom.xml](powertools-common/pom.xml). +1. **Add the `native` Maven Profile** + - The root `pom.xml` provides a shared `pluginManagement` configuration for the `native-maven-plugin` with the `` configuration, common `buildArgs`, and the `test-native` execution. + - Each module only needs to define a `native` profile that specifies module-specific configuration: `imageName` and `metadataCopy.outputDirectory`. You can find an example in the `native` profile of this [pom.xml](powertools-common/pom.xml). + - If a module needs extra `buildArgs` beyond the common ones, use `` to add them without overriding the parent configuration. 2. **Generate Reachability Metadata** - Set the `JAVA_HOME` environment variable to use GraalVM - - Run tests with `-Pgenerate-graalvm-files` profile. + - Run tests with the `-Dagent=true` flag to attach the GraalVM tracing agent to the test execution: ```shell -mvn -Pgenerate-graalvm-files clean test +mvn -Pnative -Dagent=true clean test ``` -3. **Validate Native Image Tests** +3. **Copy Metadata to Source** + - Copy the generated metadata from the agent output directory to the module's `src/main/resources` directory: +```shell +mvn -Pnative native:metadata-copy +``` + +4. **Validate Native Image Tests** - Set the `JAVA_HOME` environment variable to use GraalVM - - Run tests with `-Pgraalvm-native` profile. This will build a GraalVM native image and run the JUnit tests. + - Run tests with `-Pnative` profile. This will build a GraalVM native image and run the JUnit tests. ```shell -mvn -Pgraalvm-native clean test +mvn -Pnative test ``` -4. **Clean Up Metadata** - - GraalVM metadata reachability files generated in Step 2 contains references to the test scoped dependencies as well. +5. **Clean Up Metadata** + - GraalVM metadata reachability files generated in Step 2 contain references to the test scoped dependencies as well. - Remove the references in generated metadata files for the following (and any other references to test scoped resources and classes): - JUnit - Mockito @@ -46,17 +53,21 @@ mvn -Pgraalvm-native clean test ## Known Issues and Solutions 1. **Mockito Compatibility** - - Powertools uses Mockito 5.x which uses “inline mock maker” as the default. This mock maker does not play well with GraalVM. Mockito [recommends](https://github.com/mockito/mockito/releases/tag/v5.0.0) using subclass mock maker with GraalVM. Therefore `generate-graalvm-files` profile uses subclass mock maker instead of inline mock maker. + - Powertools uses Mockito 5.x which uses "inline mock maker" as the default. This mock maker does not play well with GraalVM. Mockito [recommends](https://github.com/mockito/mockito/releases/tag/v5.0.0) using subclass mock maker with GraalVM. Therefore the `native` profile adds the `mockito-subclass` dependency where needed. - Subclass mock maker does not support testing static methods. Tests have therefore been modified to use [JUnit Pioneer](https://junit-pioneer.org/docs/environment-variables/) to inject the environment variables in the scope of the test's execution. -2. **Log4j Compatibility** +2. **Unsafe Allocation Tracing** + - GraalVM 21.0.10+ requires `"unsafeAllocated": true` in `reflect-config.json` for classes instantiated via `Unsafe.allocateInstance()`. Mockito uses Objenesis which relies on this. + - The `enableExperimentalUnsafeAllocationTracing` option is enabled in the root `pluginManagement` agent configuration to address this. + +3. **Log4j Compatibility** - Version 2.22.1 fails with this error ``` java.lang.InternalError: com.oracle.svm.core.jdk.UnsupportedFeatureError: Defining hidden classes at runtime is not supported. ``` - This has been [fixed](https://github.com/apache/logging-log4j2/discussions/2364#discussioncomment-8950077) in Log4j 2.24.x. PT has been updated to use this version of Log4j -3. **Test Class Organization** +4. **Test Class Organization** - **Issue**: Anonymous inner classes and lambda expressions in Mockito matchers cause `NoSuchMethodError` in GraalVM native tests - **Solution**: - Extract static inner test classes to separate concrete classes in the same package as the class under test @@ -72,12 +83,12 @@ java.lang.InternalError: com.oracle.svm.core.jdk.UnsupportedFeatureError: Defini }) ``` -4. **Package Visibility Issues** +5. **Package Visibility Issues** - **Issue**: Test handler classes cannot access package-private methods when placed in subpackages - **Solution**: Place test handler classes in the same package as the class under test, not in subpackages like `handlers/` - **Example**: Use `software.amazon.lambda.powertools.cloudformation` instead of `software.amazon.lambda.powertools.cloudformation.handlers` -5. **Test Stubs Best Practice** +6. **Test Stubs Best Practice** - **Best Practice**: Avoid mocking where possible and use concrete test stubs provided by `powertools-common` package - **Solution**: Use `TestLambdaContext` and other test stubs from `powertools-common` test-jar instead of Mockito mocks - **Implementation**: Add `powertools-common` test-jar dependency and replace `mock(Context.class)` with `new TestLambdaContext()` diff --git a/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProduct.java b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProduct.java index 196c0fa52..99c490869 100644 --- a/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProduct.java +++ b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProduct.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: ProtobufProduct.proto -// Protobuf Java Version: 4.33.2 +// Protobuf Java Version: 4.33.4 package org.demo.kafka.protobuf; @@ -19,7 +19,7 @@ public final class ProtobufProduct extends com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, /* minor= */ 33, - /* patch= */ 2, + /* patch= */ 4, /* suffix= */ "", "ProtobufProduct"); } diff --git a/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOrBuilder.java b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOrBuilder.java index 714a2c110..462d3a66d 100644 --- a/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOrBuilder.java +++ b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: ProtobufProduct.proto -// Protobuf Java Version: 4.33.2 +// Protobuf Java Version: 4.33.4 package org.demo.kafka.protobuf; diff --git a/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOuterClass.java b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOuterClass.java index abefa922f..3edc97e12 100644 --- a/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOuterClass.java +++ b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOuterClass.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: ProtobufProduct.proto -// Protobuf Java Version: 4.33.2 +// Protobuf Java Version: 4.33.4 package org.demo.kafka.protobuf; @@ -13,7 +13,7 @@ private ProtobufProductOuterClass() {} com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, /* minor= */ 33, - /* patch= */ 2, + /* patch= */ 4, /* suffix= */ "", "ProtobufProductOuterClass"); } diff --git a/pom.xml b/pom.xml index 30bc6e334..5564d4b57 100644 --- a/pom.xml +++ b/pom.xml @@ -117,6 +117,7 @@ 5.21.0 2.3.0 1.5.0 + 0.11.5 @@ -450,6 +451,45 @@ exec-maven-plugin 3.6.2 + + org.graalvm.buildtools + native-maven-plugin + ${native-maven-plugin.version} + true + + + test-native + + test + + test + + + + + true + + true + true + + + + main + + false + + + + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + --no-fallback + --verbose + --native-image-info + -H:+UnlockExperimentalVMOptions + -H:+ReportExceptionStackTraces + + + @@ -635,6 +675,29 @@ + + + native + + + + org.apache.maven.plugins + maven-surefire-plugin + + + --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED + + + + + + diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index cb06dc1f3..4c3d2ac70 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -120,33 +120,7 @@ - generate-graalvm-files - - - org.mockito - mockito-subclass - test - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - -Dorg.graalvm.nativeimage.imagecode=agent - -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-cloudformation,experimental-class-define-support - --add-opens java.base/java.util=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED - - - - - - - - graalvm-native + native org.mockito @@ -159,28 +133,15 @@ org.graalvm.buildtools native-maven-plugin - 0.11.2 - true - - - test-native - - test - - test - - powertools-cloudformation - - --add-opens java.base/java.util=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED + + + src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-cloudformation + + + --enable-url-protocols=http - --no-fallback - --verbose - --native-image-info - -H:+UnlockExperimentalVMOptions - -H:+ReportExceptionStackTraces diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 75ef10beb..cd43df238 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -93,33 +93,7 @@ - generate-graalvm-files - - - org.mockito - mockito-subclass - test - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - -Dorg.graalvm.nativeimage.imagecode=agent - -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-common,experimental-class-define-support - --add-opens java.base/java.util=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED - - - - - - - - graalvm-native + native org.mockito @@ -132,28 +106,13 @@ org.graalvm.buildtools native-maven-plugin - 0.11.2 - true - - - test-native - - test - - test - - powertools-common - - --add-opens java.base/java.util=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED - --no-fallback - --verbose - --native-image-info - -H:+UnlockExperimentalVMOptions - -H:+ReportExceptionStackTraces - + + + src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-common + + diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index 4cba1956f..0676be3fe 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -75,33 +75,7 @@ - generate-graalvm-files - - - org.mockito - mockito-subclass - test - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - -Dorg.graalvm.nativeimage.imagecode=agent - -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-idempotency-core,experimental-class-define-support - --add-opens java.base/java.util=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED - - - - - - - - graalvm-native + native org.mockito @@ -114,28 +88,13 @@ org.graalvm.buildtools native-maven-plugin - 0.11.2 - true - - - test-native - - test - - test - - powertools-idempotency-core - - --add-opens java.base/java.util=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED - --no-fallback - --verbose - --native-image-info - -H:+UnlockExperimentalVMOptions - -H:+ReportExceptionStackTraces - + + + src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-idempotency-core + + diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index d223e0d2f..e7101da6f 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -107,7 +107,7 @@ - generate-graalvm-files + native org.junit.jupiter @@ -141,28 +114,13 @@ org.graalvm.buildtools native-maven-plugin - 0.11.2 - true - - - test-native - - test - - test - - powertools-parameters-appconfig - - --add-opens java.base/java.util=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED - --no-fallback - --verbose - --native-image-info - -H:+UnlockExperimentalVMOptions - -H:+ReportExceptionStackTraces - + + + src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-appconfig + + diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index eb5604046..ff4be1ab6 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -96,40 +96,13 @@ - generate-graalvm-files + native org.mockito mockito-subclass test - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - -Dorg.graalvm.nativeimage.imagecode=agent - -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-dynamodb,experimental-class-define-support - --add-opens java.base/java.util=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED - - - - - - - - graalvm-native - - - org.mockito - mockito-subclass - test - - org.junit.jupiter @@ -142,28 +115,13 @@ org.graalvm.buildtools native-maven-plugin - 0.11.2 - true - - - test-native - - test - - test - - powertools-parameters-dynamodb - - --add-opens java.base/java.util=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED - --no-fallback - --verbose - --native-image-info - -H:+UnlockExperimentalVMOptions - -H:+ReportExceptionStackTraces - + + + src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-dynamodb + + diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index b9535269e..2dddedec2 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -96,40 +96,13 @@ - generate-graalvm-files + native org.mockito mockito-subclass test - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - -Dorg.graalvm.nativeimage.imagecode=agent - -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-secrets,experimental-class-define-support - --add-opens java.base/java.util=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED - - - - - - - - graalvm-native - - - org.mockito - mockito-subclass - test - - org.junit.jupiter @@ -142,28 +115,13 @@ org.graalvm.buildtools native-maven-plugin - 0.11.2 - true - - - test-native - - test - - test - - powertools-parameters-secrets - - --add-opens java.base/java.util=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED - --no-fallback - --verbose - --native-image-info - -H:+UnlockExperimentalVMOptions - -H:+ReportExceptionStackTraces - + + + src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-secrets + + diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index e0253e10b..fac44f57f 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -109,40 +109,13 @@ - generate-graalvm-files + native org.mockito mockito-subclass test - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - -Dorg.graalvm.nativeimage.imagecode=agent - -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-ssm,experimental-class-define-support - --add-opens java.base/java.util=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED - - - - - - - - graalvm-native - - - org.mockito - mockito-subclass - test - - org.junit.jupiter @@ -155,28 +128,13 @@ org.graalvm.buildtools native-maven-plugin - 0.11.2 - true - - - test-native - - test - - test - - powertools-parameters-ssm - - --add-opens java.base/java.util=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED - --no-fallback - --verbose - --native-image-info - -H:+UnlockExperimentalVMOptions - -H:+ReportExceptionStackTraces - + + + src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters-ssm + + diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index fa2542730..289a86ecb 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -97,40 +97,13 @@ - generate-graalvm-files + native org.mockito mockito-subclass test - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - -Dorg.graalvm.nativeimage.imagecode=agent - -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters,experimental-class-define-support - --add-opens java.base/java.util=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED - - - - - - - - graalvm-native - - - org.mockito - mockito-subclass - test - - org.junit.jupiter @@ -143,28 +116,13 @@ org.graalvm.buildtools native-maven-plugin - 0.11.2 - true - - - test-native - - test - - test - - powertools-parameters - - --add-opens java.base/java.util=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED - --no-fallback - --verbose - --native-image-info - -H:+UnlockExperimentalVMOptions - -H:+ReportExceptionStackTraces - + + + src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-parameters + + diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 81603cd4f..45a724ba6 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -101,50 +101,19 @@ - generate-graalvm-files - - - - org.apache.maven.plugins - maven-surefire-plugin - - - -Dorg.graalvm.nativeimage.imagecode=agent - -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization,experimental-class-define-support - - - - - - - graalvm-native + native org.graalvm.buildtools native-maven-plugin - 0.11.2 - true - - - test-native - - test - - test - - powertools-serialization - - --add-opens java.base/java.util=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED - --no-fallback - --verbose - --native-image-info - -H:+UnlockExperimentalVMOptions - -H:+ReportExceptionStackTraces - + + + src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-serialization + + diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index a9bb8e61e..b70c23262 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -172,33 +172,7 @@ - generate-graalvm-files - - - org.mockito - mockito-subclass - test - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - -Dorg.graalvm.nativeimage.imagecode=agent - -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-tracing,experimental-class-define-support - --add-opens java.base/java.util=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED - - - - - - - - graalvm-native + native org.mockito @@ -211,28 +185,15 @@ org.graalvm.buildtools native-maven-plugin - 0.11.2 - true - - - test-native - - test - - test - - powertools-tracing - - --add-opens java.base/java.util=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED + + + src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-tracing + + + --enable-url-protocols=http - --no-fallback - --verbose - --native-image-info - -H:+UnlockExperimentalVMOptions - -H:+ReportExceptionStackTraces