diff --git a/.github/workflows/jackson3-source-build.yml b/.github/workflows/jackson3-source-build.yml
new file mode 100644
index 00000000..56895886
--- /dev/null
+++ b/.github/workflows/jackson3-source-build.yml
@@ -0,0 +1,127 @@
+name: Jackson 3 source build
+on:
+ pull_request:
+ workflow_dispatch:
+ inputs:
+ source_refs:
+ description: JSON map of prerequisite repositories to full commit SHAs
+ type: string
+ default: '{}'
+ push:
+ branches: [master]
+permissions:
+ contents: read
+ actions: read
+env:
+ # Reviewed master policy and separately qualified candidate; both pins are immutable.
+ JACKSON3_QUALIFIER_REVISION: f0d2100f972eec765b2b276e7ace38922394f0a6
+ JACKSON3_CORE_REVISION: 97b2359b92ed3e1d05cac04f2da8760aa954e49d
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ timeout-minutes: 90
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ path: consumer
+ ref: ${{ github.event.pull_request.head.sha || github.sha }}
+ - uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: '25'
+ - uses: actions/checkout@v4
+ with:
+ repository: networknt/light-4j
+ ref: ${{ env.JACKSON3_QUALIFIER_REVISION }}
+ path: qualification-policy
+ - name: Select qualified core and resolve remaining prerequisites
+ id: prerequisites
+ env:
+ GH_TOKEN: ${{ github.token }}
+ SOURCE_REFS: ${{ inputs.source_refs || '{}' }}
+ shell: bash
+ run: |
+ jq -e --arg core "$JACKSON3_CORE_REVISION" --argjson expected '["http-client","json-overlay","light-4j","light-rest-4j","openapi-parser","yaml-rule"]' '.["light-4j"] == $core and keys == $expected and all(.[]; type == "string" and test("^[0-9a-f]{40}$"))' consumer/ci/jackson3-source-refs.json >/dev/null
+ source_refs=$(jq -c --argjson overrides "$SOURCE_REFS" '. * $overrides' consumer/ci/jackson3-source-refs.json)
+ python3 qualification-policy/scripts/resolve_jackson3_consumer_sources.py \
+ --core-sha "$JACKSON3_CORE_REVISION" \
+ --repositories '{"json-overlay":null,"openapi-parser":null,"light-rest-4j":null}' --source-refs "$source_refs" \
+ --report core-qualification.json --github-output "$GITHUB_OUTPUT"
+ - uses: actions/checkout@v4
+ with:
+ repository: networknt/yaml-rule
+ ref: ${{ steps.prerequisites.outputs.yaml_rule }}
+ path: sources/yaml-rule
+ - uses: actions/checkout@v4
+ with:
+ repository: networknt/http-client
+ ref: ${{ steps.prerequisites.outputs.http_client }}
+ path: sources/http-client
+ - uses: actions/checkout@v4
+ with:
+ repository: networknt/light-4j
+ ref: ${{ steps.prerequisites.outputs.light_4j }}
+ path: sources/light-4j
+ - uses: actions/checkout@v4
+ with:
+ repository: networknt/json-overlay
+ ref: ${{ steps.prerequisites.outputs.json_overlay }}
+ path: sources/json-overlay
+ - uses: actions/checkout@v4
+ with:
+ repository: networknt/openapi-parser
+ ref: ${{ steps.prerequisites.outputs.openapi_parser }}
+ path: sources/openapi-parser
+ - uses: actions/checkout@v4
+ with:
+ repository: networknt/light-rest-4j
+ ref: ${{ steps.prerequisites.outputs.light_rest_4j }}
+ path: sources/light-rest-4j
+ - name: Record source commits
+ shell: bash
+ run: |
+ for source in consumer sources/*; do
+ echo "$source $(git -C "$source" rev-parse HEAD)"
+ done | tee source-commits.txt
+ for source in sources/*; do
+ printf '{"key":"%s","value":"%s"}\n' "${source##*/}" "$(git -C "$source" rev-parse HEAD)"
+ done | jq -s from_entries | tee source-refs.json
+ - name: Build prerequisites and run full tests
+ shell: bash
+ env:
+ GH_TOKEN: ${{ github.token }}
+ JACKSON3_REUSE_CORE_QUALIFICATION: 1
+ JACKSON3_CORE_SOURCE: ${{ github.workspace }}/sources/light-4j
+ JACKSON3_CORE_QUALIFICATION_REPORT: ${{ github.workspace }}/core-qualification.json
+ JACKSON3_MAVEN_REPO: ${{ runner.temp }}/m2
+ JACKSON3_YAML_RULE: ${{ github.workspace }}/sources/yaml-rule
+ JACKSON3_HTTP_CLIENT: ${{ github.workspace }}/sources/http-client
+ run: |
+ set -euo pipefail
+ bash qualification-policy/scripts/verify-jackson3-bootstrap.sh 2>&1 | tee source-build.log
+ mvn -f sources/yaml-rule/pom.xml "-Dmaven.repo.local=$JACKSON3_MAVEN_REPO" clean install 2>&1 | tee -a source-build.log
+ mvn -f sources/json-overlay/pom.xml "-Dmaven.repo.local=$JACKSON3_MAVEN_REPO" clean install 2>&1 | tee -a source-build.log
+ mvn -f sources/openapi-parser/pom.xml "-Dmaven.repo.local=$JACKSON3_MAVEN_REPO" clean install 2>&1 | tee -a source-build.log
+ mvn -f sources/light-rest-4j/pom.xml "-Dmaven.repo.local=$JACKSON3_MAVEN_REPO" clean install 2>&1 | tee -a source-build.log
+ mvn -f consumer/pom.xml "-Dmaven.repo.local=$JACKSON3_MAVEN_REPO" clean install 2>&1 | tee -a source-build.log
+ - name: Audit resolved artifacts, including releases and Jackson boundaries
+ env:
+ SOURCE_MAVEN_REPO: ${{ runner.temp }}/m2
+ shell: bash
+ run: |
+ set -euo pipefail
+ mvn -B -Dstyle.color=never -f consumer/pom.xml "-Dmaven.repo.local=$SOURCE_MAVEN_REPO" dependency:tree 2>&1 | tee resolved-dependencies.txt
+ python3 sources/light-4j/scripts/jackson3_artifact_audit.py --repository "$SOURCE_MAVEN_REPO" --tree resolved-dependencies.txt --report artifact-audit.json --require-jackson3-only
+ - uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: source-build-reports
+ path: |
+ source-build.log
+ source-commits.txt
+ source-refs.json
+ resolved-dependencies.txt
+ artifact-audit.json
+ core-qualification.json
+ **/target/surefire-reports/**
+ **/target/*smoke*.log
diff --git a/README.md b/README.md
index 1596b086..cc005a18 100644
--- a/README.md
+++ b/README.md
@@ -35,3 +35,23 @@ This is a Lambda extension that will collect the metrics of the Lambda function
`env-config` uses SnakeYAML Engine with YAML 1.2 Core resolution. See the
[configuration migration notes](env-config/README.md) for scalar changes,
duplicate-key handling, and dependency compatibility before rollout.
+
+## Jackson 3 migration
+
+This source line requires Jackson 3.2.3, json-schema-validator 3.0.8 and the Jackson 3 light-4j/http-client prerequisites. Public mapper and JsonNode types use tools.jackson; annotations retain com.fasterxml.jackson.annotation. Build those prerequisites before this reactor. Do not mix published Jackson 2 snapshot binaries with this source line.
+
+Mappers use the shared Jackson2Defaults profile and preserve existing per-mapper overrides. Mappers are immutable; Java time and Optional support are built into Jackson 3. Classic SnakeYAML compatibility pins remain until the stack-wide cleanup.
+
+Local qualification covers JVM tests and sanitized API Gateway/event and validation parity. It does not qualify deployed AWS execution or GraalVM native images.
+
+### Reproducible Jackson 3 source build
+
+`.github/workflows/jackson3-source-build.yml` builds the prerequisite source stack in an isolated Maven repository, reuses independently qualified full-core test evidence, and runs the prerequisite and full consumer suites. It pins reviewed policy `f0d2100f972eec765b2b276e7ace38922394f0a6` and candidate `97b2359b92ed3e1d05cac04f2da8760aa954e49d`; yaml-rule/http-client are selected from that verified evidence. The complete prerequisite closure is pinned in `ci/jackson3-source-refs.json`, initialized from the successful source-build manifest, with reviewed updates expressed as full SHAs; no default-branch or migration-branch lookup is used. Explicit `source_refs` replay overrides are merged into that map and remain subject to core qualification. The workflow uploads `source-refs.json`, `source-commits.txt`, `core-qualification.json` and Surefire reports. Core registration and workflow-token acceptance are merged in light-4j #2825 and #2826.
+
+The optional `source_refs` workflow-dispatch input replays recorded full SHAs. GitHub enables dispatch only after the workflow exists on the default branch; before merge, reproduce from the recorded source checkouts locally. A green local suite is separate from a successful remote source-build run.
+
+### Exact-source CI qualification
+
+Consumer CI requires retained successful full-reactor evidence for the actual checked-out light-4j SHA and the same yaml-rule/http-client source commits. Core PR CI explicitly checks out the PR head, not its merge ref. Replay dispatches cannot qualify consumers. The attempt-specific evidence artifact records the actual checkout, prerequisite SHAs and test totals. A later pending, failed or cancelled run does not invalidate earlier valid evidence. All API pages and retained attempts are considered; missing or expired evidence blocks reuse. `core-qualification.json` records the accepted run and artifact.
+
+The full dependency ledger is audited across all groups. Every NetworkNT release and snapshot must be source-installed, apart from the explicitly SHA-256-pinned Jackson 3 validator release. Known Jackson 2 yaml-rule and chaos-monkey releases are rejected even with a local-install marker. Jackson 2 dependencies in other groups are reported; ready migrations reject them except shared annotations.
diff --git a/ci/jackson3-source-refs.json b/ci/jackson3-source-refs.json
new file mode 100644
index 00000000..399f89a4
--- /dev/null
+++ b/ci/jackson3-source-refs.json
@@ -0,0 +1,8 @@
+{
+ "http-client": "5ecee29dbb08d813512f5379c2e1b902d553c5d4",
+ "json-overlay": "e309fd63f95317067c9b13a4bed7ac16727bfea5",
+ "light-4j": "97b2359b92ed3e1d05cac04f2da8760aa954e49d",
+ "light-rest-4j": "a31955945036d9108b7e9bb72167415cc9cfc287",
+ "openapi-parser": "9ae849056e49f7c796fe28bfe6093fbe4847cc5c",
+ "yaml-rule": "c237cc3a25720f43e57bf571e25d5fa06687aa3b"
+}
diff --git a/custom-runtime/pom.xml b/custom-runtime/pom.xml
index 4c1f60a2..962be8fe 100644
--- a/custom-runtime/pom.xml
+++ b/custom-runtime/pom.xml
@@ -31,6 +31,14 @@
A custom runtime for GraalVM native image to improve code start performance
+
+ tools.jackson.core
+ jackson-databind
+
+
+ com.networknt
+ jackson-defaults
+
com.networknt
utility
diff --git a/custom-runtime/src/main/java/com/networknt/aws/lambda/DefaultResponse.java b/custom-runtime/src/main/java/com/networknt/aws/lambda/DefaultResponse.java
index df11e128..5389aa7b 100644
--- a/custom-runtime/src/main/java/com/networknt/aws/lambda/DefaultResponse.java
+++ b/custom-runtime/src/main/java/com/networknt/aws/lambda/DefaultResponse.java
@@ -1,7 +1,7 @@
package com.networknt.aws.lambda;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import tools.jackson.databind.annotation.JsonDeserialize;
+import tools.jackson.databind.annotation.JsonPOJOBuilder;
@JsonDeserialize(builder = DefaultResponse.Builder.class)
public class DefaultResponse {
diff --git a/custom-runtime/src/main/java/com/networknt/aws/lambda/InvocationResponse.java b/custom-runtime/src/main/java/com/networknt/aws/lambda/InvocationResponse.java
index b3464d39..bcdccc24 100644
--- a/custom-runtime/src/main/java/com/networknt/aws/lambda/InvocationResponse.java
+++ b/custom-runtime/src/main/java/com/networknt/aws/lambda/InvocationResponse.java
@@ -1,8 +1,8 @@
package com.networknt.aws.lambda;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import tools.jackson.databind.annotation.JsonDeserialize;
+import tools.jackson.databind.annotation.JsonPOJOBuilder;
@JsonDeserialize(builder = InvocationResponse.Builder.class)
public class InvocationResponse {
diff --git a/custom-runtime/src/main/java/com/networknt/aws/lambda/Runtime.java b/custom-runtime/src/main/java/com/networknt/aws/lambda/Runtime.java
index b111f618..0b99f2d2 100644
--- a/custom-runtime/src/main/java/com/networknt/aws/lambda/Runtime.java
+++ b/custom-runtime/src/main/java/com/networknt/aws/lambda/Runtime.java
@@ -3,23 +3,22 @@
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
+import com.networknt.jackson.JacksonDefaults;
+import com.networknt.utility.NioUtils;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
-
-import com.networknt.utility.NioUtils;
+import tools.jackson.databind.DeserializationFeature;
+import tools.jackson.databind.ObjectMapper;
public class Runtime {
private static final String REQUEST_ID_HEADER = "lambda-runtime-aws-request-id";
- private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
- .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+ private static final ObjectMapper OBJECT_MAPPER = JacksonDefaults.builder()
+ .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES).build();
public static void main(String[] args) throws IOException {
while(true) {
diff --git a/custom-runtime/src/test/java/com/networknt/aws/lambda/Jackson3RuntimeCompatibilityTest.java b/custom-runtime/src/test/java/com/networknt/aws/lambda/Jackson3RuntimeCompatibilityTest.java
new file mode 100644
index 00000000..387d202a
--- /dev/null
+++ b/custom-runtime/src/test/java/com/networknt/aws/lambda/Jackson3RuntimeCompatibilityTest.java
@@ -0,0 +1,64 @@
+package com.networknt.aws.lambda;
+
+import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
+import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
+import org.junit.jupiter.api.Test;
+import tools.jackson.databind.ObjectMapper;
+
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class Jackson3RuntimeCompatibilityTest {
+ private ObjectMapper runtimeMapper() throws Exception {
+ var field = Runtime.class.getDeclaredField("OBJECT_MAPPER");
+ field.setAccessible(true);
+ return (ObjectMapper) field.get(null);
+ }
+
+ @Test
+ void bindsAwsGatewayEventsWithoutJackson2Types() throws Exception {
+ var mapper = runtimeMapper();
+ var request = mapper.readValue("{\"path\":\"/pets\",\"httpMethod\":\"POST\","
+ + "\"body\":\"雪\",\"isBase64Encoded\":true,\"requestContext\":{\"requestId\":\"fixture\"},"
+ + "\"multiValueHeaders\":{\"X-Test\":[\"one\",\"two\"]}}", APIGatewayProxyRequestEvent.class);
+ assertEquals("/pets", request.getPath());
+ assertEquals("fixture", request.getRequestContext().getRequestId());
+ assertEquals(List.of("one", "two"), request.getMultiValueHeaders().get("X-Test"));
+ assertTrue(request.getIsBase64Encoded());
+ assertEquals(request, mapper.readValue(mapper.writeValueAsString(request), APIGatewayProxyRequestEvent.class));
+ }
+
+ @Test
+ void preservesTheMeasuredResponseWireShape() throws Exception {
+ var response = new APIGatewayProxyResponseEvent().withStatusCode(201).withBody("雪\nbody")
+ .withIsBase64Encoded(true).withHeaders(Map.of("Content-Type", "application/json"))
+ .withMultiValueHeaders(Map.of("Set-Cookie", List.of("a=1", "b=2")));
+ assertEquals("{\"statusCode\":201,\"headers\":{\"Content-Type\":\"application/json\"},"
+ + "\"multiValueHeaders\":{\"Set-Cookie\":[\"a=1\",\"b=2\"]},\"body\":\"雪\\nbody\","
+ + "\"isBase64Encoded\":true}", runtimeMapper().writeValueAsString(response));
+ }
+
+ @Test
+ void retainsBuilderBindingAndNullPrimitiveHandling() throws Exception {
+ var mapper = runtimeMapper();
+ var response = mapper.readValue("{\"code\":null,\"message\":\"fixture error\"}", DefaultResponse.class);
+ assertEquals(0, response.getCode());
+ assertEquals("{\"code\":0,\"message\":\"fixture error\"}", mapper.writeValueAsString(response));
+ var invocation = mapper.readValue("{\"requestId\":\"fixture\",\"event\":{\"path\":\"/pets\"}}", InvocationResponse.class);
+ assertEquals("fixture", invocation.getRequestId());
+ assertEquals("/pets", invocation.getEvent().getPath());
+ }
+
+ @Test
+ void retainsPermissiveRuntimeBinding() throws Exception {
+ var mapper = runtimeMapper();
+ var request = mapper.readValue("{\"futureField\":1,\"isBase64Encoded\":false} {}", APIGatewayProxyRequestEvent.class);
+ assertNull(request.getPath());
+ assertFalse(request.getIsBase64Encoded());
+ }
+}
diff --git a/env-config/pom.xml b/env-config/pom.xml
index 5bda6212..6fea8960 100644
--- a/env-config/pom.xml
+++ b/env-config/pom.xml
@@ -31,6 +31,11 @@
Environmental Config for Lambda for seldom changed properties
+
+ com.networknt
+ config
+ test
+
com.amazonaws
aws-lambda-java-core
@@ -40,7 +45,7 @@
aws-lambda-java-events
- com.fasterxml.jackson.core
+ tools.jackson.core
jackson-databind
diff --git a/env-config/src/test/java/com/networknt/aws/lambda/ConfigurationTest.java b/env-config/src/test/java/com/networknt/aws/lambda/ConfigurationTest.java
index a3c6f346..7cf40dd0 100644
--- a/env-config/src/test/java/com/networknt/aws/lambda/ConfigurationTest.java
+++ b/env-config/src/test/java/com/networknt/aws/lambda/ConfigurationTest.java
@@ -1,7 +1,9 @@
package com.networknt.aws.lambda;
+import com.networknt.jackson.JacksonDefaults;
+
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import tools.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -9,7 +11,7 @@
import java.util.Map;
public class ConfigurationTest {
- ObjectMapper objectMapper = new ObjectMapper();
+ ObjectMapper objectMapper = JacksonDefaults.mapper();
@Test
@Disabled
public void thatConfigurationValuesAreLoaded() throws Exception {
diff --git a/lambda-interceptor/pom.xml b/lambda-interceptor/pom.xml
index 05306a2c..e86716e5 100644
--- a/lambda-interceptor/pom.xml
+++ b/lambda-interceptor/pom.xml
@@ -31,6 +31,10 @@
The interceptor to pre-process request and post-process response when light-proxy is used
+
+ com.networknt
+ jackson-defaults
+
com.networknt
slf4j-logback
@@ -44,7 +48,7 @@
aws-lambda-java-events
- com.fasterxml.jackson.core
+ tools.jackson.core
jackson-databind
diff --git a/lambda-interceptor/src/main/java/com/networknt/aws/lambda/LightLambdaInterceptor.java b/lambda-interceptor/src/main/java/com/networknt/aws/lambda/LightLambdaInterceptor.java
index 70be294d..ccf6b2be 100644
--- a/lambda-interceptor/src/main/java/com/networknt/aws/lambda/LightLambdaInterceptor.java
+++ b/lambda-interceptor/src/main/java/com/networknt/aws/lambda/LightLambdaInterceptor.java
@@ -4,12 +4,13 @@
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import com.networknt.jackson.JacksonDefaults;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import tools.jackson.databind.ObjectMapper;
public class LightLambdaInterceptor {
- private static ObjectMapper objectMapper = new ObjectMapper();
+ private static ObjectMapper objectMapper = JacksonDefaults.mapper();
private static final Logger logger = LoggerFactory.getLogger(LightLambdaInterceptor.class);
public APIGatewayProxyRequestEvent interceptRequest(final APIGatewayProxyRequestEvent input, final Context context) {
diff --git a/lambda-invoker/pom.xml b/lambda-invoker/pom.xml
index 5ea049cc..76be8a04 100644
--- a/lambda-invoker/pom.xml
+++ b/lambda-invoker/pom.xml
@@ -77,7 +77,7 @@
sts
- com.fasterxml.jackson.core
+ tools.jackson.core
jackson-databind
diff --git a/lambda-invoker/src/main/java/com/networknt/aws/lambda/LambdaInvokerConfig.java b/lambda-invoker/src/main/java/com/networknt/aws/lambda/LambdaInvokerConfig.java
index f10a5819..d2f39cd0 100644
--- a/lambda-invoker/src/main/java/com/networknt/aws/lambda/LambdaInvokerConfig.java
+++ b/lambda-invoker/src/main/java/com/networknt/aws/lambda/LambdaInvokerConfig.java
@@ -1,6 +1,6 @@
package com.networknt.aws.lambda;
-import com.fasterxml.jackson.core.type.TypeReference;
+import tools.jackson.core.type.TypeReference;
import com.networknt.config.Config;
import com.networknt.config.ConfigException;
import com.networknt.config.schema.ConfigSchema;
diff --git a/lambda-validator/pom.xml b/lambda-validator/pom.xml
index bebc2819..6b7c7d68 100644
--- a/lambda-validator/pom.xml
+++ b/lambda-validator/pom.xml
@@ -60,7 +60,7 @@
aws-lambda-java-events
- com.fasterxml.jackson.core
+ tools.jackson.core
jackson-databind
diff --git a/lambda-validator/src/main/java/com/networknt/aws/lambda/LambdaSchemaValidator.java b/lambda-validator/src/main/java/com/networknt/aws/lambda/LambdaSchemaValidator.java
index 0f341a91..f9274e23 100644
--- a/lambda-validator/src/main/java/com/networknt/aws/lambda/LambdaSchemaValidator.java
+++ b/lambda-validator/src/main/java/com/networknt/aws/lambda/LambdaSchemaValidator.java
@@ -2,7 +2,7 @@
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
-import com.fasterxml.jackson.core.JsonProcessingException;
+import tools.jackson.core.JacksonException;
import com.networknt.aws.lambda.validator.RequestValidator;
import com.networknt.aws.lambda.validator.SchemaValidator;
import com.networknt.config.Config;
@@ -48,7 +48,7 @@ public LambdaSchemaValidator() {
String openapiString = Config.getInstance().getMapper().writeValueAsString(openapi);
if(logger.isTraceEnabled()) logger.trace("OpenApiMiddleware openapiString: " + openapiString);
helper = new OpenApiHelper(openapiString);
- } catch (JsonProcessingException e) {
+ } catch (JacksonException e) {
logger.error("merge specification failed");
throw new RuntimeException("merge specification failed");
}
diff --git a/lambda-validator/src/test/java/com/networknt/aws/lambda/Jackson3LambdaValidationRegressionTest.java b/lambda-validator/src/test/java/com/networknt/aws/lambda/Jackson3LambdaValidationRegressionTest.java
new file mode 100644
index 00000000..6db1cf7b
--- /dev/null
+++ b/lambda-validator/src/test/java/com/networknt/aws/lambda/Jackson3LambdaValidationRegressionTest.java
@@ -0,0 +1,44 @@
+package com.networknt.aws.lambda;
+
+import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
+import com.networknt.config.Config;
+import org.junit.jupiter.api.Test;
+
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class Jackson3LambdaValidationRegressionTest {
+ @Test
+ void validatesAnEventBoundByTheFrameworkMapper() {
+ var request = Config.getInstance().getMapper().readValue(
+ "{\"path\":\"/pets\",\"httpMethod\":\"GET\",\"queryStringParameters\":{\"includeCode\":\"true\"}}",
+ APIGatewayProxyRequestEvent.class);
+ assertNull(new LambdaSchemaValidator().validateRequest(request));
+ }
+
+ @Test
+ void keepsMalformedBodyErrorsHandled() {
+ var request = new APIGatewayProxyRequestEvent().withPath("/pets").withHttpMethod("POST")
+ .withHeaders(Map.of("Content-Type", "application/json")).withBody("{");
+ var response = new LambdaSchemaValidator().validateRequest(request);
+ assertNotNull(response);
+ assertEquals(400, response.getStatusCode());
+ assertTrue(response.getBody().contains("ERR10015"), response.getBody());
+ }
+
+ @Test
+ void keepsValidationMessagesAndMethodErrorsStable() {
+ var validator = new LambdaSchemaValidator();
+ var request = new APIGatewayProxyRequestEvent().withPath("/pets").withHttpMethod("GET")
+ .withQueryStringParameters(Map.of("includeCode", "wrong"));
+ var response = validator.validateRequest(request);
+ assertEquals(400, response.getStatusCode());
+ assertTrue(response.getBody().contains("/includeCode: string found, boolean expected"), response.getBody());
+ request.setHttpMethod("PATCH");
+ assertEquals(405, validator.validateRequest(request).getStatusCode());
+ }
+}
diff --git a/lambda-validator/src/test/java/com/networknt/aws/lambda/LambdaSchemaValidatorTest.java b/lambda-validator/src/test/java/com/networknt/aws/lambda/LambdaSchemaValidatorTest.java
index bf590f3e..f1e4bded 100644
--- a/lambda-validator/src/test/java/com/networknt/aws/lambda/LambdaSchemaValidatorTest.java
+++ b/lambda-validator/src/test/java/com/networknt/aws/lambda/LambdaSchemaValidatorTest.java
@@ -1,9 +1,11 @@
package com.networknt.aws.lambda;
+import com.networknt.jackson.JacksonDefaults;
+
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import tools.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
@@ -13,7 +15,7 @@
import java.util.Map;
public class LambdaSchemaValidatorTest {
- ObjectMapper objectMapper = new ObjectMapper();
+ ObjectMapper objectMapper = JacksonDefaults.mapper();
private final LambdaSchemaValidator validator = new LambdaSchemaValidator();
APIGatewayProxyRequestEvent requestEvent;
diff --git a/pom.xml b/pom.xml
index cc7bd09f..43c13982 100644
--- a/pom.xml
+++ b/pom.xml
@@ -99,7 +99,7 @@
${java.version}
UTF-8
2.4.2-SNAPSHOT
- 2.22.3
+ 3.2.3
2.0.20
1.4.0
3.16.1
@@ -116,7 +116,7 @@
2.4.4.Final
3.27.7
2.23.0
- 2.0.8
+ 3.0.8
3.12.0
1.7.0
3.6.0
@@ -139,6 +139,11 @@
+
+ com.networknt
+ jackson-defaults
+ ${version.light-4j}
+
org.junit
junit-bom
@@ -147,7 +152,7 @@
import
- com.fasterxml.jackson
+ tools.jackson
jackson-bom
${version.jackson}
pom
diff --git a/request-handler/pom.xml b/request-handler/pom.xml
index 77f13f78..ea81c974 100644
--- a/request-handler/pom.xml
+++ b/request-handler/pom.xml
@@ -52,7 +52,7 @@
aws-lambda-java-events
- com.fasterxml.jackson.core
+ tools.jackson.core
jackson-databind
diff --git a/schema-validator/pom.xml b/schema-validator/pom.xml
index 83e0bc1a..30a73650 100644
--- a/schema-validator/pom.xml
+++ b/schema-validator/pom.xml
@@ -60,7 +60,7 @@
aws-lambda-java-events
- com.fasterxml.jackson.core
+ tools.jackson.core
jackson-databind
diff --git a/schema-validator/src/main/java/com/networknt/aws/lambda/validator/RequestValidator.java b/schema-validator/src/main/java/com/networknt/aws/lambda/validator/RequestValidator.java
index fc786188..f08202ef 100644
--- a/schema-validator/src/main/java/com/networknt/aws/lambda/validator/RequestValidator.java
+++ b/schema-validator/src/main/java/com/networknt/aws/lambda/validator/RequestValidator.java
@@ -1,8 +1,8 @@
package com.networknt.aws.lambda.validator;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.node.TextNode;
+import tools.jackson.databind.JsonNode;
+import tools.jackson.databind.node.StringNode;
import com.networknt.config.Config;
import com.networknt.jsonoverlay.Overlay;
import com.networknt.oas.model.Parameter;
@@ -144,7 +144,7 @@ private Status validatePathParameters(final APIGatewayProxyRequestEvent requestE
LOG.info("Path parameter cannot be decoded, it will be used directly");
}
- return schemaValidator.validate(new TextNode(paramValue), Overlay.toJson((SchemaImpl)(parameter.get().getSchema())), paramName);
+ return schemaValidator.validate(new StringNode(paramValue), Overlay.toJson((SchemaImpl)(parameter.get().getSchema())), paramName);
}
}
return status;
@@ -179,7 +179,7 @@ private Status validateQueryParameter(final APIGatewayProxyRequestEvent requestE
return new Status(VALIDATOR_REQUEST_PARAMETER_QUERY_MISSING, queryParameter.getName(), openApiOperation.getPathString().original());
}
} else {
- return schemaValidator.validate(new TextNode(queryParameterValue), Overlay.toJson((SchemaImpl)queryParameter.getSchema()), queryParameter.getName());
+ return schemaValidator.validate(new StringNode(queryParameterValue), Overlay.toJson((SchemaImpl)queryParameter.getSchema()), queryParameter.getName());
}
return null;
}
@@ -233,7 +233,7 @@ private Status validateHeader(final APIGatewayProxyRequestEvent requestEvent,
return new Status(VALIDATOR_REQUEST_PARAMETER_HEADER_MISSING, headerParameter.getName(), openApiOperation.getPathString().original());
}
} else {
- return headerParameter.getSchema() != null ? schemaValidator.validate(new TextNode(headerValue.get()), Overlay.toJson((SchemaImpl)headerParameter.getSchema()), headerParameter.getName()) : null;
+ return headerParameter.getSchema() != null ? schemaValidator.validate(new StringNode(headerValue.get()), Overlay.toJson((SchemaImpl)headerParameter.getSchema()), headerParameter.getName()) : null;
}
return null;
}
@@ -249,7 +249,7 @@ private ValidationResult validateDeserializedValues(final APIGatewayProxyRequest
if (null==deserializedValue) {
validationResult.addSkipped(p);
}else {
- Status s = schemaValidator.validate(new TextNode(deserializedValue), Overlay.toJson((SchemaImpl)(p.getSchema())), p.getName());
+ Status s = schemaValidator.validate(new StringNode(deserializedValue), Overlay.toJson((SchemaImpl)(p.getSchema())), p.getName());
validationResult.addStatus(s);
}
});
diff --git a/schema-validator/src/main/java/com/networknt/aws/lambda/validator/SchemaValidator.java b/schema-validator/src/main/java/com/networknt/aws/lambda/validator/SchemaValidator.java
index a5e45217..52ed6493 100644
--- a/schema-validator/src/main/java/com/networknt/aws/lambda/validator/SchemaValidator.java
+++ b/schema-validator/src/main/java/com/networknt/aws/lambda/validator/SchemaValidator.java
@@ -16,8 +16,8 @@
package com.networknt.aws.lambda.validator;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
+import tools.jackson.databind.JsonNode;
+import tools.jackson.databind.node.ObjectNode;
import com.networknt.jsonoverlay.Overlay;
import com.networknt.oas.model.OpenApi3;
import com.networknt.oas.model.impl.OpenApi3Impl;
diff --git a/schema-validator/src/test/java/com/networknt/aws/lambda/validator/SchemaValidatorTest.java b/schema-validator/src/test/java/com/networknt/aws/lambda/validator/SchemaValidatorTest.java
index 33abbbcf..a257a181 100644
--- a/schema-validator/src/test/java/com/networknt/aws/lambda/validator/SchemaValidatorTest.java
+++ b/schema-validator/src/test/java/com/networknt/aws/lambda/validator/SchemaValidatorTest.java
@@ -1,9 +1,11 @@
package com.networknt.aws.lambda.validator;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.NullNode;
-import com.fasterxml.jackson.databind.node.TextNode;
+import com.networknt.jackson.JacksonDefaults;
+
+import tools.jackson.databind.JsonNode;
+import tools.jackson.databind.ObjectMapper;
+import tools.jackson.databind.node.NullNode;
+import tools.jackson.databind.node.StringNode;
import com.networknt.status.Status;
import org.junit.jupiter.api.Test;
@@ -14,7 +16,7 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
public class SchemaValidatorTest {
- private static final ObjectMapper MAPPER = new ObjectMapper();
+ private static final ObjectMapper MAPPER = JacksonDefaults.mapper();
@Test
void shouldConstructWithoutAnApi() {
@@ -26,8 +28,8 @@ void shouldKeepLooseAndStrictTypeProfilesSeparate() throws Exception {
SchemaValidator validator = new SchemaValidator();
JsonNode schema = MAPPER.readTree("{\"type\":\"integer\"}");
- assertNull(validator.validate(new TextNode("42"), schema, true, false));
- assertNotNull(validator.validate(new TextNode("42"), schema, false, false));
+ assertNull(validator.validate(new StringNode("42"), schema, true, false));
+ assertNotNull(validator.validate(new StringNode("42"), schema, false, false));
}
@Test
@@ -56,7 +58,7 @@ void shouldAllowStandardNonDefaultDialects() throws Exception {
SchemaValidator validator = new SchemaValidator();
JsonNode schema = MAPPER.readTree("{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"integer\"}");
- Status status = validator.validate(new TextNode("invalid"), schema, false, false);
+ Status status = validator.validate(new StringNode("invalid"), schema, false, false);
assertNotNull(status);
assertEquals(SchemaValidator.VALIDATOR_SCHEMA, status.getCode());
@@ -67,7 +69,7 @@ void shouldFailClosedWhenSchemaCannotBeBuilt() throws Exception {
SchemaValidator validator = new SchemaValidator();
JsonNode schema = MAPPER.readTree("{\"type\":\"string\",\"pattern\":\"[\"}");
- Status status = validator.validate(new TextNode("value"), schema, false, false);
+ Status status = validator.validate(new StringNode("value"), schema, false, false);
assertNotNull(status);
assertEquals(SchemaValidator.VALIDATOR_SCHEMA_INVALID_JSON, status.getCode());
diff --git a/scope-verifier/pom.xml b/scope-verifier/pom.xml
index f32f475a..578cbdb6 100644
--- a/scope-verifier/pom.xml
+++ b/scope-verifier/pom.xml
@@ -44,7 +44,7 @@
aws-lambda-java-events
- com.fasterxml.jackson.core
+ tools.jackson.core
jackson-databind
diff --git a/scope-verifier/src/test/java/com/networknt/aws/lambda/ScopeVerifierTest.java b/scope-verifier/src/test/java/com/networknt/aws/lambda/ScopeVerifierTest.java
index 8c902480..e686d8dc 100644
--- a/scope-verifier/src/test/java/com/networknt/aws/lambda/ScopeVerifierTest.java
+++ b/scope-verifier/src/test/java/com/networknt/aws/lambda/ScopeVerifierTest.java
@@ -1,8 +1,10 @@
package com.networknt.aws.lambda;
+import com.networknt.jackson.JacksonDefaults;
+
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import tools.jackson.databind.ObjectMapper;
import com.networknt.utility.StringUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -11,7 +13,7 @@
import java.util.List;
public class ScopeVerifierTest {
- ObjectMapper objectMapper = new ObjectMapper();
+ ObjectMapper objectMapper = JacksonDefaults.mapper();
@Test
public void testSuccessRequest() {
ScopeVerifier verifier = new ScopeVerifier();