Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/check-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,11 @@ jobs:
- name: Setup GraalVM
uses: graalvm/setup-graalvm@27b360dbb857524eaf0b4c4cef364d38e5b4f173 # v1.6.7
with:
# Pinned to 21.0.8 due to unsafeAllocated enforcement in 21.0.10+. See #2416
java-version: "21.0.8"
java-version: "25"
# Pinned to the GraalVM 25.4.4.1.1 innovation release. GraalVM 25.0 LTS cannot read Java 25 class files
# for predefined classes (oracle/graal#12723). Move back to the LTS release once the fix is backported.
# See #2416
version: "25.4.4.1.1"
distribution: "graalvm"
cache: maven
- id: graalvm-native-test
Expand Down
17 changes: 11 additions & 6 deletions GraalVM.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
This documentation provides guidance for adding GraalVM support for AWS Lambda Powertools Java modules and using the modules in Lambda functions.

## Prerequisites
- GraalVM 21+ installation
- GraalVM 25 installation. To run the native tests of modules that use Mockito, use a GraalVM 25 innovation release (see [Known Issues and Solutions](#known-issues-and-solutions)). The CI `graalvm-build` job in [check-build.yml](.github/workflows/check-build.yml) runs the native tests on Oracle GraalVM 25.4.4.1.1.
- The `sam-graalvm` example Dockerfiles use the GraalVM for JDK 25 LTS release from `https://download.oracle.com/graalvm/25/latest/`. They only build native images of the example functions and do not run the native tests with the tracing agent, so the known issue does not affect them.
- Maven 3.x

## General Implementation Steps
Expand Down Expand Up @@ -57,17 +58,21 @@ mvn -Pnative test
- 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. **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.
- GraalVM 21.0.10 and later, including GraalVM 25, require `"unsafeAllocated": true` in the reachability metadata 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**
3. **Predefined Classes on GraalVM 25.0**
- **Issue**: On GraalVM 25.0.x, native test image builds fail with `Unsupported class file major version 69` in `ClassPredefinitionFeature`. The tracing agent records the classes that Byte Buddy generates for Mockito mocks at runtime (`enableExperimentalPredefinedClasses`). On JDK 25, these classes use class file version 69, which the ASM version shaded in GraalVM 25.0 cannot read ([oracle/graal#12723](https://github.com/oracle/graal/issues/12723)).
- **Solution**: Use a GraalVM 25 innovation release (25.1 or later), which reads class files with the Java Class-File API. The CI `graalvm-build` job is pinned to GraalVM 25.4.4.1.1.

4. **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

4. **Test Class Organization**
5. **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
Expand All @@ -83,12 +88,12 @@ java.lang.InternalError: com.oracle.svm.core.jdk.UnsupportedFeatureError: Defini
})
```

5. **Package Visibility Issues**
6. **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`

6. **Test Stubs Best Practice**
7. **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()`
Expand Down
2 changes: 1 addition & 1 deletion docs/FAQs.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ Use the `native-maven-plugin` to build the native image. You can do this by addi
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>0.10.1</version>
<version>1.1.14</version>
<extensions>true</extensions>
<executions>
<execution>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Use the official AWS SAM base image for Java 21
FROM public.ecr.aws/sam/build-java21@sha256:a5554d68374e19450c6c88448516ac95a9acedc779f318040f5c230134b4e461
# Use the official AWS SAM base image for Java 25
FROM public.ecr.aws/sam/build-java25@sha256:35c398d9eaadd173262573be05ba7e1c3eda552a5bd320659b7b1442632b47df

# Install GraalVM dependencies
RUN curl -4 -L curl https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz
RUN mv graalvm-jdk-21.* /usr/lib/graalvm
RUN curl -4 -L https://download.oracle.com/graalvm/25/latest/graalvm-jdk-25_linux-x64_bin.tar.gz | tar -xvz
RUN mv graalvm-jdk-25.* /usr/lib/graalvm

# Make native image and mvn available on CLI
RUN ln -s /usr/lib/graalvm/bin/native-image /usr/bin/native-image
Expand Down
2 changes: 1 addition & 1 deletion examples/powertools-examples-cloudformation/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>0.11.5</version>
<version>1.1.14</version>
<extensions>true</extensions>
<executions>
<execution>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#Use the official AWS SAM base image for Java 21
FROM public.ecr.aws/sam/build-java21@sha256:a5554d68374e19450c6c88448516ac95a9acedc779f318040f5c230134b4e461
#Use the official AWS SAM base image for Java 25
FROM public.ecr.aws/sam/build-java25@sha256:35c398d9eaadd173262573be05ba7e1c3eda552a5bd320659b7b1442632b47df

#Install GraalVM dependencies
RUN curl -4 -L curl https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz
RUN mv graalvm-jdk-21.* /usr/lib/graalvm
RUN curl -4 -L https://download.oracle.com/graalvm/25/latest/graalvm-jdk-25_linux-x64_bin.tar.gz | tar -xvz
RUN mv graalvm-jdk-25.* /usr/lib/graalvm

#Make native image and mvn available on CLI
RUN ln -s /usr/lib/graalvm/bin/native-image /usr/bin/native-image
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>0.11.5</version>
<version>1.1.14</version>
<extensions>true</extensions>
<executions>
<execution>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Use the official AWS SAM base image for Java 21
FROM public.ecr.aws/sam/build-java21@sha256:a5554d68374e19450c6c88448516ac95a9acedc779f318040f5c230134b4e461
# Use the official AWS SAM base image for Java 25
FROM public.ecr.aws/sam/build-java25@sha256:35c398d9eaadd173262573be05ba7e1c3eda552a5bd320659b7b1442632b47df

# Install GraalVM dependencies
RUN curl -4 -L curl https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz
RUN mv graalvm-jdk-21.* /usr/lib/graalvm
RUN curl -4 -L https://download.oracle.com/graalvm/25/latest/graalvm-jdk-25_linux-x64_bin.tar.gz | tar -xvz
RUN mv graalvm-jdk-25.* /usr/lib/graalvm

# Make native image and mvn available on CLI
RUN ln -s /usr/lib/graalvm/bin/native-image /usr/bin/native-image
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>0.11.5</version>
<version>1.1.14</version>
<extensions>true</extensions>
<executions>
<execution>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#Use the official AWS SAM base image for Java 21
FROM public.ecr.aws/sam/build-java21@sha256:a5554d68374e19450c6c88448516ac95a9acedc779f318040f5c230134b4e461
#Use the official AWS SAM base image for Java 25
FROM public.ecr.aws/sam/build-java25@sha256:35c398d9eaadd173262573be05ba7e1c3eda552a5bd320659b7b1442632b47df

#Install GraalVM dependencies
RUN curl -4 -L curl https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz
RUN mv graalvm-jdk-21.* /usr/lib/graalvm
RUN curl -4 -L https://download.oracle.com/graalvm/25/latest/graalvm-jdk-25_linux-x64_bin.tar.gz | tar -xvz
RUN mv graalvm-jdk-25.* /usr/lib/graalvm

#Make native image and mvn available on CLI
RUN ln -s /usr/lib/graalvm/bin/native-image /usr/bin/native-image
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>0.11.5</version>
<version>1.1.14</version>
<extensions>true</extensions>
<executions>
<execution>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#Use the official AWS SAM base image for Java 21
FROM public.ecr.aws/sam/build-java21@sha256:a5554d68374e19450c6c88448516ac95a9acedc779f318040f5c230134b4e461
#Use the official AWS SAM base image for Java 25
FROM public.ecr.aws/sam/build-java25@sha256:35c398d9eaadd173262573be05ba7e1c3eda552a5bd320659b7b1442632b47df

#Install GraalVM dependencies
RUN curl -4 -L curl https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz
RUN mv graalvm-jdk-21.* /usr/lib/graalvm
RUN curl -4 -L https://download.oracle.com/graalvm/25/latest/graalvm-jdk-25_linux-x64_bin.tar.gz | tar -xvz
RUN mv graalvm-jdk-25.* /usr/lib/graalvm

#Make native image and mvn available on CLI
RUN ln -s /usr/lib/graalvm/bin/native-image /usr/bin/native-image
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>0.11.5</version>
<version>1.1.14</version>
<extensions>true</extensions>
<executions>
<execution>
Expand Down
57 changes: 22 additions & 35 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
<mockito-junit-jupiter.version>5.23.0</mockito-junit-jupiter.version>
<junit-pioneer.version>2.3.0</junit-pioneer.version>
<crac.version>1.5.0</crac.version>
<native-maven-plugin.version>0.11.5</native-maven-plugin.version>
<native-maven-plugin.version>1.1.14</native-maven-plugin.version>

<!-- As we have a .mvn directory at the root of the project, this will evaluate to the root directory
regardless of where maven is run - sub-module, or root. -->
Expand Down Expand Up @@ -502,6 +502,14 @@
<buildArg>--native-image-info</buildArg>
<buildArg>-H:+UnlockExperimentalVMOptions</buildArg>
<buildArg>-H:+ReportExceptionStackTraces</buildArg>
<!--
Byte Buddy loads the Mockito mock classes with Class.forName on its own class loader.
Since GraalVM 25.1, Class.forName ignores the class loader by default, so the
predefined mock classes are never defined. This option restores the class loader
delegation. It is equivalent to the class-for-name-respects-class-loader future default,
which GraalVM 25.0 does not support.
-->
<buildArg>-H:+ClassForNameRespectsClassLoader</buildArg>
</buildArgs>
</configuration>
</plugin>
Expand Down Expand Up @@ -690,45 +698,24 @@
</plugins>
</build>
</profile>
<!--
On GraalVM for JDK 21 and earlier, the junit-platform-native feature initializes a hardcoded list of
JUnit classes at build time. native-maven-plugin 0.11.5 does not list the following JUnit 6 classes,
which breaks native test image builds. Newer native-build-tools releases include them
(graalvm/native-build-tools#794). Remove this profile after modernizing the GraalVM native-image test
infrastructure and upgrading native-maven-plugin to 1.x:
- https://github.com/aws-powertools/powertools-lambda-java/issues/2416
- https://github.com/aws-powertools/powertools-lambda-java/pull/2661
-->
<profile>
<id>native-junit6-jdk21</id>
<activation>
<jdk>(,22)</jdk>
</activation>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<configuration>
<buildArgs combine.children="append">
<buildArg>--initialize-at-build-time=org.junit.jupiter.engine.discovery.MethodSegmentResolver,org.junit.platform.launcher.core.DiscoveryIssueReportingDiscoveryListener</buildArg>
</buildArgs>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</profile>
<!--
The native profile configures surefire to pass add-opens flags via JDK_JAVA_OPTIONS.
This is necessary because the native-maven-plugin's NativeExtension overwrites surefire's
argLine when -Dagent=true is set (it adds its own argLine element for the tracing agent),
which causes the add-opens flags from the jdk16 profile to be lost.
JDK_JAVA_OPTIONS is picked up by the JVM automatically, bypassing the argLine override.
When -Dagent=true is set, the native-maven-plugin's NativeExtension appends the tracing agent to the
existing surefire argLine, so the add-opens flags from the jdk16 profile are kept. native-maven-plugin
0.11.x replaced the argLine instead and dropped them. JDK_JAVA_OPTIONS is no longer required with 1.x,
but it is kept as a safeguard: the JVM picks it up automatically, so the tests get the add-opens flags
regardless of how the argLine is built.

The native profile also skips JaCoCo. Because the tracing agent is appended to the existing argLine,
the JaCoCo agent would instrument classes that Byte Buddy defines at runtime before the tracing agent
records them as predefined classes. The native test image then rejects the uninstrumented classes at
runtime because their hash does not match.
-->
<profile>
<id>native</id>
<properties>
<jacoco.skip>true</jacoco.skip>
</properties>
<build>
<plugins>
<plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
URL url = files.nextElement();
URLConnection conn = url.openConnection();
conn.setUseCaches(false);
InputStream is = conn.getInputStream();

Check failure on line 51 in powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/ClassPreLoader.java

View workflow job for this annotation

GitHub Actions / pmd_analyse

Ensure that resources like this InputStream object are closed after use

Ensure that resources (like `java.sql.Connection`, `java.sql.Statement`, and `java.sql.ResultSet` objects and any subtype of `java.lang.AutoCloseable`) are always closed after use. Failing to do so might result in resource leaks. Note: It suffices to configure the super type, e.g. `java.lang.AutoCloseable`, so that this rule automatically triggers on any subtype (e.g. `java.io.FileInputStream`). Additionally specifying `java.sql.Connection` helps in detecting the types, if the type resolution / auxclasspath is not correctly setup. Note: Since PMD 6.16.0 the default value for the property `types` contains `java.lang.AutoCloseable` and detects now cases where the standard `java.io.*Stream` classes are involved. In order to restore the old behaviour, just remove "AutoCloseable" from the types. The property `allowedResourceMethodPatterns` can be used to specify method invocation patterns that return resources which are managed externally and don't need to be closed by the caller. This is useful for servlet-related streams like `HttpServletRequest.getReader()` or `HttpServletResponse.getWriter()`, which are managed by the servlet container, and for mocking frameworks, whose mocks are not real resources even when the mocked type implements `java.lang.AutoCloseable`. The patterns use InvocationMatcher syntax (e.g., `javax.servlet.ServletRequest#getReader()`). The defaults cover the servlet API and Mockito's `mock()`. Mocks created by other frameworks can be allowed by adding their factory methods, for example `org.easymock.EasyMock#createMock(_*)`, `org.jmock.Mockery#mock(_*)` or `mockit.Mocked#new(_*)`. Note that `Mockito.spy()` and `Mockito.mockStatic()` are intentionally not among the defaults. A spy calls through to the real object by default, so either the spy or the object it wraps still has to be closed; `mockStatic()` returns a `MockedStatic`, which has to be closed as well. A violation in either case is a true positive. CloseResource (Priority: 1, Ruleset: Error Prone) https://docs.pmd-code.org/snapshot/pmd_rules_java_errorprone.html#closeresource
preloadClassesFromStream(is);
}
} catch (IOException ignored) {
Expand Down Expand Up @@ -96,6 +96,11 @@
} catch (ClassNotFoundException e) {
// No action is required if the class with given name cannot be found
return false;
} catch (LinkageError e) {
// A class can fail to link or initialize in the current runtime, e.g. a JDK class that needs a native
// library that is not available in a GraalVM native image. Skip it and continue with the next class.
LOG.debug("SnapStart priming: failed to preload class {}", className, e);
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
class ClassPreLoaderTest {

// Making this volatile so the Thread Context doesn't need any special handling
static volatile boolean dummyClassLoaded = false;

Check failure on line 10 in powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/ClassPreLoaderTest.java

View workflow job for this annotation

GitHub Actions / pmd_analyse

Use of modifier volatile is not recommended.

The `volatile` modifier is an extremely low-level concurrency tool (like `notify` and `wait`). It makes sure that all threads see a consistent value for a field, and it establishes a happens-before relationship: everything the writing thread did before the volatile write becomes visible to any thread that afterwards reads the field. What `volatile` does **not** do: * It doesn't make updates atomic. Read and write remain separate operations, so `counter++` is a read, an increment and a write - two threads can interleave and lose an update. The same applies to every check-then-act idiom. * On a reference or array field, it only covers the reference itself. Writes to the referenced object's fields, or to the array's elements, are not covered. Using `volatile` correctly requires more knowledge of the Java Memory Model than most Java developers have. Therefore, most Java developers shouldn't use it. Instead, use the higher-level classes from `java.util.concurrent`, especially the Atomic wrapper classes: * instead of a volatile `boolean`, `int`, `long` or reference: `AtomicBoolean`, `AtomicInteger`, `AtomicLong`, `AtomicReference` * instead of a volatile array: `AtomicIntegerArray`, `AtomicLongArray`, `AtomicReferenceArray` * for counters under contention: `LongAdder`, `DoubleAdder` * for a value that never changes after construction: `final` * for shared collections: the concurrent collections, e.g. `ConcurrentHashMap` A related trap concerns 64-bit fields: a write to a non-volatile `long` or `double` field is not required to be atomic (JLS §17.7). The JVM may split it into two separate 32-bit writes, so another thread can read a value assembled from two different writes - a value nobody ever wrote. Declaring the field `volatile` does fix this specific problem, but it still leaves `++` and `+=` non-atomic. `AtomicLong` fixes both and is the better default. (There is no `AtomicDouble` in the JDK; use `DoubleAdder` or an `AtomicReference<Double>`.) Note that this rule allows no exceptions. This is intentional. Valid uses exist but are rare (e.g. {% rule DoubleCheckedLocking %}). If yours is one of them, suppress the violation explicitly with `// NOPMD` or `@SuppressWarnings("PMD.AvoidUsingVolatile")` and a comment explaining why. AvoidUsingVolatile (Priority: 1, Ruleset: Multithreading) https://docs.pmd-code.org/snapshot/pmd_rules_java_multithreading.html#avoidusingvolatile
static volatile boolean leadingSpaceDummyClassLoaded = false;

Check failure on line 11 in powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/ClassPreLoaderTest.java

View workflow job for this annotation

GitHub Actions / pmd_analyse

Use of modifier volatile is not recommended.

The `volatile` modifier is an extremely low-level concurrency tool (like `notify` and `wait`). It makes sure that all threads see a consistent value for a field, and it establishes a happens-before relationship: everything the writing thread did before the volatile write becomes visible to any thread that afterwards reads the field. What `volatile` does **not** do: * It doesn't make updates atomic. Read and write remain separate operations, so `counter++` is a read, an increment and a write - two threads can interleave and lose an update. The same applies to every check-then-act idiom. * On a reference or array field, it only covers the reference itself. Writes to the referenced object's fields, or to the array's elements, are not covered. Using `volatile` correctly requires more knowledge of the Java Memory Model than most Java developers have. Therefore, most Java developers shouldn't use it. Instead, use the higher-level classes from `java.util.concurrent`, especially the Atomic wrapper classes: * instead of a volatile `boolean`, `int`, `long` or reference: `AtomicBoolean`, `AtomicInteger`, `AtomicLong`, `AtomicReference` * instead of a volatile array: `AtomicIntegerArray`, `AtomicLongArray`, `AtomicReferenceArray` * for counters under contention: `LongAdder`, `DoubleAdder` * for a value that never changes after construction: `final` * for shared collections: the concurrent collections, e.g. `ConcurrentHashMap` A related trap concerns 64-bit fields: a write to a non-volatile `long` or `double` field is not required to be atomic (JLS §17.7). The JVM may split it into two separate 32-bit writes, so another thread can read a value assembled from two different writes - a value nobody ever wrote. Declaring the field `volatile` does fix this specific problem, but it still leaves `++` and `+=` non-atomic. `AtomicLong` fixes both and is the better default. (There is no `AtomicDouble` in the JDK; use `DoubleAdder` or an `AtomicReference<Double>`.) Note that this rule allows no exceptions. This is intentional. Valid uses exist but are rare (e.g. {% rule DoubleCheckedLocking %}). If yours is one of them, suppress the violation explicitly with `// NOPMD` or `@SuppressWarnings("PMD.AvoidUsingVolatile")` and a comment explaining why. AvoidUsingVolatile (Priority: 1, Ruleset: Multithreading) https://docs.pmd-code.org/snapshot/pmd_rules_java_multithreading.html#avoidusingvolatile

/**
* Dummy class to be loaded by ClassPreLoader in test.
Expand All @@ -34,6 +34,19 @@
}
}

/**
* Dummy class whose static initializer fails. The class name is referenced before {@link DummyClass} in
* <i>powertools-common/src/test/resources/classesloaded.txt</i>.
* This class is used to verify that the ClassPreLoader continues after a class fails to initialize.
*/
static class FailingInitializerDummyClass {

Check failure on line 42 in powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/ClassPreLoaderTest.java

View workflow job for this annotation

GitHub Actions / pmd_analyse

All members are static. Consider adding a private no-args constructor to prevent instantiation.

A class is a utility class, if and only if it fulfills ALL the following criteria: * ALL member functions, member variables, nested classes, and initializers are static. * The class has at least one member function, member variable, or nested class that is not private. * The class is a concrete class (neither abstract nor an interface). * The class has no superclasses and implements no interfaces. * The class has no main method. Utility classes should not be instantiable. Make sure that the only constructor is a private no-args constructor to enforce this. (Note, that this rule was known as UseSingleton before PMD 5.1.0 and as UseUtilityClass before PMD 7.27.0). UseUtilityClass (Priority: 1, Ruleset: Design) https://docs.pmd-code.org/snapshot/pmd_rules_java_design.html#instantiableutilityclass
static final Object VALUE = fail();

private static Object fail() {
throw new IllegalStateException("Failing static initializer");
}
}

@Test
void preloadClasses_shouldIgnoreInvalidClassesAndLoadValidClasses() {

Expand All @@ -43,6 +56,8 @@
// preloadClasses once and asserts on all classes listed in classesloaded.txt together.
// powertools-common/src/test/resources/classesloaded.txt has a class that does not exist.
// Verify that the missing class did not throw any exception
// It also has a class whose static initializer fails. Verify that the ExceptionInInitializerError
// did not stop the preloading of the following classes
assertDoesNotThrow(ClassPreLoader::preloadClasses);

// When the classloaded.txt is a mixed bag of valid and invalid classes, valid classes must load
Expand Down
1 change: 1 addition & 0 deletions powertools-common/src/test/resources/classesloaded.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
software.amazon.lambda.powertools.common.internal.NonExistingClass
software.amazon.lambda.powertools.common.internal.ClassPreLoaderTest$FailingInitializerDummyClass
software.amazon.lambda.powertools.common.internal.ClassPreLoaderTest$DummyClass
software.amazon.lambda.powertools.common.internal.ClassPreLoaderTest$LeadingSpaceDummyClass
2 changes: 1 addition & 1 deletion powertools-e2e-tests/handlers/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>0.11.5</version>
<version>1.1.14</version>
<extensions>true</extensions>
<executions>
<execution>
Expand Down
Loading