From 77344eb5cd897f4b0856569139c35610fd7e3ede Mon Sep 17 00:00:00 2001 From: Davide Melfi Date: Thu, 19 Mar 2026 19:06:24 +0000 Subject: [PATCH 1/5] ci: dependent library test fan out --- .github/workflows/aws-lambda-java-tests.yml | 3 ++- .github/workflows/runtime-interface-client_pr.yml | 11 +++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/aws-lambda-java-tests.yml b/.github/workflows/aws-lambda-java-tests.yml index 324c4451..e651c023 100644 --- a/.github/workflows/aws-lambda-java-tests.yml +++ b/.github/workflows/aws-lambda-java-tests.yml @@ -11,14 +11,15 @@ on: - 'aws-lambda-java-tests/**' - 'aws-lambda-java-events/**' - 'aws-lambda-java-serialization/**' + - 'aws-lambda-java-runtime-interface-client/**' pull_request: branches: [ '*' ] paths: - 'aws-lambda-java-tests/**' - 'aws-lambda-java-events/**' - 'aws-lambda-java-serialization/**' + - 'aws-lambda-java-runtime-interface-client/**' - '.github/workflows/aws-lambda-java-tests.yml' - permissions: contents: read diff --git a/.github/workflows/runtime-interface-client_pr.yml b/.github/workflows/runtime-interface-client_pr.yml index a0d8c6cc..c4faa116 100644 --- a/.github/workflows/runtime-interface-client_pr.yml +++ b/.github/workflows/runtime-interface-client_pr.yml @@ -21,13 +21,16 @@ jobs: smoke-test: runs-on: ubuntu-latest + strategy: + matrix: + java-version: [8, 11, 17, 21, 25] steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Set up JDK 1.8 + - name: Set up JDK ${{ matrix.java-version }} uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 with: - java-version: 8 + java-version: ${{ matrix.java-version }} distribution: corretto cache: maven @@ -43,7 +46,11 @@ jobs: working-directory: ./aws-lambda-java-runtime-interface-client run: make pr env: +<<<<<<< HEAD IS_JAVA_8: true +======= + IS_JAVA_8: ${{ matrix.java-version == 8 }} +>>>>>>> c093beb (ci: dependent library test fan out) build: runs-on: ubuntu-latest From 06831a981b13b817ebb872b056382e4fc15e680b Mon Sep 17 00:00:00 2001 From: Davide Melfi Date: Thu, 19 Mar 2026 19:34:32 +0000 Subject: [PATCH 2/5] test: fix tests that are failing after introducint enum matrix --- .../api/client/ClasspathLoaderTest.java | 57 ++++++++++++------- .../api/client/util/UnsafeUtilTest.java | 39 ++++++++----- 2 files changed, 61 insertions(+), 35 deletions(-) diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/ClasspathLoaderTest.java b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/ClasspathLoaderTest.java index 38147d21..581a6dda 100644 --- a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/ClasspathLoaderTest.java +++ b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/ClasspathLoaderTest.java @@ -6,16 +6,16 @@ package com.amazonaws.services.lambda.runtime.api.client; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledForJreRange; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.io.TempDir; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Path; -import java.util.Collections; -import java.util.Enumeration; import java.util.jar.JarEntry; -import java.util.jar.JarFile; import java.util.jar.JarOutputStream; import static org.junit.jupiter.api.Assertions.*; @@ -27,7 +27,7 @@ void testLoadAllClassesWithNoClasspath() throws IOException { String originalClasspath = System.getProperty("java.class.path"); try { System.clearProperty("java.class.path"); - ClasspathLoader.main(new String[]{}); + ClasspathLoader.main(new String[] {}); } finally { if (originalClasspath != null) { System.setProperty("java.class.path", originalClasspath); @@ -35,13 +35,30 @@ void testLoadAllClassesWithNoClasspath() throws IOException { } } + // On JDK 8-24, new File("").exists() returns false → FileNotFoundException. @Test + @DisabledForJreRange(min = JRE.JAVA_25) void testLoadAllClassesWithEmptyClasspath() { String originalClasspath = System.getProperty("java.class.path"); try { System.setProperty("java.class.path", ""); - assertThrows(FileNotFoundException.class, () -> - ClasspathLoader.main(new String[]{})); + assertThrows(FileNotFoundException.class, () -> ClasspathLoader.main(new String[] {})); + } finally { + if (originalClasspath != null) { + System.setProperty("java.class.path", originalClasspath); + } + } + } + + // On JDK 25+, new File("") resolves to cwd (exists=true, isDirectory=true) → + // skipped with warning, no exception. + @Test + @EnabledForJreRange(min = JRE.JAVA_25) + void testLoadAllClassesWithEmptyClasspathJdk25Plus() throws IOException { + String originalClasspath = System.getProperty("java.class.path"); + try { + System.setProperty("java.class.path", ""); + ClasspathLoader.main(new String[] {}); } finally { if (originalClasspath != null) { System.setProperty("java.class.path", originalClasspath); @@ -54,8 +71,7 @@ void testLoadAllClassesWithInvalidPath() { String originalClasspath = System.getProperty("java.class.path"); try { System.setProperty("java.class.path", "nonexistent/path"); - assertThrows(FileNotFoundException.class, () -> - ClasspathLoader.main(new String[]{})); + assertThrows(FileNotFoundException.class, () -> ClasspathLoader.main(new String[] {})); } finally { if (originalClasspath != null) { System.setProperty("java.class.path", originalClasspath); @@ -69,7 +85,7 @@ void testLoadAllClassesWithValidJar(@TempDir Path tempDir) throws IOException { String originalClasspath = System.getProperty("java.class.path"); try { System.setProperty("java.class.path", jarFile.getAbsolutePath()); - ClasspathLoader.main(new String[]{}); + ClasspathLoader.main(new String[] {}); } finally { if (originalClasspath != null) { System.setProperty("java.class.path", originalClasspath); @@ -82,7 +98,7 @@ void testLoadAllClassesWithDirectory(@TempDir Path tempDir) throws IOException { String originalClasspath = System.getProperty("java.class.path"); try { System.setProperty("java.class.path", tempDir.toString()); - ClasspathLoader.main(new String[]{}); + ClasspathLoader.main(new String[] {}); } finally { if (originalClasspath != null) { System.setProperty("java.class.path", originalClasspath); @@ -94,14 +110,14 @@ void testLoadAllClassesWithDirectory(@TempDir Path tempDir) throws IOException { void testLoadAllClassesWithMultipleEntries(@TempDir Path tempDir) throws IOException { File jarFile1 = createSimpleJar(tempDir, "test1.jar", "TestClass1"); File jarFile2 = createSimpleJar(tempDir, "test2.jar", "TestClass2"); - + String originalClasspath = System.getProperty("java.class.path"); try { - String newClasspath = jarFile1.getAbsolutePath() + - File.pathSeparator + - jarFile2.getAbsolutePath(); + String newClasspath = jarFile1.getAbsolutePath() + + File.pathSeparator + + jarFile2.getAbsolutePath(); System.setProperty("java.class.path", newClasspath); - ClasspathLoader.main(new String[]{}); + ClasspathLoader.main(new String[] {}); } finally { if (originalClasspath != null) { System.setProperty("java.class.path", originalClasspath); @@ -112,7 +128,7 @@ void testLoadAllClassesWithMultipleEntries(@TempDir Path tempDir) throws IOExcep @Test void testLoadAllClassesWithBlocklistedClass(@TempDir Path tempDir) throws IOException { File jarFile = tempDir.resolve("blocklist-test.jar").toFile(); - + try (JarOutputStream jos = new JarOutputStream(new FileOutputStream(jarFile))) { JarEntry blockedEntry = new JarEntry("META-INF/versions/9/module-info.class"); jos.putNextEntry(blockedEntry); @@ -128,8 +144,9 @@ void testLoadAllClassesWithBlocklistedClass(@TempDir Path tempDir) throws IOExce String originalClasspath = System.getProperty("java.class.path"); try { System.setProperty("java.class.path", jarFile.getAbsolutePath()); - ClasspathLoader.main(new String[]{}); - // The test passes if no exception is thrown and the blocklisted class is skipped + ClasspathLoader.main(new String[] {}); + // The test passes if no exception is thrown and the blocklisted class is + // skipped } finally { if (originalClasspath != null) { System.setProperty("java.class.path", originalClasspath); @@ -139,7 +156,7 @@ void testLoadAllClassesWithBlocklistedClass(@TempDir Path tempDir) throws IOExce private File createSimpleJar(Path tempDir, String jarName, String className) throws IOException { File jarFile = tempDir.resolve(jarName).toFile(); - + try (JarOutputStream jos = new JarOutputStream(new FileOutputStream(jarFile))) { // Add a simple non-class file to make it a valid jar JarEntry entry = new JarEntry("com/test/" + className + ".txt"); @@ -147,7 +164,7 @@ private File createSimpleJar(Path tempDir, String jarName, String className) thr jos.write("test content".getBytes()); jos.closeEntry(); } - + return jarFile; } } diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/util/UnsafeUtilTest.java b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/util/UnsafeUtilTest.java index b1f0592f..c4862719 100644 --- a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/util/UnsafeUtilTest.java +++ b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/util/UnsafeUtilTest.java @@ -6,6 +6,8 @@ package com.amazonaws.services.lambda.runtime.api.client.util; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.JRE; import java.lang.reflect.Field; import static org.junit.jupiter.api.Assertions.*; @@ -19,7 +21,7 @@ void testTheUnsafeIsInitialized() { @Test void testThrowException() { Exception testException = new Exception("Test exception"); - + try { UnsafeUtil.throwException(testException); fail("Should have thrown an exception"); @@ -29,22 +31,29 @@ void testThrowException() { } } + // IllegalAccessLogger only exists on JDK 9-16; skipped on JDK 8 (no module + // system) and JDK 17+ (class removed). + @Test + @EnabledForJreRange(min = JRE.JAVA_9, max = JRE.JAVA_16) + void testDisableIllegalAccessWarning() throws Exception { + // We disable the warning log for "jdk.internal.module.IllegalAccessLogger" + UnsafeUtil.disableIllegalAccessWarning(); + + Class illegalAccessLoggerClass = Class.forName("jdk.internal.module.IllegalAccessLogger"); + Field loggerField = illegalAccessLoggerClass.getDeclaredField("logger"); + + // Now we are getting it back with getObjectVolatile and verify that the logger + // is null. We are not using default reflection because that will throw because + // that field is private, defeating the point of the test. + Object loggerValue = UnsafeUtil.TheUnsafe.getObjectVolatile( + illegalAccessLoggerClass, + UnsafeUtil.TheUnsafe.staticFieldOffset(loggerField)); + assertNull(loggerValue); + } + @Test - void testDisableIllegalAccessWarning() { + void testDisableIllegalAccessWarningDoesNotThrow() { assertDoesNotThrow(() -> UnsafeUtil.disableIllegalAccessWarning()); - try { - Class illegalAccessLoggerClass = Class.forName("jdk.internal.module.IllegalAccessLogger"); - Field loggerField = illegalAccessLoggerClass.getDeclaredField("logger"); - loggerField.setAccessible(true); - Object loggerValue = loggerField.get(null); - assertNull(loggerValue); - } catch (ClassNotFoundException e) { - assertTrue(true); - } catch (NoSuchFieldException e) { - assertTrue(true); - } catch (Exception e) { - fail("Unexpected exception: " + e.getMessage()); - } } @Test From a7c2b1c233646757acf7c25ed910752a36ba7109 Mon Sep 17 00:00:00 2001 From: Davide Melfi Date: Fri, 20 Mar 2026 16:38:57 +0000 Subject: [PATCH 3/5] build: upgrading junit-jupiter everywhere --- .../pom.xml | 4 + aws-lambda-java-events/.factorypath | 4 + aws-lambda-java-events/pom.xml | 4 + aws-lambda-java-log4j2/.factorypath | 5 ++ .../pom.xml | 4 + aws-lambda-java-tests/pom.xml | 4 + .../examples/cdk/pom.xml | 4 + jackson-datetime-regression.md | 82 +++++++++++++++++++ .../kinesis-firehose-event-handler/pom.xml | 4 +- 9 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 aws-lambda-java-events/.factorypath create mode 100644 aws-lambda-java-log4j2/.factorypath create mode 100644 jackson-datetime-regression.md diff --git a/aws-lambda-java-events-sdk-transformer/pom.xml b/aws-lambda-java-events-sdk-transformer/pom.xml index 6de599ef..33207b63 100644 --- a/aws-lambda-java-events-sdk-transformer/pom.xml +++ b/aws-lambda-java-events-sdk-transformer/pom.xml @@ -38,8 +38,12 @@ 1.8 1.11.914 2.15.40 +<<<<<<< HEAD 5.12.2 3.5.4 +======= + 5.14.3 +>>>>>>> 7946ec4 (build: upgrading junit-jupiter everywhere) diff --git a/aws-lambda-java-events/.factorypath b/aws-lambda-java-events/.factorypath new file mode 100644 index 00000000..e5972c45 --- /dev/null +++ b/aws-lambda-java-events/.factorypath @@ -0,0 +1,4 @@ + + + + diff --git a/aws-lambda-java-events/pom.xml b/aws-lambda-java-events/pom.xml index c8c40e0c..497a5859 100644 --- a/aws-lambda-java-events/pom.xml +++ b/aws-lambda-java-events/pom.xml @@ -39,7 +39,11 @@ UTF-8 2.20.1 2.40.1 +<<<<<<< HEAD 5.12.2 +======= + 5.14.3 +>>>>>>> 7946ec4 (build: upgrading junit-jupiter everywhere) diff --git a/aws-lambda-java-log4j2/.factorypath b/aws-lambda-java-log4j2/.factorypath new file mode 100644 index 00000000..ddfb3782 --- /dev/null +++ b/aws-lambda-java-log4j2/.factorypath @@ -0,0 +1,5 @@ + + + + + diff --git a/aws-lambda-java-runtime-interface-client/pom.xml b/aws-lambda-java-runtime-interface-client/pom.xml index a09fd3df..b5aa957e 100644 --- a/aws-lambda-java-runtime-interface-client/pom.xml +++ b/aws-lambda-java-runtime-interface-client/pom.xml @@ -37,7 +37,11 @@ 0.8.12 2.4 3.1.1 +<<<<<<< HEAD 5.12.2 +======= + 5.14.3 +>>>>>>> 7946ec4 (build: upgrading junit-jupiter everywhere) 3.4.0 3.5.4 5.9.2 +======= + 5.14.3 +>>>>>>> 7946ec4 (build: upgrading junit-jupiter everywhere) 0.8.7 1.4.0 3.16.1 diff --git a/experimental/aws-lambda-java-profiler/examples/cdk/pom.xml b/experimental/aws-lambda-java-profiler/examples/cdk/pom.xml index 4b46f4e2..d1781bbc 100644 --- a/experimental/aws-lambda-java-profiler/examples/cdk/pom.xml +++ b/experimental/aws-lambda-java-profiler/examples/cdk/pom.xml @@ -11,7 +11,11 @@ UTF-8 2.155.0 [10.0.0,11.0.0) +<<<<<<< HEAD 5.12.2 +======= + 5.14.3 +>>>>>>> 7946ec4 (build: upgrading junit-jupiter everywhere) diff --git a/jackson-datetime-regression.md b/jackson-datetime-regression.md new file mode 100644 index 00000000..45c269dc --- /dev/null +++ b/jackson-datetime-regression.md @@ -0,0 +1,82 @@ +# Jackson 2.18 – `joda.time.DateTime` Deserialization Regression + +## Symptom + +After upgrading Jackson from **2.15.4 → 2.18.6**, deserializing `joda.time.DateTime` throws: + +``` +com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.exc.MismatchedInputException: +Cannot construct instance of `org.joda.time.DateTime` (although at least one Creator exists): +no String-argument constructor/factory method to deserialize from String value ('2016-01-01T23:59:59.000+0000') +``` + +Reproduced by: `EventLoaderTest.testLoadCodeCommitEvent` — `CodeCommitEvent.Record.eventTime` is of type `org.joda.time.DateTime`. + +## Root Cause + +### Background: how `DateTime` deserialization is supposed to work + +`LambdaEventSerializers.serializerFor()` registers a `DateTimeModule` (which extends `JodaModule`) on the mapper before returning the serializer: + +```java +// LambdaEventSerializers.java line 262 +factory.getMapper().registerModules(new DateModule(), new DateTimeModule(classLoader)); +``` + +`DateTimeModule` registers a custom deserializer for `org.joda.time.DateTime` (the customer's unshaded class, via the pom reverse-transform workaround). This deserializer simply calls `DateTime.parse(jsonParser.getValueAsString())` and handles any String input correctly. + +### What broke in Jackson 2.18 + +`JacksonFactory` disables creator auto-detection: + +```java +// JacksonFactory.java line 68 +.disable(MapperFeature.AUTO_DETECT_CREATORS) +``` + +Jackson 2.18 shipped a complete rewrite of `POJOPropertiesCollector` ([databind #4515](https://github.com/FasterXML/jackson-databind/issues/4515)), which moved creator detection logic from `BasicDeserializerFactory` into `POJOPropertiesCollector` and changed when it runs. + +With `AUTO_DETECT_CREATORS` disabled, no creators are found for `DateTime` during bean property collection. In Jackson 2.18's rewritten code path, when no creators are detected for a type, `_findCustomDeserializer()` — the lookup that would find `DateTimeModule`'s registered deserializer — is bypassed. Jackson falls straight through to building a `BeanDeserializer` for `DateTime`. + +The `BeanDeserializer` finds `DateTime()`'s no-arg constructor (which is not gated by `AUTO_DETECT_CREATORS`), so it reports "at least one Creator exists" — but that creator doesn't accept a String, hence the error. + +### Why it worked in 2.15.4 + +In Jackson 2.15.4, `_findCustomDeserializer()` was called **unconditionally**, regardless of whether creator detection found anything. So `DateTimeModule`'s registered deserializer was always found and used. That unconditional call was the fallback that 2.18 removed as part of the `#4515` rewrite. + +## Fix Options + +### Option 1 — Remove `.disable(MapperFeature.AUTO_DETECT_CREATORS)` from `JacksonFactory` + +Simplest fix. Jackson's default is enabled, so this aligns with standard behaviour. Risk: could enable constructor-based creator detection for customer POJOs, potentially changing deserialization semantics for handler inputs that have multi-arg constructors. The original intent behind disabling it is undocumented (present since the initial commit), so the blast radius is unknown. + +To confirm this is the cause: remove the line and run `testLoadCodeCommitEvent`. + +### Option 2 — Use `BeanDeserializerModifier` in `DateTimeModule` ✅ Recommended + +More targeted. Since Jackson 2.18 IS reaching `BeanDeserializer` for `DateTime` (confirmed by the error), `BeanDeserializerModifier.modifyDeserializer()` is called at that point. We can intercept it there and swap in the custom deserializer — without touching `AUTO_DETECT_CREATORS` at all. + +Add the following to the `DateTimeModule` constructor, after the existing `addDeserializer` call: + +```java +this.setDeserializerModifier(new BeanDeserializerModifier() { + @Override + public JsonDeserializer modifyDeserializer(DeserializationConfig config, + BeanDescription beanDesc, JsonDeserializer deserializer) { + if (beanDesc.getBeanClass() == dateTimeClass) { + return getDeserializer(dateTimeClass); + } + return deserializer; + } +}); +``` + +This is scoped only to `DateTime`, leaves `AUTO_DETECT_CREATORS` untouched, and fixes exactly the broken code path without risk of side effects elsewhere. + +## Related Issues + +| Issue | Description | +|-------|-------------| +| [databind #4515](https://github.com/FasterXML/jackson-databind/issues/4515) | Rewrite of `POJOPropertiesCollector` — root cause of the behaviour change | +| [databind #4920](https://github.com/FasterXML/jackson-databind/issues/4920) | Regression introduced by #4515: custom module deserializers bypassed when creator detection finds nothing | +| [databind #4982](https://github.com/FasterXML/jackson-databind/pull/4982) | Partial fix in 2.18.3, does not cover the `AUTO_DETECT_CREATORS = false` case | diff --git a/samples/kinesis-firehose-event-handler/pom.xml b/samples/kinesis-firehose-event-handler/pom.xml index 0db8ed83..4b51c34c 100644 --- a/samples/kinesis-firehose-event-handler/pom.xml +++ b/samples/kinesis-firehose-event-handler/pom.xml @@ -35,9 +35,7 @@ 1.8 1.8 UTF-8 - 5.12.2 - 3.5.4 - + 5.14.3 From 700912808860616f5be409fbad44dc644bde9281 Mon Sep 17 00:00:00 2001 From: Davide Melfi Date: Thu, 19 Mar 2026 17:05:16 +0000 Subject: [PATCH 4/5] build: add a checke that docker exist in the current environment to build jni code --- .gitignore | 15 +++++++++++++++ .../src/main/jni/build-jni-lib.sh | 10 ++++++++++ 2 files changed, 25 insertions(+) diff --git a/.gitignore b/.gitignore index 5a277e5d..3eda5429 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,19 @@ experimental/aws-lambda-java-profiler/integration_tests/helloworld/bin .vscode .kiro build +<<<<<<< HEAD +======= + +# Logs +*.log + +# Maven flatten plugin +.flattened-pom.xml + +# Vim swap files +*.swp +*.swo + +# Tool version manager +>>>>>>> 45b43fe (chore: adding other files to .gitignore) mise.toml diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/build-jni-lib.sh b/aws-lambda-java-runtime-interface-client/src/main/jni/build-jni-lib.sh index b7dbb5a8..d46e3f40 100755 --- a/aws-lambda-java-runtime-interface-client/src/main/jni/build-jni-lib.sh +++ b/aws-lambda-java-runtime-interface-client/src/main/jni/build-jni-lib.sh @@ -3,6 +3,16 @@ set -euo pipefail +# Check if Docker is available and running. This works regardless of how Docker +# is configured (Unix socket, TCP, Docker Desktop, Colima, Rancher, etc.) +if ! docker info >/dev/null 2>&1; then + echo "WARNING: Docker is not available. Skipping JNI native library build." + echo "The native .so libraries will not be included in the build artifacts." + echo "This is fine for local development and running unit tests." + echo "To build the native libraries, ensure Docker is installed and running." + exit 0 +fi + SRC_DIR=$(dirname "$0") DST_DIR=${1} MULTI_ARCH=${2} From b19e7857d865f96e58f627558428dec39af960f7 Mon Sep 17 00:00:00 2001 From: Davide Melfi Date: Fri, 20 Mar 2026 18:02:07 +0000 Subject: [PATCH 5/5] build: removed useless profile specific builds --- .../workflows/runtime-interface-client_pr.yml | 4 - .gitignore | 4 +- .../pom.xml | 7 +- aws-lambda-java-events/.factorypath | 4 - aws-lambda-java-events/pom.xml | 4 - aws-lambda-java-log4j2/.factorypath | 5 -- .../Makefile | 40 +++++---- .../pom.xml | 4 - aws-lambda-java-tests/pom.xml | 4 - .../examples/cdk/pom.xml | 4 - jackson-datetime-regression.md | 82 ------------------- 11 files changed, 27 insertions(+), 135 deletions(-) delete mode 100644 aws-lambda-java-events/.factorypath delete mode 100644 aws-lambda-java-log4j2/.factorypath delete mode 100644 jackson-datetime-regression.md diff --git a/.github/workflows/runtime-interface-client_pr.yml b/.github/workflows/runtime-interface-client_pr.yml index c4faa116..183549ec 100644 --- a/.github/workflows/runtime-interface-client_pr.yml +++ b/.github/workflows/runtime-interface-client_pr.yml @@ -46,11 +46,7 @@ jobs: working-directory: ./aws-lambda-java-runtime-interface-client run: make pr env: -<<<<<<< HEAD - IS_JAVA_8: true -======= IS_JAVA_8: ${{ matrix.java-version == 8 }} ->>>>>>> c093beb (ci: dependent library test fan out) build: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 3eda5429..ded8a5fe 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ dependency-reduced-pom.xml .settings .classpath .project +.factorypath # OSX .DS_Store @@ -38,8 +39,6 @@ experimental/aws-lambda-java-profiler/integration_tests/helloworld/bin .vscode .kiro build -<<<<<<< HEAD -======= # Logs *.log @@ -52,5 +51,4 @@ build *.swo # Tool version manager ->>>>>>> 45b43fe (chore: adding other files to .gitignore) mise.toml diff --git a/aws-lambda-java-events-sdk-transformer/pom.xml b/aws-lambda-java-events-sdk-transformer/pom.xml index 33207b63..a02a80f1 100644 --- a/aws-lambda-java-events-sdk-transformer/pom.xml +++ b/aws-lambda-java-events-sdk-transformer/pom.xml @@ -38,12 +38,9 @@ 1.8 1.11.914 2.15.40 -<<<<<<< HEAD - 5.12.2 - 3.5.4 -======= 5.14.3 ->>>>>>> 7946ec4 (build: upgrading junit-jupiter everywhere) + 3.5.4 + diff --git a/aws-lambda-java-events/.factorypath b/aws-lambda-java-events/.factorypath deleted file mode 100644 index e5972c45..00000000 --- a/aws-lambda-java-events/.factorypath +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/aws-lambda-java-events/pom.xml b/aws-lambda-java-events/pom.xml index 497a5859..ba93601d 100644 --- a/aws-lambda-java-events/pom.xml +++ b/aws-lambda-java-events/pom.xml @@ -39,11 +39,7 @@ UTF-8 2.20.1 2.40.1 -<<<<<<< HEAD - 5.12.2 -======= 5.14.3 ->>>>>>> 7946ec4 (build: upgrading junit-jupiter everywhere) diff --git a/aws-lambda-java-log4j2/.factorypath b/aws-lambda-java-log4j2/.factorypath deleted file mode 100644 index ddfb3782..00000000 --- a/aws-lambda-java-log4j2/.factorypath +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/aws-lambda-java-runtime-interface-client/Makefile b/aws-lambda-java-runtime-interface-client/Makefile index 6c3a268f..78cfa11a 100644 --- a/aws-lambda-java-runtime-interface-client/Makefile +++ b/aws-lambda-java-runtime-interface-client/Makefile @@ -15,57 +15,63 @@ endif # making it possible to publish resulting artifacts to a codeartifact maven repository -include ric-dev-environment/codeartifact-repo.mk -.PHONY: target + target: $(info ${HELP_MESSAGE}) @exit 0 -.PHONY: test + test: mvn test $(EXTRA_LOAD_ARG) -.PHONY: setup-codebuild-agent + setup-codebuild-agent: docker build -t codebuild-agent \ --build-arg ARCHITECTURE=$(ARCHITECTURE_ALIAS) \ - < test/integration/codebuild-local/Dockerfile.agent -.PHONY: test-smoke + test-smoke: setup-codebuild-agent CODEBUILD_IMAGE_TAG=codebuild-agent test/integration/codebuild-local/test_one.sh test/integration/codebuild/buildspec.os.alpine.yml alpine 3.15 corretto11 linux/amd64 CODEBUILD_IMAGE_TAG=codebuild-agent test/integration/codebuild-local/test_one.sh test/integration/codebuild/buildspec.os.alpine.yml alpine 3.15 corretto11 linux/arm64/v8 CODEBUILD_IMAGE_TAG=codebuild-agent test/integration/codebuild-local/test_one.sh test/integration/codebuild/buildspec.os.amazoncorretto.yml amazoncorretto amazoncorretto 11 linux/amd64 CODEBUILD_IMAGE_TAG=codebuild-agent test/integration/codebuild-local/test_one.sh test/integration/codebuild/buildspec.os.amazoncorretto.yml amazoncorretto amazoncorretto 11 linux/arm64/v8 -.PHONY: test-integ + test-integ: setup-codebuild-agent CODEBUILD_IMAGE_TAG=codebuild-agent test/integration/codebuild-local/test_all.sh test/integration/codebuild # Command to run everytime you make changes to verify everything works -.PHONY: dev + dev: test # Verifications to run before sending a pull request -.PHONY: pr + pr: test test-smoke -.PHONY: build + +# The default `mvn clean install` builds all 4 native .so variants (x86_64/aarch64 × glibc/musl) +# and bundles them into a single jar (see build-jni-lib.sh), runs the tests for the host +# environment, and updates coverage data. This is sufficient for what we want to do here. +# +# Running profile-specific builds (e.g., `mvn install -P linux-aarch64`) does NOT test the +# cross-compiled .so — JniHelper.load() tries each .so in order and always loads the first +# one matching the host platform (linux-x86_64 on GitHub Actions runners), so the other +# variants are never exercised. +# +# Platform-specific tests are run through test-smoke via codebuild containers. + build: - mvn clean install $(EXTRA_LOAD_ARG) - mvn install -P linux-x86_64 $(EXTRA_LOAD_ARG) - mvn install -P linux_musl-x86_64 $(EXTRA_LOAD_ARG) - mvn install -P linux-aarch64 $(EXTRA_LOAD_ARG) - mvn install -P linux_musl-aarch64 $(EXTRA_LOAD_ARG) + mvn clean install $(EXTRA_LOAD_ARG) -.PHONY: publish publish: ./ric-dev-environment/publish_snapshot.sh -.PHONY: publish + test-publish: ./ric-dev-environment/test-platform-specific-jar-snapshot.sh -.PHONY: test-rie + test-rie: ./scripts/test-rie.sh "EchoHandler::handleRequest" @@ -80,3 +86,5 @@ TARGETS test Run the Unit tests. test-rie Build and test RIC locally with Lambda Runtime Interface Emulator. (Requires building the project first) endef + +.PHONY: target test setup-codebuild-agent test-smoke test-integ dev pr build publish publish test-rie \ No newline at end of file diff --git a/aws-lambda-java-runtime-interface-client/pom.xml b/aws-lambda-java-runtime-interface-client/pom.xml index b5aa957e..c7e1225a 100644 --- a/aws-lambda-java-runtime-interface-client/pom.xml +++ b/aws-lambda-java-runtime-interface-client/pom.xml @@ -37,11 +37,7 @@ 0.8.12 2.4 3.1.1 -<<<<<<< HEAD - 5.12.2 -======= 5.14.3 ->>>>>>> 7946ec4 (build: upgrading junit-jupiter everywhere) 3.4.0 3.5.4 - 5.9.2 -======= 5.14.3 ->>>>>>> 7946ec4 (build: upgrading junit-jupiter everywhere) 0.8.7 1.4.0 3.16.1 diff --git a/experimental/aws-lambda-java-profiler/examples/cdk/pom.xml b/experimental/aws-lambda-java-profiler/examples/cdk/pom.xml index d1781bbc..d84b2bd1 100644 --- a/experimental/aws-lambda-java-profiler/examples/cdk/pom.xml +++ b/experimental/aws-lambda-java-profiler/examples/cdk/pom.xml @@ -11,11 +11,7 @@ UTF-8 2.155.0 [10.0.0,11.0.0) -<<<<<<< HEAD - 5.12.2 -======= 5.14.3 ->>>>>>> 7946ec4 (build: upgrading junit-jupiter everywhere) diff --git a/jackson-datetime-regression.md b/jackson-datetime-regression.md deleted file mode 100644 index 45c269dc..00000000 --- a/jackson-datetime-regression.md +++ /dev/null @@ -1,82 +0,0 @@ -# Jackson 2.18 – `joda.time.DateTime` Deserialization Regression - -## Symptom - -After upgrading Jackson from **2.15.4 → 2.18.6**, deserializing `joda.time.DateTime` throws: - -``` -com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.exc.MismatchedInputException: -Cannot construct instance of `org.joda.time.DateTime` (although at least one Creator exists): -no String-argument constructor/factory method to deserialize from String value ('2016-01-01T23:59:59.000+0000') -``` - -Reproduced by: `EventLoaderTest.testLoadCodeCommitEvent` — `CodeCommitEvent.Record.eventTime` is of type `org.joda.time.DateTime`. - -## Root Cause - -### Background: how `DateTime` deserialization is supposed to work - -`LambdaEventSerializers.serializerFor()` registers a `DateTimeModule` (which extends `JodaModule`) on the mapper before returning the serializer: - -```java -// LambdaEventSerializers.java line 262 -factory.getMapper().registerModules(new DateModule(), new DateTimeModule(classLoader)); -``` - -`DateTimeModule` registers a custom deserializer for `org.joda.time.DateTime` (the customer's unshaded class, via the pom reverse-transform workaround). This deserializer simply calls `DateTime.parse(jsonParser.getValueAsString())` and handles any String input correctly. - -### What broke in Jackson 2.18 - -`JacksonFactory` disables creator auto-detection: - -```java -// JacksonFactory.java line 68 -.disable(MapperFeature.AUTO_DETECT_CREATORS) -``` - -Jackson 2.18 shipped a complete rewrite of `POJOPropertiesCollector` ([databind #4515](https://github.com/FasterXML/jackson-databind/issues/4515)), which moved creator detection logic from `BasicDeserializerFactory` into `POJOPropertiesCollector` and changed when it runs. - -With `AUTO_DETECT_CREATORS` disabled, no creators are found for `DateTime` during bean property collection. In Jackson 2.18's rewritten code path, when no creators are detected for a type, `_findCustomDeserializer()` — the lookup that would find `DateTimeModule`'s registered deserializer — is bypassed. Jackson falls straight through to building a `BeanDeserializer` for `DateTime`. - -The `BeanDeserializer` finds `DateTime()`'s no-arg constructor (which is not gated by `AUTO_DETECT_CREATORS`), so it reports "at least one Creator exists" — but that creator doesn't accept a String, hence the error. - -### Why it worked in 2.15.4 - -In Jackson 2.15.4, `_findCustomDeserializer()` was called **unconditionally**, regardless of whether creator detection found anything. So `DateTimeModule`'s registered deserializer was always found and used. That unconditional call was the fallback that 2.18 removed as part of the `#4515` rewrite. - -## Fix Options - -### Option 1 — Remove `.disable(MapperFeature.AUTO_DETECT_CREATORS)` from `JacksonFactory` - -Simplest fix. Jackson's default is enabled, so this aligns with standard behaviour. Risk: could enable constructor-based creator detection for customer POJOs, potentially changing deserialization semantics for handler inputs that have multi-arg constructors. The original intent behind disabling it is undocumented (present since the initial commit), so the blast radius is unknown. - -To confirm this is the cause: remove the line and run `testLoadCodeCommitEvent`. - -### Option 2 — Use `BeanDeserializerModifier` in `DateTimeModule` ✅ Recommended - -More targeted. Since Jackson 2.18 IS reaching `BeanDeserializer` for `DateTime` (confirmed by the error), `BeanDeserializerModifier.modifyDeserializer()` is called at that point. We can intercept it there and swap in the custom deserializer — without touching `AUTO_DETECT_CREATORS` at all. - -Add the following to the `DateTimeModule` constructor, after the existing `addDeserializer` call: - -```java -this.setDeserializerModifier(new BeanDeserializerModifier() { - @Override - public JsonDeserializer modifyDeserializer(DeserializationConfig config, - BeanDescription beanDesc, JsonDeserializer deserializer) { - if (beanDesc.getBeanClass() == dateTimeClass) { - return getDeserializer(dateTimeClass); - } - return deserializer; - } -}); -``` - -This is scoped only to `DateTime`, leaves `AUTO_DETECT_CREATORS` untouched, and fixes exactly the broken code path without risk of side effects elsewhere. - -## Related Issues - -| Issue | Description | -|-------|-------------| -| [databind #4515](https://github.com/FasterXML/jackson-databind/issues/4515) | Rewrite of `POJOPropertiesCollector` — root cause of the behaviour change | -| [databind #4920](https://github.com/FasterXML/jackson-databind/issues/4920) | Regression introduced by #4515: custom module deserializers bypassed when creator detection finds nothing | -| [databind #4982](https://github.com/FasterXML/jackson-databind/pull/4982) | Partial fix in 2.18.3, does not cover the `AUTO_DETECT_CREATORS = false` case |