From 2516b83f4924e786fa2d62fabfcd44c1f50965ce Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Wed, 29 Oct 2025 17:37:15 +0100 Subject: [PATCH 01/76] feat(idempotency): Add functional `Idempotency` API (#2244) * Decouple AspectJ dependencies on pjp from IdempotencyHandler. * Create PowertoolsIdempotency functional API with overloads for all use-cases. * Make IdempotencyConfig thread-safe to avoid Lamdba context leaks in config singleton. * Fix bug: We need to pass the return type explicitly to support deserialization to the return type of the method. * Address some pmd findings. * Make java doc consistent in PowertoolsIdempotency. * Hide checked exceptions (from AspectJ) from user-facing API in PowertoolsIdempotency. * Remove unncessary try catch Throwable in unit tests * Add E2E test for functional idempotency API. * Fix sonar issues. * Remove unncessary constructors for Input object. * Add support for generic types. * Merge PowertoolsIdempotency with Idempotency to offer a single entrypoint to idempotency. * Make ConfigHolder final. * Make ConfigHolder modifier order comply with java language spec. * Make Holder class final. --- .../handlers/idempotency-functional/pom.xml | 60 ++++ .../lambda/powertools/e2e/Function.java | 66 ++++ .../amazon/lambda/powertools/e2e/Input.java | 27 ++ .../aws-lambda-java-core/reflect-config.json | 13 + .../reflect-config.json | 35 ++ .../jni-config.json | 11 + .../native-image.properties | 1 + .../reflect-config.json | 61 ++++ .../resource-config.json | 19 ++ .../reflect-config.json | 25 ++ .../reflect-config.json | 20 ++ .../resource-config.json | 7 + .../src/main/resources/log4j2.xml | 16 + .../handlers/idempotency-generics/pom.xml | 60 ++++ .../lambda/powertools/e2e/Function.java | 78 +++++ .../amazon/lambda/powertools/e2e/Input.java | 27 ++ .../aws-lambda-java-core/reflect-config.json | 13 + .../reflect-config.json | 35 ++ .../jni-config.json | 11 + .../native-image.properties | 1 + .../reflect-config.json | 61 ++++ .../resource-config.json | 19 ++ .../reflect-config.json | 25 ++ .../reflect-config.json | 20 ++ .../resource-config.json | 7 + .../src/main/resources/log4j2.xml | 16 + powertools-e2e-tests/handlers/pom.xml | 2 + .../lambda/powertools/IdempotencyE2ET.java | 31 +- .../powertools/idempotency/Idempotency.java | 238 +++++++++++++- .../idempotency/IdempotencyConfig.java | 8 +- .../internal/IdempotencyHandler.java | 28 +- .../internal/IdempotentAspect.java | 28 +- .../internal/IdempotentFunction.java | 30 ++ .../idempotency/internal/cache/LRUCache.java | 4 +- .../idempotency/IdempotencyTest.java | 311 ++++++++++++++++++ .../IdempotencyFunctionalFunction.java | 48 +++ ...IdempotencyMultiArgFunctionalFunction.java | 54 +++ .../internal/IdempotencyAspectTest.java | 71 +++- .../testutils/InMemoryPersistenceStore.java | 58 ++++ .../powertools/utilities/JsonConfig.java | 28 +- 40 files changed, 1612 insertions(+), 61 deletions(-) create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/pom.xml create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/java/software/amazon/lambda/powertools/e2e/Input.java create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/log4j2.xml create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/pom.xml create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/java/software/amazon/lambda/powertools/e2e/Function.java create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/java/software/amazon/lambda/powertools/e2e/Input.java create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json create mode 100644 powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/log4j2.xml create mode 100644 powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentFunction.java create mode 100644 powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java create mode 100644 powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunctionalFunction.java create mode 100644 powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyMultiArgFunctionalFunction.java create mode 100644 powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/testutils/InMemoryPersistenceStore.java diff --git a/powertools-e2e-tests/handlers/idempotency-functional/pom.xml b/powertools-e2e-tests/handlers/idempotency-functional/pom.xml new file mode 100644 index 000000000..c8893c6e9 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/pom.xml @@ -0,0 +1,60 @@ + + 4.0.0 + + + software.amazon.lambda + e2e-test-handlers-parent + 2.5.0 + + + e2e-test-handler-idempotency-functional + jar + E2E test handler – Idempotency Functional + + + + software.amazon.lambda + powertools-idempotency-dynamodb + + + software.amazon.lambda + powertools-logging-log4j + + + com.amazonaws + aws-lambda-java-events + + + com.amazonaws + aws-lambda-java-runtime-interface-client + + + com.amazonaws + aws-lambda-java-core + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + + + + + native-image + + + + org.graalvm.buildtools + native-maven-plugin + + + + + + diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/idempotency-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java new file mode 100644 index 000000000..fec7459c1 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -0,0 +1,66 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.e2e; + +import java.time.Duration; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.TimeZone; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.lambda.powertools.idempotency.Idempotency; +import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; +import software.amazon.lambda.powertools.idempotency.persistence.dynamodb.DynamoDBPersistenceStore; + +public class Function implements RequestHandler { + + public Function() { + this(DynamoDbClient + .builder() + .httpClient(UrlConnectionHttpClient.builder().build()) + .region(Region.of(System.getenv("AWS_REGION"))) + .build()); + } + + public Function(DynamoDbClient client) { + Idempotency.config().withConfig( + IdempotencyConfig.builder() + .withExpiration(Duration.of(10, ChronoUnit.SECONDS)) + .build()) + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withDynamoDbClient(client) + .withTableName(System.getenv("IDEMPOTENCY_TABLE")) + .build()) + .configure(); + } + + public String handleRequest(Input input, Context context) { + Idempotency.registerLambdaContext(context); + + return Idempotency.makeIdempotent(this::processRequest, input, String.class); + } + + private String processRequest(Input input) { + DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME.withZone(TimeZone.getTimeZone("UTC").toZoneId()); + return dtf.format(Instant.now()); + } +} diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/idempotency-functional/src/main/java/software/amazon/lambda/powertools/e2e/Input.java new file mode 100644 index 000000000..0d14b749e --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -0,0 +1,27 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.e2e; + +public class Input { + private String message; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json new file mode 100644 index 000000000..2780aca09 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json @@ -0,0 +1,13 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "methods":[{"name":"","parameterTypes":[] }], + "fields":[{"name":"logger"}], + "allPublicMethods":true + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal", + "methods":[{"name":"","parameterTypes":[] }], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json new file mode 100644 index 000000000..ddda5d5f1 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json @@ -0,0 +1,35 @@ +[ + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json new file mode 100644 index 000000000..91be72f7a --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json @@ -0,0 +1,11 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClientException", + "methods":[{"name":"","parameterTypes":["java.lang.String","int"] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties new file mode 100644 index 000000000..20f8b7801 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties @@ -0,0 +1 @@ +Args = --initialize-at-build-time=jdk.xml.internal.SecuritySupport \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json new file mode 100644 index 000000000..e69fa735c --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -0,0 +1,61 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.LambdaRuntime", + "fields": [{ "name": "logger" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogLevel", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogFormat", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "java.lang.Void", + "methods": [{ "name": "", "parameterTypes": [] }] + }, + { + "name": "java.util.Collections$UnmodifiableMap", + "fields": [{ "name": "m" }] + }, + { + "name": "jdk.internal.module.IllegalAccessLogger", + "fields": [{ "name": "logger" }] + }, + { + "name": "sun.misc.Unsafe", + "fields": [{ "name": "theUnsafe" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields": [ + { "name": "id" }, + { "name": "invokedFunctionArn" }, + { "name": "deadlineTimeInMs" }, + { "name": "xrayTraceId" }, + { "name": "clientContext" }, + { "name": "cognitoIdentity" }, + { "name": "tenantId" }, + { "name": "content" } + ], + "allPublicMethods": true + } +] diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json new file mode 100644 index 000000000..1062b4249 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json @@ -0,0 +1,19 @@ +{ + "resources": { + "includes": [ + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-x86_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-x86_64.so\\E" + } + ] + }, + "bundles": [] +} \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json new file mode 100644 index 000000000..9890688f9 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json @@ -0,0 +1,25 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl", + "methods": [{ "name": "", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers[]" + }, + { + "name": "org.joda.time.DateTime", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json new file mode 100644 index 000000000..9ddd235e2 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json @@ -0,0 +1,20 @@ +[ + { + "name": "software.amazon.lambda.powertools.e2e.Function", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "software.amazon.lambda.powertools.e2e.Input", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json new file mode 100644 index 000000000..be6aac3f6 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json @@ -0,0 +1,7 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\Qlog4j2.xml\\E" + }]}, + "bundles":[] +} diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/log4j2.xml b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/log4j2.xml new file mode 100644 index 000000000..8925f70b9 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/log4j2.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency-generics/pom.xml b/powertools-e2e-tests/handlers/idempotency-generics/pom.xml new file mode 100644 index 000000000..5387d4059 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/pom.xml @@ -0,0 +1,60 @@ + + 4.0.0 + + + software.amazon.lambda + e2e-test-handlers-parent + 2.5.0 + + + e2e-test-handler-idempotency-generics + jar + E2E test handler – Idempotency Generics + + + + software.amazon.lambda + powertools-idempotency-dynamodb + + + software.amazon.lambda + powertools-logging-log4j + + + com.amazonaws + aws-lambda-java-events + + + com.amazonaws + aws-lambda-java-runtime-interface-client + + + com.amazonaws + aws-lambda-java-core + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + + + + + native-image + + + + org.graalvm.buildtools + native-maven-plugin + + + + + + diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/idempotency-generics/src/main/java/software/amazon/lambda/powertools/e2e/Function.java new file mode 100644 index 000000000..09e39d1eb --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -0,0 +1,78 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.e2e; + +import java.time.Duration; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.HashMap; +import java.util.Map; +import java.util.TimeZone; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.fasterxml.jackson.core.type.TypeReference; + +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.lambda.powertools.idempotency.Idempotency; +import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; +import software.amazon.lambda.powertools.idempotency.persistence.dynamodb.DynamoDBPersistenceStore; + +public class Function implements RequestHandler { + + public Function() { + this(DynamoDbClient + .builder() + .httpClient(UrlConnectionHttpClient.builder().build()) + .region(Region.of(System.getenv("AWS_REGION"))) + .build()); + } + + public Function(DynamoDbClient client) { + Idempotency.config().withConfig( + IdempotencyConfig.builder() + .withExpiration(Duration.of(10, ChronoUnit.SECONDS)) + .build()) + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withDynamoDbClient(client) + .withTableName(System.getenv("IDEMPOTENCY_TABLE")) + .build()) + .configure(); + } + + public String handleRequest(Input input, Context context) { + Idempotency.registerLambdaContext(context); + + // This is just to test the generic type support using TypeReference. + // We return the same String to run the same assertions as other idempotency E2E handlers. + Map result = Idempotency.makeIdempotent( + this::processRequest, + input, + new TypeReference>() {}); + + return result.get("timestamp"); + } + + private Map processRequest(Input input) { + DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME.withZone(TimeZone.getTimeZone("UTC").toZoneId()); + Map result = new HashMap<>(); + result.put("timestamp", dtf.format(Instant.now())); + return result; + } +} diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/idempotency-generics/src/main/java/software/amazon/lambda/powertools/e2e/Input.java new file mode 100644 index 000000000..0d14b749e --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -0,0 +1,27 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.e2e; + +public class Input { + private String message; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json new file mode 100644 index 000000000..2780aca09 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-core/reflect-config.json @@ -0,0 +1,13 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntime", + "methods":[{"name":"","parameterTypes":[] }], + "fields":[{"name":"logger"}], + "allPublicMethods":true + }, + { + "name":"com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal", + "methods":[{"name":"","parameterTypes":[] }], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json new file mode 100644 index 000000000..ddda5d5f1 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-events/reflect-config.json @@ -0,0 +1,35 @@ +[ + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json new file mode 100644 index 000000000..91be72f7a --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/jni-config.json @@ -0,0 +1,11 @@ +[ + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClientException", + "methods":[{"name":"","parameterTypes":["java.lang.String","int"] }] + }, + { + "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}], + "allPublicMethods":true + } +] \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties new file mode 100644 index 000000000..20f8b7801 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/native-image.properties @@ -0,0 +1 @@ +Args = --initialize-at-build-time=jdk.xml.internal.SecuritySupport \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json new file mode 100644 index 000000000..e69fa735c --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -0,0 +1,61 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.LambdaRuntime", + "fields": [{ "name": "logger" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogLevel", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "com.amazonaws.services.lambda.runtime.logging.LogFormat", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "allPublicFields": true + }, + { + "name": "java.lang.Void", + "methods": [{ "name": "", "parameterTypes": [] }] + }, + { + "name": "java.util.Collections$UnmodifiableMap", + "fields": [{ "name": "m" }] + }, + { + "name": "jdk.internal.module.IllegalAccessLogger", + "fields": [{ "name": "logger" }] + }, + { + "name": "sun.misc.Unsafe", + "fields": [{ "name": "theUnsafe" }] + }, + { + "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields": [ + { "name": "id" }, + { "name": "invokedFunctionArn" }, + { "name": "deadlineTimeInMs" }, + { "name": "xrayTraceId" }, + { "name": "clientContext" }, + { "name": "cognitoIdentity" }, + { "name": "tenantId" }, + { "name": "content" } + ], + "allPublicMethods": true + } +] diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json new file mode 100644 index 000000000..1062b4249 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/resource-config.json @@ -0,0 +1,19 @@ +{ + "resources": { + "includes": [ + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux-x86_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-aarch_64.so\\E" + }, + { + "pattern": "\\Qjni/libaws-lambda-jni.linux_musl-x86_64.so\\E" + } + ] + }, + "bundles": [] +} \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json new file mode 100644 index 000000000..9890688f9 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-serialization/reflect-config.json @@ -0,0 +1,25 @@ +[ + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.deser.Deserializers[]" + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7HandlersImpl", + "methods": [{ "name": "", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [{ "name": "", "parameterTypes": [] }] + }, + { + "name": "com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ser.Serializers[]" + }, + { + "name": "org.joda.time.DateTime", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json new file mode 100644 index 000000000..9ddd235e2 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/reflect-config.json @@ -0,0 +1,20 @@ +[ + { + "name": "software.amazon.lambda.powertools.e2e.Function", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "software.amazon.lambda.powertools.e2e.Input", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json new file mode 100644 index 000000000..be6aac3f6 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/software.amazon.lambda.powertools.e2e/resource-config.json @@ -0,0 +1,7 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\Qlog4j2.xml\\E" + }]}, + "bundles":[] +} diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/log4j2.xml b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/log4j2.xml new file mode 100644 index 000000000..8925f70b9 --- /dev/null +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/log4j2.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index ae075b71d..7a7da9e02 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -34,6 +34,8 @@ tracing metrics idempotency + idempotency-functional + idempotency-generics parameters validation-alb-event validation-apigw-event diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/IdempotencyE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/IdempotencyE2ET.java index c73a6d761..9ced0e4fb 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/IdempotencyE2ET.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/IdempotencyE2ET.java @@ -23,40 +23,43 @@ import java.util.concurrent.TimeUnit; import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import software.amazon.lambda.powertools.testutils.Infrastructure; import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class IdempotencyE2ET { - private static Infrastructure infrastructure; - private static String functionName; + private Infrastructure infrastructure; + private String functionName; - @BeforeAll - @Timeout(value = 15, unit = TimeUnit.MINUTES) - static void setup() { + private void setupInfrastructure(String pathToFunction) { String random = UUID.randomUUID().toString().substring(0, 6); infrastructure = Infrastructure.builder() - .testName(IdempotencyE2ET.class.getSimpleName()) - .pathToFunction("idempotency") + .testName(IdempotencyE2ET.class.getSimpleName() + "-" + pathToFunction) + .pathToFunction(pathToFunction) .idempotencyTable("idempo" + random) .build(); Map outputs = infrastructure.deploy(); functionName = outputs.get(FUNCTION_NAME_OUTPUT); } - @AfterAll - static void tearDown() { + @AfterEach + void tearDown() { if (infrastructure != null) { infrastructure.destroy(); } } - @Test - void test_ttlNotExpired_sameResult_ttlExpired_differentResult() throws InterruptedException { + @ParameterizedTest + @ValueSource(strings = { "idempotency", "idempotency-functional", "idempotency-generics" }) + @Timeout(value = 15, unit = TimeUnit.MINUTES) + void test_ttlNotExpired_sameResult_ttlExpired_differentResult(String pathToFunction) throws InterruptedException { + setupInfrastructure(pathToFunction); // GIVEN String event = "{\"message\":\"TTL 10sec\"}"; diff --git a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotency.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotency.java index bd564caf8..4f73edea0 100644 --- a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotency.java +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotency.java @@ -14,25 +14,87 @@ package software.amazon.lambda.powertools.idempotency; +import java.util.function.Function; +import java.util.function.Supplier; + import com.amazonaws.services.lambda.runtime.Context; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; + +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyConfigurationException; +import software.amazon.lambda.powertools.idempotency.internal.IdempotencyHandler; import software.amazon.lambda.powertools.idempotency.persistence.BasePersistenceStore; +import software.amazon.lambda.powertools.utilities.JsonConfig; /** - * Holds the configuration for idempotency: - *
    - *
  • The persistence layer to use for persisting the request and response of the function (mandatory).
  • - *
  • The general configuration for idempotency (optional, see {@link IdempotencyConfig.Builder} methods to see defaults values.
  • - *
- *
- * Use it before the function handler ({@link com.amazonaws.services.lambda.runtime.RequestHandler#handleRequest(Object, Context)}) - * get called. - *
- * Example: - *
- *     Idempotency.config().withPersistenceStore(...).configure();
- * 
+ * Idempotency provides both a configuration and a functional API for implementing idempotent workloads. + * + *

This class is thread-safe. All operations delegate to the underlying persistence store + * which handles concurrent access safely.

+ * + *

Configuration

+ *

Configure the persistence layer and idempotency settings before your handler executes (e.g. in constructor):

+ *
{@code
+ * Idempotency.config()
+ *     .withPersistenceStore(persistenceStore)
+ *     .withConfig(idempotencyConfig)
+ *     .configure();
+ * }
+ * + *

Functional API

+ *

Make methods idempotent without AspectJ annotations. Generic return types (e.g., {@code Map}, + * {@code List}) are supported via Jackson {@link TypeReference}.

+ * + *

Important: Always call {@link #registerLambdaContext(Context)} + * at the start of your handler to enable proper timeout handling.

+ * + *

Example usage with Function (single parameter):

+ *
{@code
+ * public Basket handleRequest(Product input, Context context) {
+ *     Idempotency.registerLambdaContext(context);
+ *     return Idempotency.makeIdempotent(this::processProduct, input, Basket.class);
+ * }
+ * 
+ * private Basket processProduct(Product product) {
+ *     // business logic
+ * }
+ * }
+ * + *

Example usage with Supplier (multi-parameter methods):

+ *
{@code
+ * public String handleRequest(SQSEvent event, Context context) {
+ *     Idempotency.registerLambdaContext(context);
+ *     return Idempotency.makeIdempotent(
+ *         event.getRecords().get(0).getBody(),
+ *         () -> processPayment(orderId, amount, currency),
+ *         String.class
+ *     );
+ * }
+ * }
+ * + *

When different methods use the same payload as idempotency key, use explicit function names + * to differentiate between them:

+ *
{@code
+ * // Different methods, same payload
+ * Idempotency.makeIdempotent("processPayment", orderId, 
+ *     () -> processPayment(orderId), String.class);
+ * 
+ * Idempotency.makeIdempotent("refundPayment", orderId, 
+ *     () -> refundPayment(orderId), String.class);
+ * }
+ * + * @see #config() + * @see #registerLambdaContext(Context) + * @see #makeIdempotent(Object, Supplier, Class) + * @see #makeIdempotent(String, Object, Supplier, Class) + * @see #makeIdempotent(Function, Object, Class) + * @see #makeIdempotent(Object, Supplier, TypeReference) + * @see #makeIdempotent(String, Object, Supplier, TypeReference) + * @see #makeIdempotent(Function, Object, TypeReference) */ -public class Idempotency { +public final class Idempotency { + private static final String DEFAULT_FUNCTION_NAME = "function"; + private IdempotencyConfig config; private BasePersistenceStore persistenceStore; @@ -81,7 +143,7 @@ private void setPersistenceStore(BasePersistenceStore persistenceStore) { this.persistenceStore = persistenceStore; } - private static class Holder { + private static final class Holder { private static final Idempotency instance = new Idempotency(); } @@ -116,5 +178,151 @@ public Config withConfig(IdempotencyConfig config) { } } + // Functional API methods + + /** + * Makes a function idempotent using the provided idempotency key. + * Uses a default function name for namespacing the idempotency key. + * + *

This method is thread-safe and can be used in parallel processing scenarios + * such as batch processors.

+ * + *

This method is suitable for making methods idempotent that have more than one parameter. + * For simple single-parameter methods, {@link #makeIdempotent(Function, Object, Class)} is more intuitive.

+ * + *

Note: If you need to call different functions with the same payload, + * use {@link #makeIdempotent(String, Object, Supplier, Class)} to specify distinct function names. + * This ensures each function has its own idempotency scope.

+ * + * @param idempotencyKey the key used for idempotency (will be converted to JSON) + * @param function the function to make idempotent + * @param returnType the class of the return type for deserialization + * @param the return type of the function + * @return the result of the function execution (either fresh or cached) + */ + public static T makeIdempotent(Object idempotencyKey, Supplier function, Class returnType) { + return makeIdempotent(DEFAULT_FUNCTION_NAME, idempotencyKey, function, returnType); + } + + /** + * Makes a function idempotent using the provided function name and idempotency key. + * + *

This method is thread-safe and can be used in parallel processing scenarios + * such as batch processors.

+ * + * @param functionName the name of the function (used for persistence store configuration) + * @param idempotencyKey the key used for idempotency (will be converted to JSON) + * @param function the function to make idempotent + * @param returnType the class of the return type for deserialization + * @param the return type of the function + * @return the result of the function execution (either fresh or cached) + */ + public static T makeIdempotent(String functionName, Object idempotencyKey, Supplier function, + Class returnType) { + return makeIdempotent(functionName, idempotencyKey, function, JsonConfig.toTypeReference(returnType)); + } + + /** + * Makes a function with one parameter idempotent. + * The parameter is used as the idempotency key. + * + *

For functions with more than one parameter, use {@link #makeIdempotent(Object, Supplier, Class)} instead.

+ * + *

Note: If you need to call different functions with the same payload, + * use {@link #makeIdempotent(String, Object, Supplier, Class)} to specify distinct function names. + * This ensures each function has its own idempotency scope.

+ * + * @param function the function to make idempotent (method reference) + * @param arg the argument to pass to the function (also used as idempotency key) + * @param returnType the class of the return type for deserialization + * @param the argument type + * @param the return type + * @return the result of the function execution (either fresh or cached) + */ + public static R makeIdempotent(Function function, T arg, Class returnType) { + return makeIdempotent(DEFAULT_FUNCTION_NAME, arg, () -> function.apply(arg), returnType); + } + + /** + * Makes a function idempotent using the provided idempotency key with support for generic return types. + * Uses a default function name for namespacing the idempotency key. + * + *

Use this method when the return type contains generics (e.g., {@code Map}).

+ * + *

Example usage:

+ *
{@code
+     * Map result = Idempotency.makeIdempotent(
+     *     payload,
+     *     () -> processBaskets(),
+     *     new TypeReference>() {}
+     * );
+     * }
+ * + * @param idempotencyKey the key used for idempotency (will be converted to JSON) + * @param function the function to make idempotent + * @param typeRef the TypeReference for deserialization of generic types + * @param the return type of the function + * @return the result of the function execution (either fresh or cached) + */ + public static T makeIdempotent(Object idempotencyKey, Supplier function, TypeReference typeRef) { + return makeIdempotent(DEFAULT_FUNCTION_NAME, idempotencyKey, function, typeRef); + } + + /** + * Makes a function idempotent using the provided function name and idempotency key with support for generic return types. + * + * @param functionName the name of the function (used for persistence store configuration) + * @param idempotencyKey the key used for idempotency (will be converted to JSON) + * @param function the function to make idempotent + * @param typeRef the TypeReference for deserialization of generic types + * @param the return type of the function + * @return the result of the function execution (either fresh or cached) + */ + @SuppressWarnings("unchecked") + public static T makeIdempotent(String functionName, Object idempotencyKey, Supplier function, + TypeReference typeRef) { + try { + JsonNode payload = JsonConfig.get().getObjectMapper().valueToTree(idempotencyKey); + Context lambdaContext = Idempotency.getInstance().getConfig().getLambdaContext(); + + IdempotencyHandler handler = new IdempotencyHandler( + function::get, + typeRef, + functionName, + payload, + lambdaContext); + + Object result = handler.handle(); + return (T) result; + } catch (RuntimeException e) { + throw e; + } catch (Throwable e) { + throw new IdempotencyConfigurationException("Idempotency operation failed: " + e.getMessage()); + } + } + + /** + * Makes a function with one parameter idempotent with support for generic return types. + * The parameter is used as the idempotency key. + * + *

Example usage:

+ *
{@code
+     * Map result = Idempotency.makeIdempotent(
+     *     this::processProduct,
+     *     product,
+     *     new TypeReference>() {}
+     * );
+     * }
+ * + * @param function the function to make idempotent (method reference) + * @param arg the argument to pass to the function (also used as idempotency key) + * @param typeRef the TypeReference for deserialization of generic types + * @param the argument type + * @param the return type + * @return the result of the function execution (either fresh or cached) + */ + public static R makeIdempotent(Function function, T arg, TypeReference typeRef) { + return makeIdempotent(DEFAULT_FUNCTION_NAME, arg, () -> function.apply(arg), typeRef); + } } diff --git a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java index 9d5c66cac..0d9504483 100644 --- a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java @@ -25,7 +25,7 @@ /** * Configuration of the idempotency feature. Use the {@link Builder} to create an instance. */ -public class IdempotencyConfig { +public final class IdempotencyConfig { private final int localCacheMaxItems; private final boolean useLocalCache; private final long expirationInSeconds; @@ -34,7 +34,7 @@ public class IdempotencyConfig { private final boolean throwOnNoIdempotencyKey; private final String hashFunction; private final BiFunction responseHook; - private Context lambdaContext; + private final InheritableThreadLocal lambdaContext = new InheritableThreadLocal<>(); private IdempotencyConfig(String eventKeyJMESPath, String payloadValidationJMESPath, boolean throwOnNoIdempotencyKey, boolean useLocalCache, int localCacheMaxItems, @@ -87,11 +87,11 @@ public String getHashFunction() { } public Context getLambdaContext() { - return lambdaContext; + return lambdaContext.get(); } public void setLambdaContext(Context lambdaContext) { - this.lambdaContext = lambdaContext; + this.lambdaContext.set(lambdaContext); } public BiFunction getResponseHook() { diff --git a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java index 0466f244f..77d3d49b0 100644 --- a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java @@ -21,12 +21,11 @@ import java.util.OptionalInt; import java.util.function.BiFunction; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.amazonaws.services.lambda.runtime.Context; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import software.amazon.lambda.powertools.idempotency.Idempotency; @@ -50,13 +49,21 @@ public class IdempotencyHandler { private static final Logger LOG = LoggerFactory.getLogger(IdempotencyHandler.class); private static final int MAX_RETRIES = 2; - private final ProceedingJoinPoint pjp; + private final IdempotentFunction function; + private final TypeReference returnTypeRef; private final JsonNode data; private final BasePersistenceStore persistenceStore; private final Context lambdaContext; - public IdempotencyHandler(ProceedingJoinPoint pjp, String functionName, JsonNode payload, Context lambdaContext) { - this.pjp = pjp; + public IdempotencyHandler(IdempotentFunction function, Class returnType, String functionName, + JsonNode payload, Context lambdaContext) { + this(function, JsonConfig.toTypeReference(returnType), functionName, payload, lambdaContext); + } + + public IdempotencyHandler(IdempotentFunction function, TypeReference returnTypeRef, String functionName, + JsonNode payload, Context lambdaContext) { + this.function = function; + this.returnTypeRef = returnTypeRef; this.data = payload; this.lambdaContext = lambdaContext; persistenceStore = Idempotency.getInstance().getPersistenceStore(); @@ -171,7 +178,6 @@ private Object handleForStatus(DataRecord record) { "Execution already in progress with idempotency key: " + record.getIdempotencyKey()); } - Class returnType = ((MethodSignature) pjp.getSignature()).getReturnType(); try { LOG.debug("Response for key '{}' retrieved from idempotency store, skipping the function", record.getIdempotencyKey()); @@ -180,12 +186,12 @@ private Object handleForStatus(DataRecord record) { .getResponseHook(); final Object responseData; - if (returnType.equals(String.class)) { + if (String.class.equals(returnTypeRef.getType())) { // Primitive String data will be returned raw and not de-serialized from JSON. responseData = record.getResponseData(); } else { - responseData = JsonConfig.get().getObjectMapper().reader().readValue(record.getResponseData(), - returnType); + responseData = JsonConfig.get().getObjectMapper().readValue(record.getResponseData(), + returnTypeRef); } if (responseHook != null) { @@ -196,14 +202,14 @@ private Object handleForStatus(DataRecord record) { return responseData; } catch (Exception e) { throw new IdempotencyPersistenceLayerException( - "Unable to get function response as " + returnType.getSimpleName(), e); + "Unable to get function response as " + returnTypeRef.getType().getTypeName(), e); } } private Object getFunctionResponse() throws Throwable { Object response; try { - response = pjp.proceed(pjp.getArgs()); + response = function.execute(); } catch (Throwable handlerException) { // We need these nested blocks to preserve function's exception in case the persistence store operation // also raises an exception diff --git a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java index ea6d743f0..35c6dee40 100644 --- a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java @@ -14,14 +14,21 @@ package software.amazon.lambda.powertools.idempotency.internal; -import com.amazonaws.services.lambda.runtime.Context; -import com.fasterxml.jackson.databind.JsonNode; +import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.placedOnRequestHandler; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.DeclarePrecedence; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; + +import com.amazonaws.services.lambda.runtime.Context; +import com.fasterxml.jackson.databind.JsonNode; + import software.amazon.lambda.powertools.idempotency.Constants; import software.amazon.lambda.powertools.idempotency.Idempotency; import software.amazon.lambda.powertools.idempotency.IdempotencyKey; @@ -29,11 +36,6 @@ import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyConfigurationException; import software.amazon.lambda.powertools.utilities.JsonConfig; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; - -import static software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor.placedOnRequestHandler; - /** * Aspect that handles the {@link Idempotent} annotation. * It uses the {@link IdempotencyHandler} to actually do the job. @@ -42,14 +44,15 @@ // Idempotency annotation should come first before large message @DeclarePrecedence("software.amazon.lambda.powertools.idempotency.internal.IdempotentAspect, *") public class IdempotentAspect { - @SuppressWarnings({"EmptyMethod"}) + @SuppressWarnings({ "EmptyMethod" }) @Pointcut("@annotation(idempotent)") public void callAt(Idempotent idempotent) { + // Pointcut method - body intentionally empty } @Around(value = "callAt(idempotent) && execution(@Idempotent * *.*(..))", argNames = "pjp,idempotent") public Object around(ProceedingJoinPoint pjp, - Idempotent idempotent) throws Throwable { + Idempotent idempotent) throws Throwable { String idempotencyDisabledEnv = System.getenv().get(Constants.IDEMPOTENCY_DISABLED_ENV); if (idempotencyDisabledEnv != null && !"false".equalsIgnoreCase(idempotencyDisabledEnv)) { @@ -76,7 +79,12 @@ public Object around(ProceedingJoinPoint pjp, lambdaContext = Idempotency.getInstance().getConfig().getLambdaContext(); } - IdempotencyHandler idempotencyHandler = new IdempotencyHandler(pjp, method.getName(), payload, lambdaContext); + IdempotencyHandler idempotencyHandler = new IdempotencyHandler( + () -> pjp.proceed(pjp.getArgs()), + method.getReturnType(), + method.getName(), + payload, + lambdaContext); return idempotencyHandler.handle(); } diff --git a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentFunction.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentFunction.java new file mode 100644 index 000000000..2ff2071b0 --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentFunction.java @@ -0,0 +1,30 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.idempotency.internal; + +/** + * Functional interface for idempotent function execution. + *

+ * This interface is similar to {@link java.util.concurrent.Callable} but throws {@link Throwable} + * instead of {@link Exception}. This is necessary to support AspectJ's {@code ProceedingJoinPoint.proceed()} + * which throws {@code Throwable}, allowing exceptions to bubble up naturally without wrapping. + * + * @param the return type of the function + */ +@FunctionalInterface +public interface IdempotentFunction { + @SuppressWarnings("java:S112") // Throwable is required for AspectJ compatibility + T execute() throws Throwable; +} diff --git a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java index b57ad2977..a3bf1c5ff 100644 --- a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java @@ -35,7 +35,7 @@ public LRUCache(int capacity) { } @Override - protected boolean removeEldestEntry(Map.Entry entry) { - return (size() > this.capacity); + protected boolean removeEldestEntry(Map.Entry entry) { + return size() > this.capacity; } } diff --git a/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java new file mode 100644 index 000000000..10664a25b --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java @@ -0,0 +1,311 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.idempotency; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import java.util.HashMap; +import java.util.Map; +import java.util.OptionalInt; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.amazonaws.services.lambda.runtime.Context; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; + +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; +import software.amazon.lambda.powertools.idempotency.handlers.IdempotencyFunctionalFunction; +import software.amazon.lambda.powertools.idempotency.handlers.IdempotencyMultiArgFunctionalFunction; +import software.amazon.lambda.powertools.idempotency.model.Basket; +import software.amazon.lambda.powertools.idempotency.model.Product; +import software.amazon.lambda.powertools.idempotency.persistence.BasePersistenceStore; +import software.amazon.lambda.powertools.idempotency.testutils.InMemoryPersistenceStore; + +@ExtendWith(MockitoExtension.class) +class IdempotencyTest { + + private Context context = new TestLambdaContext(); + + @Mock + private BasePersistenceStore store; + + @Test + void firstCall_shouldPutInStore() { + Idempotency.config() + .withPersistenceStore(store) + .withConfig(IdempotencyConfig.builder() + .withEventKeyJMESPath("id") + .build()) + .configure(); + + IdempotencyFunctionalFunction function = new IdempotencyFunctionalFunction(); + + Product p = new Product(42, "fake product", 12); + Basket basket = function.handleRequest(p, context); + + assertThat(function.processCalled()).isTrue(); + assertThat(basket.getProducts()).hasSize(1); + + ArgumentCaptor nodeCaptor = ArgumentCaptor.forClass(JsonNode.class); + ArgumentCaptor expiryCaptor = ArgumentCaptor.forClass(OptionalInt.class); + verify(store).saveInProgress(nodeCaptor.capture(), any(), expiryCaptor.capture()); + assertThat(nodeCaptor.getValue().get("id").asLong()).isEqualTo(p.getId()); + assertThat(nodeCaptor.getValue().get("name").asText()).isEqualTo(p.getName()); + assertThat(nodeCaptor.getValue().get("price").asDouble()).isEqualTo(p.getPrice()); + assertThat(expiryCaptor.getValue().orElse(-1)).isEqualTo(30000); + + ArgumentCaptor resultCaptor = ArgumentCaptor.forClass(Basket.class); + verify(store).saveSuccess(any(), resultCaptor.capture(), any()); + assertThat(resultCaptor.getValue()).isEqualTo(basket); + } + + @Test + void testMakeIdempotentWithFunctionName() throws Throwable { + BasePersistenceStore spyStore = spy(BasePersistenceStore.class); + Idempotency.config() + .withPersistenceStore(spyStore) + .configure(); + Idempotency.registerLambdaContext(context); + + String result = Idempotency.makeIdempotent("myFunction", "test-key", () -> "test-result", + String.class); + + assertThat(result).isEqualTo("test-result"); + + ArgumentCaptor functionNameCaptor = ArgumentCaptor.forClass(String.class); + verify(spyStore).configure(any(), functionNameCaptor.capture()); + assertThat(functionNameCaptor.getValue()).isEqualTo("myFunction"); + } + + @Test + void testMakeIdempotentWithMethodReferenceUsesDefaultName() throws Throwable { + BasePersistenceStore spyStore = spy(BasePersistenceStore.class); + Idempotency.config() + .withPersistenceStore(spyStore) + .configure(); + Idempotency.registerLambdaContext(context); + + String result = Idempotency.makeIdempotent("test-key", this::helperMethod, String.class); + + assertThat(result).isEqualTo("helper-result"); + + ArgumentCaptor functionNameCaptor = ArgumentCaptor.forClass(String.class); + verify(spyStore).configure(any(), functionNameCaptor.capture()); + assertThat(functionNameCaptor.getValue()).isEqualTo("function"); + } + + private String helperMethod() { + return "helper-result"; + } + + @Test + void testMakeIdempotentWithFunctionOverload() throws Throwable { + BasePersistenceStore spyStore = spy(BasePersistenceStore.class); + Idempotency.config() + .withPersistenceStore(spyStore) + .configure(); + Idempotency.registerLambdaContext(context); + + Product p = new Product(42, "test product", 10); + Basket result = Idempotency.makeIdempotent(this::processProduct, p, Basket.class); + + assertThat(result.getProducts()).hasSize(1); + assertThat(result.getProducts().get(0)).isEqualTo(p); + + ArgumentCaptor functionNameCaptor = ArgumentCaptor.forClass(String.class); + verify(spyStore).configure(any(), functionNameCaptor.capture()); + assertThat(functionNameCaptor.getValue()).isEqualTo("function"); + + ArgumentCaptor nodeCaptor = ArgumentCaptor.forClass(JsonNode.class); + verify(spyStore).saveInProgress(nodeCaptor.capture(), any(), any()); + assertThat(nodeCaptor.getValue().get("id").asLong()).isEqualTo(p.getId()); + } + + private Basket processProduct(Product product) { + Basket basket = new Basket(); + basket.add(product); + return basket; + } + + @Test + void firstCall_withExplicitIdempotencyKey_shouldPutInStore() { + Idempotency.config() + .withPersistenceStore(store) + .configure(); + + IdempotencyMultiArgFunctionalFunction function = new IdempotencyMultiArgFunctionalFunction(); + + Product p = new Product(42, "fake product", 12); + Basket basket = function.handleRequest(p, context); + + assertThat(function.processCalled()).isTrue(); + assertThat(function.getExtraData()).isEqualTo("extra-data"); + assertThat(basket.getProducts()).hasSize(1); + + ArgumentCaptor nodeCaptor = ArgumentCaptor.forClass(JsonNode.class); + verify(store).saveInProgress(nodeCaptor.capture(), any(), any()); + assertThat(nodeCaptor.getValue().asLong()).isEqualTo(p.getId()); + + ArgumentCaptor resultCaptor = ArgumentCaptor.forClass(Basket.class); + verify(store).saveSuccess(any(), resultCaptor.capture(), any()); + assertThat(resultCaptor.getValue()).isEqualTo(basket); + } + + @Test + void secondCall_shouldRetrieveFromCacheAndDeserialize() throws Throwable { + InMemoryPersistenceStore inMemoryStore = new InMemoryPersistenceStore(); + + Idempotency.config() + .withPersistenceStore(inMemoryStore) + .configure(); + Idempotency.registerLambdaContext(context); + + Product p = new Product(42, "test product", 10); + int[] callCount = { 0 }; + + // First call - executes function and stores result + Basket result1 = Idempotency.makeIdempotent(p, () -> { + callCount[0]++; + return processProduct(p); + }, Basket.class); + assertThat(result1.getProducts()).hasSize(1); + assertThat(callCount[0]).isEqualTo(1); + + // Second call - should retrieve from cache, deserialize, and NOT execute function + Basket result2 = Idempotency.makeIdempotent(p, () -> { + callCount[0]++; + return processProduct(p); + }, Basket.class); + assertThat(result2.getProducts()).hasSize(1); + assertThat(result2.getProducts().get(0).getId()).isEqualTo(42); + assertThat(result2.getProducts().get(0).getName()).isEqualTo("test product"); + assertThat(callCount[0]).isEqualTo(1); // Function should NOT be called again + } + + @Test + void concurrentInvocations_shouldNotLeakContext() throws Exception { + Idempotency.config() + .withPersistenceStore(store) + .configure(); + + IdempotencyMultiArgFunctionalFunction function = new IdempotencyMultiArgFunctionalFunction(); + + // GIVEN + int threadCount = 10; + Thread[] threads = new Thread[threadCount]; + Context[] capturedContexts = new Context[threadCount]; + int[] capturedRemainingTimes = new int[threadCount]; + boolean[] success = new boolean[threadCount]; + + // WHEN - Multiple threads call handleRequest with different contexts + for (int i = 0; i < threadCount; i++) { + final int threadIndex = i; + final int expectedTime = (i + 1) * 2000; // 2000, 4000, 6000, ..., 20000 + + final Context threadContext = new TestLambdaContext() { + @Override + public int getRemainingTimeInMillis() { + return expectedTime; + } + }; + + threads[i] = new Thread(() -> { + try { + Product p = new Product(threadIndex, "product" + threadIndex, 10); + function.handleRequest(p, threadContext); + + // Capture the context that was actually stored in ThreadLocal by this thread + Context captured = Idempotency.getInstance().getConfig().getLambdaContext(); + capturedContexts[threadIndex] = captured; + capturedRemainingTimes[threadIndex] = captured != null ? captured.getRemainingTimeInMillis() : -1; + success[threadIndex] = true; + } catch (Exception e) { + success[threadIndex] = false; + } + }); + } + + // Start all threads + for (Thread thread : threads) { + thread.start(); + } + + // Wait for all threads to complete + for (Thread thread : threads) { + thread.join(); + } + + // THEN - All threads should complete successfully + for (boolean result : success) { + assertThat(result).isTrue(); + } + + // THEN - Each thread should have captured its own context (no leakage) + for (int i = 0; i < threadCount; i++) { + int expectedTime = (i + 1) * 2000; + assertThat(capturedRemainingTimes[i]) + .as("Thread %d should have remaining time %d", i, expectedTime) + .isEqualTo(expectedTime); + assertThat(capturedContexts[i]).as("Thread %d should have non-null context", i).isNotNull(); + } + } + + @Test + void testMakeIdempotentWithGenericType() throws Throwable { + InMemoryPersistenceStore inMemoryStore = new InMemoryPersistenceStore(); + + Idempotency.config() + .withPersistenceStore(inMemoryStore) + .configure(); + Idempotency.registerLambdaContext(context); + + int[] callCount = { 0 }; + + // First call - executes function and stores result + Map result1 = Idempotency.makeIdempotent("test-key", () -> { + callCount[0]++; + Map map = new HashMap<>(); + Basket basket = new Basket(); + basket.add(new Product(1, "product1", 10)); + map.put("basket1", basket); + return map; + }, new TypeReference>() { + }); + + assertThat(result1).hasSize(1); + assertThat(result1.get("basket1").getProducts()).hasSize(1); + assertThat(callCount[0]).isEqualTo(1); + + // Second call - should retrieve from cache and deserialize correctly + Map result2 = Idempotency.makeIdempotent("test-key", () -> { + callCount[0]++; + return new HashMap<>(); + }, new TypeReference>() { + }); + + assertThat(result2).hasSize(1); + assertThat(result2.get("basket1").getProducts()).hasSize(1); + assertThat(result2.get("basket1").getProducts().get(0).getName()).isEqualTo("product1"); + assertThat(callCount[0]).isEqualTo(1); // Function should NOT be called again + } +} diff --git a/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunctionalFunction.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunctionalFunction.java new file mode 100644 index 000000000..c2a85d178 --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunctionalFunction.java @@ -0,0 +1,48 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.idempotency.handlers; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import software.amazon.lambda.powertools.idempotency.Idempotency; +import software.amazon.lambda.powertools.idempotency.model.Basket; +import software.amazon.lambda.powertools.idempotency.model.Product; + +/** + * Lambda function using Idempotency functional API without AspectJ annotations + */ +public class IdempotencyFunctionalFunction implements RequestHandler { + + private boolean processCalled = false; + + public boolean processCalled() { + return processCalled; + } + + @Override + public Basket handleRequest(Product input, Context context) { + Idempotency.registerLambdaContext(context); + + return Idempotency.makeIdempotent(this::process, input, Basket.class); + } + + private Basket process(Product input) { + processCalled = true; + Basket b = new Basket(); + b.add(input); + return b; + } +} diff --git a/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyMultiArgFunctionalFunction.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyMultiArgFunctionalFunction.java new file mode 100644 index 000000000..42e75fd55 --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyMultiArgFunctionalFunction.java @@ -0,0 +1,54 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.idempotency.handlers; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import software.amazon.lambda.powertools.idempotency.Idempotency; +import software.amazon.lambda.powertools.idempotency.model.Basket; +import software.amazon.lambda.powertools.idempotency.model.Product; + +/** + * Lambda function using Idempotency functional API with explicit idempotency key + */ +public class IdempotencyMultiArgFunctionalFunction implements RequestHandler { + + private boolean processCalled = false; + private String extraData; + + public boolean processCalled() { + return processCalled; + } + + public String getExtraData() { + return extraData; + } + + @Override + public Basket handleRequest(Product input, Context context) { + Idempotency.registerLambdaContext(context); + + return Idempotency.makeIdempotent(input.getId(), () -> process(input, "extra-data"), Basket.class); + } + + private Basket process(Product input, String extraData) { + processCalled = true; + this.extraData = extraData; + Basket b = new Basket(); + b.add(input); + return b; + } +} diff --git a/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java index 0ccb1e5aa..384a20b2a 100644 --- a/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java +++ b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java @@ -194,7 +194,7 @@ void secondCall_notExpired_shouldNotGetFromStoreIfPresentOnIdempotencyException( "Test message", new RuntimeException("Test Cause"), dr)) - .when(store).saveInProgress(any(), any(), any()); + .when(store).saveInProgress(any(), any(), any()); // WHEN IdempotencyEnabledFunction function = new IdempotencyEnabledFunction(); @@ -538,4 +538,73 @@ void idempotencyOnSubMethodVoid_shouldThrowException() { IdempotencyConfigurationException.class); } + @Test + void concurrentInvocations_shouldNotLeakContext() throws Exception { + Idempotency.config() + .withPersistenceStore(store) + .configure(); + + // Use IdempotencyInternalFunction which calls registerLambdaContext + IdempotencyInternalFunction function = new IdempotencyInternalFunction(true); + + // GIVEN + int threadCount = 10; + Thread[] threads = new Thread[threadCount]; + Context[] capturedContexts = new Context[threadCount]; + int[] capturedRemainingTimes = new int[threadCount]; + boolean[] success = new boolean[threadCount]; + + // WHEN - Multiple threads call handleRequest with different contexts + for (int i = 0; i < threadCount; i++) { + final int threadIndex = i; + final int expectedTime = (i + 1) * 1000; // 1000, 2000, 3000, ..., 10000 + + final Context threadContext = new TestLambdaContext() { + @Override + public int getRemainingTimeInMillis() { + return expectedTime; + } + }; + + threads[i] = new Thread(() -> { + try { + Product p = new Product(threadIndex, "product" + threadIndex, 10); + function.handleRequest(p, threadContext); + + // Capture the context that was actually stored in ThreadLocal by this thread + Context captured = Idempotency.getInstance().getConfig().getLambdaContext(); + capturedContexts[threadIndex] = captured; + capturedRemainingTimes[threadIndex] = captured != null ? captured.getRemainingTimeInMillis() : -1; + success[threadIndex] = true; + } catch (Exception e) { + success[threadIndex] = false; + } + }); + } + + // Start all threads + for (Thread thread : threads) { + thread.start(); + } + + // Wait for all threads to complete + for (Thread thread : threads) { + thread.join(); + } + + // THEN - All threads should complete successfully + for (boolean result : success) { + assertThat(result).isTrue(); + } + + // THEN - Each thread should have captured its own context (no leakage) + for (int i = 0; i < threadCount; i++) { + int expectedTime = (i + 1) * 1000; + assertThat(capturedRemainingTimes[i]) + .as("Thread %d should have remaining time %d", i, expectedTime) + .isEqualTo(expectedTime); + assertThat(capturedContexts[i]).as("Thread %d should have non-null context", i).isNotNull(); + } + } + } diff --git a/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/testutils/InMemoryPersistenceStore.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/testutils/InMemoryPersistenceStore.java new file mode 100644 index 000000000..5fec80a3c --- /dev/null +++ b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/testutils/InMemoryPersistenceStore.java @@ -0,0 +1,58 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.idempotency.testutils; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; + +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; +import software.amazon.lambda.powertools.idempotency.persistence.BasePersistenceStore; +import software.amazon.lambda.powertools.idempotency.persistence.DataRecord; + +/** + * In-memory implementation of BasePersistenceStore for testing purposes. + */ +public class InMemoryPersistenceStore extends BasePersistenceStore { + private final Map data = new HashMap<>(); + + @Override + public DataRecord getRecord(String idempotencyKey) throws IdempotencyItemNotFoundException { + DataRecord dr = data.get(idempotencyKey); + if (dr == null) { + throw new IdempotencyItemNotFoundException(idempotencyKey); + } + return dr; + } + + @Override + public void putRecord(DataRecord dr, Instant now) throws IdempotencyItemAlreadyExistsException { + if (data.containsKey(dr.getIdempotencyKey())) { + throw new IdempotencyItemAlreadyExistsException(); + } + data.put(dr.getIdempotencyKey(), dr); + } + + @Override + public void updateRecord(DataRecord dr) { + data.put(dr.getIdempotencyKey(), dr); + } + + @Override + public void deleteRecord(String idempotencyKey) { + data.remove(idempotencyKey); + } +} diff --git a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/JsonConfig.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/JsonConfig.java index fc0f083e5..c6a986564 100644 --- a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/JsonConfig.java +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/JsonConfig.java @@ -14,19 +14,23 @@ package software.amazon.lambda.powertools.utilities; +import java.lang.reflect.Type; +import java.util.function.Supplier; + import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.json.JsonMapper; + import io.burt.jmespath.JmesPath; import io.burt.jmespath.RuntimeConfiguration; import io.burt.jmespath.function.BaseFunction; import io.burt.jmespath.function.FunctionRegistry; import io.burt.jmespath.jackson.JacksonRuntime; -import java.util.function.Supplier; import software.amazon.lambda.powertools.utilities.jmespath.Base64Function; import software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction; import software.amazon.lambda.powertools.utilities.jmespath.JsonFunction; @@ -51,8 +55,7 @@ public final class JsonConfig { private final FunctionRegistry customFunctions = defaultFunctions.extend( new Base64Function(), new Base64GZipFunction(), - new JsonFunction() - ); + new JsonFunction()); private final RuntimeConfiguration configuration = new RuntimeConfiguration.Builder() .withSilentTypeErrors(true) @@ -77,6 +80,23 @@ public ObjectMapper getObjectMapper() { return om.get(); } + /** + * Creates a TypeReference from a Class for use with Jackson deserialization. + * This is useful when you need to convert a Class to a TypeReference for generic type handling. + * + * @param clazz the class to convert to TypeReference + * @param the type parameter + * @return a TypeReference wrapping the provided class + */ + public static TypeReference toTypeReference(Class clazz) { + return new TypeReference() { + @Override + public Type getType() { + return clazz; + } + }; + } + /** * Return the JmesPath used to select sub node of Json * @@ -103,7 +123,7 @@ public void addFunction(T function) { jmesPath = new JacksonRuntime(updatedConfig, getObjectMapper()); } - private static class ConfigHolder { + private static final class ConfigHolder { private static final JsonConfig instance = new JsonConfig(); } } From dcb2b605b13cfa9391ca79795fbf7955b6cf8e74 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 14:23:48 +0100 Subject: [PATCH 02/76] chore: bump aws.sdk.version from 2.36.2 to 2.37.1 (#2246) Bumps `aws.sdk.version` from 2.36.2 to 2.37.1. Updates `software.amazon.awssdk:url-connection-client` from 2.36.2 to 2.37.1 Updates `software.amazon.awssdk:sdk-core` from 2.36.2 to 2.37.1 Updates `software.amazon.awssdk:s3` from 2.36.2 to 2.37.1 Updates `software.amazon.awssdk:kinesis` from 2.36.2 to 2.37.1 Updates `software.amazon.awssdk:sqs` from 2.36.2 to 2.37.1 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.36.2 to 2.37.1 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.37.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sdk-core dependency-version: 2.37.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.37.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.37.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.37.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-version: 2.37.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index a5d14f0c1..19b404c82 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ 11 11 1.9.20.1 - 2.36.2 + 2.37.1 From 74ca992a317346bd991024ad8988a117e1466f0a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 14:24:13 +0100 Subject: [PATCH 03/76] chore: bump software.amazon.awscdk:aws-cdk-lib from 2.221.0 to 2.221.1 (#2248) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.221.0 to 2.221.1. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/v2.221.1/CHANGELOG.v2.alpha.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.221.0...v2.221.1) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-version: 2.221.1 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 42777cc96..ce887884c 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -7,7 +7,7 @@ 2.5.0 UTF-8 - 2.221.0 + 2.221.1 [10.0.0,11.0.0) 5.14.0 diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 046592e1c..557611a93 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ 11 11 10.4.2 - 2.221.0 + 2.221.1 From 6f2f262acbe248b0a6e408386be82d8a85b6d393 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 14:24:46 +0100 Subject: [PATCH 04/76] chore: bump github/codeql-action from 4.31.0 to 4.31.1 (#2249) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.0 to 4.31.1. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/4e94bd11f71e507f7f87df81788dff88d1dacbfb...5fe9434cd24fe243e33e7f3305f8a5b519b70280) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.31.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 4ca976695..632e4b0d9 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v3.29.5 + uses: github/codeql-action/upload-sarif@5fe9434cd24fe243e33e7f3305f8a5b519b70280 # v3.29.5 with: sarif_file: results.sarif From 6e4701c75da366465b25cac43ea7669aa96bf616 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 14:25:00 +0100 Subject: [PATCH 05/76] chore: bump aws.sdk.version from 2.36.2 to 2.36.3 (#2242) Bumps `aws.sdk.version` from 2.36.2 to 2.36.3. Updates `software.amazon.awssdk:bom` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:http-client-spi` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:url-connection-client` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:s3` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:dynamodb` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:lambda` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:kinesis` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:cloudwatch` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:xray` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:sqs` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:cloudformation` from 2.36.2 to 2.36.3 Updates `software.amazon.awssdk:sts` from 2.36.2 to 2.36.3 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.36.3 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.36.3 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.36.3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.36.3 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.36.3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.36.3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.36.3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.36.3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.36.3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.36.3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.36.3 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.36.3 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 3d9bb3c67..7f9bbc6fa 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ 11 1.4.0 3.16.1 - 2.36.2 + 2.36.3 1.9.20.1 diff --git a/pom.xml b/pom.xml index 878826077..449baba05 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ 2.25.2 2.0.17 2.20.0 - 2.36.2 + 2.36.3 2.20.0 2.2.0 UTF-8 diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 7a7da9e02..86e867548 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ 3.6.1 1.14.1 3.14.1 - 2.36.2 + 2.36.3 1.9.20.1 true From e1dbd95997b00704cf509cf3fe04dd7b2d6c679b Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Fri, 31 Oct 2025 11:40:40 +0100 Subject: [PATCH 06/76] fix(serialization): Fix Joda time serialization for AWS events. (#2252) --- powertools-serialization/pom.xml | 4 + .../powertools/utilities/JsonConfig.java | 5 +- .../utilities/EventDeserializerTest.java | 198 ++++++++++++++---- .../jmespath/Base64FunctionTest.java | 19 +- .../jmespath/Base64GZipFunctionTest.java | 30 +-- .../utilities/jmespath/JsonFunctionTest.java | 13 +- 6 files changed, 204 insertions(+), 65 deletions(-) diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 89fb2056a..1ed2ee259 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -47,6 +47,10 @@ com.fasterxml.jackson.core jackson-databind + + com.fasterxml.jackson.datatype + jackson-datatype-joda + diff --git a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/JsonConfig.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/JsonConfig.java index c6a986564..ac10412fa 100644 --- a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/JsonConfig.java +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/JsonConfig.java @@ -25,6 +25,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.joda.JodaModule; import io.burt.jmespath.JmesPath; import io.burt.jmespath.RuntimeConfiguration; @@ -41,11 +42,13 @@ public final class JsonConfig { // Don't throw an exception when json has extra fields you are not serializing on. .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) // Ignore null values when writing json. - .serializationInclusion(JsonInclude.Include.NON_NULL) + .defaultPropertyInclusion( + JsonInclude.Value.construct(JsonInclude.Include.NON_NULL, JsonInclude.Include.USE_DEFAULTS)) // Write times as a String instead of a Long so its human-readable. .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) // Sort fields in alphabetical order .configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true) + .addModule(new JodaModule()) .build(); private static final ThreadLocal om = ThreadLocal.withInitial(objectMapperSupplier); diff --git a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/EventDeserializerTest.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/EventDeserializerTest.java index 2914bd286..14fc32231 100644 --- a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/EventDeserializerTest.java +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/EventDeserializerTest.java @@ -18,6 +18,13 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static software.amazon.lambda.powertools.utilities.EventDeserializer.extractDataFrom; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; import com.amazonaws.services.lambda.runtime.events.ActiveMQEvent; @@ -34,41 +41,37 @@ import com.amazonaws.services.lambda.runtime.events.SQSEvent; import com.amazonaws.services.lambda.runtime.events.ScheduledEvent; import com.amazonaws.services.lambda.runtime.tests.annotations.Event; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; +import com.fasterxml.jackson.databind.JsonNode; + import software.amazon.lambda.powertools.utilities.model.Order; import software.amazon.lambda.powertools.utilities.model.Product; -public class EventDeserializerTest { +class EventDeserializerTest { @Test - public void testDeserializeStringAsString_shouldReturnString() { + void testDeserializeStringAsString_shouldReturnString() { String stringEvent = "Hello World"; String result = extractDataFrom(stringEvent).as(String.class); assertThat(result).isEqualTo(stringEvent); } @Test - public void testDeserializeStringAsObject_shouldReturnObject() { + void testDeserializeStringAsObject_shouldReturnObject() { String productStr = "{\"id\":1234, \"name\":\"product\", \"price\":42}"; Product product = extractDataFrom(productStr).as(Product.class); assertProduct(product); } @Test - public void testDeserializeStringArrayAsList_shouldReturnList() { - String productStr = - "[{\"id\":1234, \"name\":\"product\", \"price\":42}, {\"id\":2345, \"name\":\"product2\", \"price\":43}]"; + void testDeserializeStringArrayAsList_shouldReturnList() { + String productStr = "[{\"id\":1234, \"name\":\"product\", \"price\":42}, {\"id\":2345, \"name\":\"product2\", \"price\":43}]"; List products = extractDataFrom(productStr).asListOf(Product.class); assertThat(products).hasSize(2); assertProduct(products.get(0)); } @Test - public void testDeserializeStringAsList_shouldThrowException() { + void testDeserializeStringAsList_shouldThrowException() { String productStr = "{\"id\":1234, \"name\":\"product\", \"price\":42}"; assertThatThrownBy(() -> extractDataFrom(productStr).asListOf(Product.class)) .isInstanceOf(EventDeserializationException.class) @@ -76,7 +79,7 @@ public void testDeserializeStringAsList_shouldThrowException() { } @Test - public void testDeserializeMapAsObject_shouldReturnObject() { + void testDeserializeMapAsObject_shouldReturnObject() { Map map = new HashMap<>(); map.put("id", 1234); map.put("name", "product"); @@ -87,21 +90,21 @@ public void testDeserializeMapAsObject_shouldReturnObject() { @ParameterizedTest @Event(value = "apigw_event.json", type = APIGatewayProxyRequestEvent.class) - public void testDeserializeAPIGWEventBodyAsObject_shouldReturnObject(APIGatewayProxyRequestEvent event) { + void testDeserializeAPIGWEventBodyAsObject_shouldReturnObject(APIGatewayProxyRequestEvent event) { Product product = extractDataFrom(event).as(Product.class); assertProduct(product); } @ParameterizedTest @Event(value = "sns_event.json", type = SNSEvent.class) - public void testDeserializeSNSEventMessageAsObject_shouldReturnObject(SNSEvent event) { + void testDeserializeSNSEventMessageAsObject_shouldReturnObject(SNSEvent event) { Product product = extractDataFrom(event).as(Product.class); assertProduct(product); } @ParameterizedTest @Event(value = "sqs_event.json", type = SQSEvent.class) - public void testDeserializeSQSEventMessageAsList_shouldReturnList(SQSEvent event) { + void testDeserializeSQSEventMessageAsList_shouldReturnList(SQSEvent event) { List products = extractDataFrom(event).asListOf(Product.class); assertThat(products).hasSize(2); assertProduct(products.get(0)); @@ -109,7 +112,7 @@ public void testDeserializeSQSEventMessageAsList_shouldReturnList(SQSEvent event @ParameterizedTest @Event(value = "kinesis_event.json", type = KinesisEvent.class) - public void testDeserializeKinesisEventMessageAsList_shouldReturnList(KinesisEvent event) { + void testDeserializeKinesisEventMessageAsList_shouldReturnList(KinesisEvent event) { List products = extractDataFrom(event).asListOf(Product.class); assertThat(products).hasSize(2); assertProduct(products.get(0)); @@ -117,7 +120,7 @@ public void testDeserializeKinesisEventMessageAsList_shouldReturnList(KinesisEve @ParameterizedTest @Event(value = "kafka_event.json", type = KafkaEvent.class) - public void testDeserializeKafkaEventMessageAsList_shouldReturnList(KafkaEvent event) { + void testDeserializeKafkaEventMessageAsList_shouldReturnList(KafkaEvent event) { List products = extractDataFrom(event).asListOf(Product.class); assertThat(products).hasSize(2); assertProduct(products.get(0)); @@ -125,7 +128,7 @@ public void testDeserializeKafkaEventMessageAsList_shouldReturnList(KafkaEvent e @ParameterizedTest @Event(value = "sqs_event.json", type = SQSEvent.class) - public void testDeserializeSQSEventMessageAsObject_shouldThrowException(SQSEvent event) { + void testDeserializeSQSEventMessageAsObject_shouldThrowException(SQSEvent event) { assertThatThrownBy(() -> extractDataFrom(event).as(Product.class)) .isInstanceOf(EventDeserializationException.class) .hasMessageContaining("consider using 'asListOf' instead"); @@ -133,7 +136,7 @@ public void testDeserializeSQSEventMessageAsObject_shouldThrowException(SQSEvent @ParameterizedTest @Event(value = "apigw_event.json", type = APIGatewayProxyRequestEvent.class) - public void testDeserializeAPIGatewayEventAsList_shouldThrowException(APIGatewayProxyRequestEvent event) { + void testDeserializeAPIGatewayEventAsList_shouldThrowException(APIGatewayProxyRequestEvent event) { assertThatThrownBy(() -> extractDataFrom(event).asListOf(Product.class)) .isInstanceOf(EventDeserializationException.class) .hasMessageContaining("consider using 'as' instead") @@ -142,14 +145,14 @@ public void testDeserializeAPIGatewayEventAsList_shouldThrowException(APIGateway @ParameterizedTest @Event(value = "custom_event_map.json", type = HashMap.class) - public void testDeserializeAPIGatewayMapEventAsList_shouldThrowException(Map event) { + void testDeserializeAPIGatewayMapEventAsList_shouldThrowException(Map event) { assertThatThrownBy(() -> extractDataFrom(event).asListOf(Order.class)) .isInstanceOf(EventDeserializationException.class) .hasMessage("The content of this event is not a list, consider using 'as' instead"); } @Test - public void testDeserializeEmptyEventAsList_shouldThrowException() { + void testDeserializeEmptyEventAsList_shouldThrowException() { assertThatThrownBy(() -> extractDataFrom(null).asListOf(Product.class)) .isInstanceOf(IllegalStateException.class) .hasMessage("Event content is null: the event may be malformed (missing fields)"); @@ -157,14 +160,14 @@ public void testDeserializeEmptyEventAsList_shouldThrowException() { @ParameterizedTest @Event(value = "apigw_event_no_body.json", type = APIGatewayProxyRequestEvent.class) - public void testDeserializeAPIGatewayNoBody_shouldThrowException(APIGatewayProxyRequestEvent event) { + void testDeserializeAPIGatewayNoBody_shouldThrowException(APIGatewayProxyRequestEvent event) { assertThatThrownBy(() -> extractDataFrom(event).as(Product.class)) .isInstanceOf(IllegalStateException.class) .hasMessage("Event content is null: the event may be malformed (missing fields)"); } @Test - public void testDeserializeAPIGatewayNoBodyAsList_shouldThrowException() { + void testDeserializeAPIGatewayNoBodyAsList_shouldThrowException() { assertThatThrownBy(() -> extractDataFrom(new Object()).asListOf(Product.class)) .isInstanceOf(EventDeserializationException.class) .hasMessage("The content of this event is not a list, consider using 'as' instead"); @@ -172,19 +175,18 @@ public void testDeserializeAPIGatewayNoBodyAsList_shouldThrowException() { @ParameterizedTest @Event(value = "sqs_event_no_body.json", type = SQSEvent.class) - public void testDeserializeSQSEventNoBody_shouldThrowException(SQSEvent event) { + void testDeserializeSQSEventNoBody_shouldThrowException(SQSEvent event) { List products = extractDataFrom(event).asListOf(Product.class); assertThat(products.get(0)).isNull(); } @Test - public void testDeserializeProductAsProduct_shouldReturnProduct() { + void testDeserializeProductAsProduct_shouldReturnProduct() { Product myProduct = new Product(1234, "product", 42); Product product = extractDataFrom(myProduct).as(Product.class); assertProduct(product); } - private void assertProduct(Product product) { assertThat(product) .isEqualTo(new Product(1234, "product", 42)) @@ -193,28 +195,28 @@ private void assertProduct(Product product) { @ParameterizedTest @Event(value = "scheduled_event.json", type = ScheduledEvent.class) - public void testDeserializeScheduledEventMessageAsObject_shouldReturnObject(ScheduledEvent event) { + void testDeserializeScheduledEventMessageAsObject_shouldReturnObject(ScheduledEvent event) { Product product = extractDataFrom(event).as(Product.class); assertProduct(product); } @ParameterizedTest @Event(value = "alb_event.json", type = ApplicationLoadBalancerRequestEvent.class) - public void testDeserializeALBEventMessageAsObjectShouldReturnObject(ApplicationLoadBalancerRequestEvent event) { + void testDeserializeALBEventMessageAsObjectShouldReturnObject(ApplicationLoadBalancerRequestEvent event) { Product product = extractDataFrom(event).as(Product.class); assertProduct(product); } @ParameterizedTest @Event(value = "cwl_event.json", type = CloudWatchLogsEvent.class) - public void testDeserializeCWLEventMessageAsObjectShouldReturnObject(CloudWatchLogsEvent event) { + void testDeserializeCWLEventMessageAsObjectShouldReturnObject(CloudWatchLogsEvent event) { Product product = extractDataFrom(event).as(Product.class); assertProduct(product); } @ParameterizedTest @Event(value = "kf_event.json", type = KinesisFirehoseEvent.class) - public void testDeserializeKFEventMessageAsListShouldReturnList(KinesisFirehoseEvent event) { + void testDeserializeKFEventMessageAsListShouldReturnList(KinesisFirehoseEvent event) { List products = extractDataFrom(event).asListOf(Product.class); assertThat(products).hasSize(1); assertProduct(products.get(0)); @@ -222,7 +224,7 @@ public void testDeserializeKFEventMessageAsListShouldReturnList(KinesisFirehoseE @ParameterizedTest @Event(value = "amq_event.json", type = ActiveMQEvent.class) - public void testDeserializeAMQEventMessageAsListShouldReturnList(ActiveMQEvent event) { + void testDeserializeAMQEventMessageAsListShouldReturnList(ActiveMQEvent event) { List products = extractDataFrom(event).asListOf(Product.class); assertThat(products).hasSize(1); assertProduct(products.get(0)); @@ -230,7 +232,7 @@ public void testDeserializeAMQEventMessageAsListShouldReturnList(ActiveMQEvent e @ParameterizedTest @Event(value = "rabbitmq_event.json", type = RabbitMQEvent.class) - public void testDeserializeRabbitMQEventMessageAsListShouldReturnList(RabbitMQEvent event) { + void testDeserializeRabbitMQEventMessageAsListShouldReturnList(RabbitMQEvent event) { List products = extractDataFrom(event).asListOf(Product.class); assertThat(products).hasSize(1); assertProduct(products.get(0)); @@ -238,7 +240,7 @@ public void testDeserializeRabbitMQEventMessageAsListShouldReturnList(RabbitMQEv @ParameterizedTest @Event(value = "kasip_event.json", type = KinesisAnalyticsStreamsInputPreprocessingEvent.class) - public void testDeserializeKasipEventMessageAsListShouldReturnList( + void testDeserializeKasipEventMessageAsListShouldReturnList( KinesisAnalyticsStreamsInputPreprocessingEvent event) { List products = extractDataFrom(event).asListOf(Product.class); assertThat(products).hasSize(1); @@ -247,7 +249,7 @@ public void testDeserializeKasipEventMessageAsListShouldReturnList( @ParameterizedTest @Event(value = "kafip_event.json", type = KinesisAnalyticsFirehoseInputPreprocessingEvent.class) - public void testDeserializeKafipEventMessageAsListShouldReturnList( + void testDeserializeKafipEventMessageAsListShouldReturnList( KinesisAnalyticsFirehoseInputPreprocessingEvent event) { List products = extractDataFrom(event).asListOf(Product.class); assertThat(products).hasSize(1); @@ -256,16 +258,138 @@ public void testDeserializeKafipEventMessageAsListShouldReturnList( @ParameterizedTest @Event(value = "apigwv2_event.json", type = APIGatewayV2HTTPEvent.class) - public void testDeserializeApiGWV2EventMessageAsObjectShouldReturnObject(APIGatewayV2HTTPEvent event) { + void testDeserializeApiGWV2EventMessageAsObjectShouldReturnObject(APIGatewayV2HTTPEvent event) { Product product = extractDataFrom(event).as(Product.class); assertProduct(product); } @ParameterizedTest @Event(value = "cfcr_event.json", type = CloudFormationCustomResourceEvent.class) - public void testDeserializeCfcrEventMessageAsObjectShouldReturnObject(CloudFormationCustomResourceEvent event) { + void testDeserializeCfcrEventMessageAsObjectShouldReturnObject(CloudFormationCustomResourceEvent event) { Product product = extractDataFrom(event).as(Product.class); assertProduct(product); } + @ParameterizedTest + @Event(value = "scheduled_event.json", type = ScheduledEvent.class) + void testSerializeScheduledEvent_shouldReturnValidJson(ScheduledEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("detail")).isTrue(); + } + + @ParameterizedTest + @Event(value = "apigw_event.json", type = APIGatewayProxyRequestEvent.class) + void testSerializeAPIGatewayEvent_shouldReturnValidJson(APIGatewayProxyRequestEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("body")).isTrue(); + } + + @ParameterizedTest + @Event(value = "sqs_event.json", type = SQSEvent.class) + void testSerializeSQSEvent_shouldReturnValidJson(SQSEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("records")).isTrue(); + } + + @ParameterizedTest + @Event(value = "sns_event.json", type = SNSEvent.class) + void testSerializeSNSEvent_shouldReturnValidJson(SNSEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("records")).isTrue(); + } + + @ParameterizedTest + @Event(value = "kinesis_event.json", type = KinesisEvent.class) + void testSerializeKinesisEvent_shouldReturnValidJson(KinesisEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("records")).isTrue(); + } + + @ParameterizedTest + @Event(value = "kafka_event.json", type = KafkaEvent.class) + void testSerializeKafkaEvent_shouldReturnValidJson(KafkaEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("records")).isTrue(); + } + + @ParameterizedTest + @Event(value = "alb_event.json", type = ApplicationLoadBalancerRequestEvent.class) + void testSerializeALBEvent_shouldReturnValidJson(ApplicationLoadBalancerRequestEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("body")).isTrue(); + } + + @ParameterizedTest + @Event(value = "cwl_event.json", type = CloudWatchLogsEvent.class) + void testSerializeCWLEvent_shouldReturnValidJson(CloudWatchLogsEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("awsLogs")).isTrue(); + } + + @ParameterizedTest + @Event(value = "kf_event.json", type = KinesisFirehoseEvent.class) + void testSerializeKFEvent_shouldReturnValidJson(KinesisFirehoseEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("records")).isTrue(); + } + + @ParameterizedTest + @Event(value = "amq_event.json", type = ActiveMQEvent.class) + void testSerializeAMQEvent_shouldReturnValidJson(ActiveMQEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("messages")).isTrue(); + } + + @ParameterizedTest + @Event(value = "rabbitmq_event.json", type = RabbitMQEvent.class) + void testSerializeRabbitMQEvent_shouldReturnValidJson(RabbitMQEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("rmqMessagesByQueue")).isTrue(); + } + + @ParameterizedTest + @Event(value = "kasip_event.json", type = KinesisAnalyticsStreamsInputPreprocessingEvent.class) + void testSerializeKasipEvent_shouldReturnValidJson(KinesisAnalyticsStreamsInputPreprocessingEvent event) + throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("records")).isTrue(); + } + + @ParameterizedTest + @Event(value = "kafip_event.json", type = KinesisAnalyticsFirehoseInputPreprocessingEvent.class) + void testSerializeKafipEvent_shouldReturnValidJson(KinesisAnalyticsFirehoseInputPreprocessingEvent event) + throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("records")).isTrue(); + } + + @ParameterizedTest + @Event(value = "apigwv2_event.json", type = APIGatewayV2HTTPEvent.class) + void testSerializeApiGWV2Event_shouldReturnValidJson(APIGatewayV2HTTPEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("body")).isTrue(); + } + + @ParameterizedTest + @Event(value = "cfcr_event.json", type = CloudFormationCustomResourceEvent.class) + void testSerializeCfcrEvent_shouldReturnValidJson(CloudFormationCustomResourceEvent event) throws Exception { + String json = JsonConfig.get().getObjectMapper().valueToTree(event).toString(); + JsonNode parsed = JsonConfig.get().getObjectMapper().readTree(json); + assertThat(parsed.has("resourceProperties")).isTrue(); + } + } diff --git a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64FunctionTest.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64FunctionTest.java index d86af6671..478d3477c 100644 --- a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64FunctionTest.java +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64FunctionTest.java @@ -16,21 +16,24 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.io.IOException; + +import org.junit.jupiter.api.Test; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeType; + import io.burt.jmespath.Expression; -import java.io.IOException; -import org.junit.jupiter.api.Test; import software.amazon.lambda.powertools.utilities.JsonConfig; -public class Base64FunctionTest { +class Base64FunctionTest { @Test - public void testPowertoolsBase64() throws IOException { - JsonNode event = - JsonConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/custom_event.json")); - Expression expression = - JsonConfig.get().getJmesPath().compile("basket.powertools_base64(hiddenProduct)"); + void testPowertoolsBase64() throws IOException { + JsonNode event = JsonConfig.get().getObjectMapper() + .readTree(this.getClass().getResourceAsStream("/custom_event.json")); + Expression expression = JsonConfig.get().getJmesPath() + .compile("basket.powertools_base64(hiddenProduct)"); JsonNode result = expression.search(event); assertThat(result.getNodeType()).isEqualTo(JsonNodeType.STRING); assertThat(result.asText()).isEqualTo("{\n" + diff --git a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunctionTest.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunctionTest.java index eeb605076..6f9f9a592 100644 --- a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunctionTest.java +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunctionTest.java @@ -16,18 +16,21 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.io.IOException; + +import org.junit.jupiter.api.Test; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeType; + import io.burt.jmespath.Expression; import io.burt.jmespath.JmesPathType; -import java.io.IOException; -import org.junit.jupiter.api.Test; import software.amazon.lambda.powertools.utilities.JsonConfig; -public class Base64GZipFunctionTest { +class Base64GZipFunctionTest { @Test - public void testConstructor() { + void testConstructor() { Base64GZipFunction base64GZipFunction = new Base64GZipFunction(); assertThat(base64GZipFunction.name()).isEqualTo("powertools_base64_gzip"); assertThat(base64GZipFunction.argumentConstraints().expectedType().toLowerCase()).isEqualTo( @@ -38,18 +41,18 @@ public void testConstructor() { } @Test - public void testPowertoolsGzip() throws IOException { + void testPowertoolsGzip() throws IOException { JsonNode event = JsonConfig.get().getObjectMapper() .readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); - Expression expression = - JsonConfig.get().getJmesPath().compile("basket.powertools_base64_gzip(hiddenProduct)"); + Expression expression = JsonConfig.get().getJmesPath() + .compile("basket.powertools_base64_gzip(hiddenProduct)"); JsonNode result = expression.search(event); assertThat(result.getNodeType()).isEqualTo(JsonNodeType.STRING); assertThat(result.asText()).isEqualTo("{ \"id\": 43242, \"name\": \"FooBar XY\", \"price\": 258}"); } @Test - public void testPowertoolsGzipEmptyJsonAttribute() throws IOException { + void testPowertoolsGzipEmptyJsonAttribute() throws IOException { JsonNode event = JsonConfig.get().getObjectMapper() .readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); Expression expression = JsonConfig.get().getJmesPath().compile("basket.powertools_base64_gzip('')"); @@ -58,7 +61,7 @@ public void testPowertoolsGzipEmptyJsonAttribute() throws IOException { } @Test - public void testPowertoolsGzipWrongArgumentType() throws IOException { + void testPowertoolsGzipWrongArgumentType() throws IOException { JsonNode event = JsonConfig.get().getObjectMapper() .readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); Expression expression = JsonConfig.get().getJmesPath().compile("basket.powertools_base64_gzip(null)"); @@ -68,21 +71,20 @@ public void testPowertoolsGzipWrongArgumentType() throws IOException { } @Test - public void testBase64GzipDecompressNull() { + void testBase64GzipDecompressNull() { String result = Base64GZipFunction.decompress(null); assertThat(result).isNull(); } @Test - public void testPowertoolsGzipNotCompressedJsonAttribute() throws IOException { + void testPowertoolsGzipNotCompressedJsonAttribute() throws IOException { JsonNode event = JsonConfig.get().getObjectMapper() .readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); - Expression expression = - JsonConfig.get().getJmesPath().compile("basket.powertools_base64_gzip(encodedString)"); + Expression expression = JsonConfig.get().getJmesPath() + .compile("basket.powertools_base64_gzip(encodedString)"); JsonNode result = expression.search(event); assertThat(result.getNodeType()).isEqualTo(JsonNodeType.STRING); assertThat(result.asText()).isEqualTo("test"); } - } diff --git a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunctionTest.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunctionTest.java index 0bfb635fa..028dba9ea 100644 --- a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunctionTest.java +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunctionTest.java @@ -16,17 +16,20 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.io.IOException; + +import org.junit.jupiter.api.Test; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeType; + import io.burt.jmespath.Expression; -import java.io.IOException; -import org.junit.jupiter.api.Test; import software.amazon.lambda.powertools.utilities.JsonConfig; -public class JsonFunctionTest { +class JsonFunctionTest { @Test - public void testJsonFunction() throws IOException { + void testJsonFunction() throws IOException { JsonNode event = JsonConfig.get().getObjectMapper() .readTree(this.getClass().getResourceAsStream("/custom_event_json.json")); Expression expression = JsonConfig.get().getJmesPath().compile("powertools_json(body)"); @@ -38,7 +41,7 @@ public void testJsonFunction() throws IOException { } @Test - public void testJsonFunctionChild() throws IOException { + void testJsonFunctionChild() throws IOException { JsonNode event = JsonConfig.get().getObjectMapper() .readTree(this.getClass().getResourceAsStream("/custom_event_json.json")); Expression expression = JsonConfig.get().getJmesPath().compile("powertools_json(body).list[0].item"); From f543b68171cb38df9dde72b2839aeb673e3e2b3d Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Fri, 31 Oct 2025 12:47:29 +0100 Subject: [PATCH 07/76] fix(logging): Fix bug where correlation_id field was missing in JSON structured output (#2256) --- .../json/resolver/PowertoolsResolver.java | 20 ++++++++++++++++++- .../src/main/resources/LambdaEcsLayout.json | 14 ++++++++----- .../src/main/resources/LambdaJsonLayout.json | 4 ++++ .../PowertoolsResolverArgumentsTest.java | 6 ++++-- .../logging/logback/LambdaEcsEncoder.java | 7 +++++++ .../internal/LambdaEcsEncoderTest.java | 4 +++- .../internal/LambdaJsonEncoderTest.java | 6 ++++-- 7 files changed, 50 insertions(+), 11 deletions(-) diff --git a/powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolver.java b/powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolver.java index 8ada50f49..cef5b86ee 100644 --- a/powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolver.java +++ b/powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolver.java @@ -15,6 +15,7 @@ package org.apache.logging.log4j.layout.template.json.resolver; import static java.util.Arrays.stream; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.CORRELATION_ID; import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_ARN; import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_COLD_START; import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_MEMORY_SIZE; @@ -112,7 +113,7 @@ public boolean isResolvable(LogEvent logEvent) { final String samplingRate = logEvent.getContextData().getValue(PowertoolsLoggedFields.SAMPLING_RATE.getName()); try { - return (null != samplingRate && Float.parseFloat(samplingRate) > 0.f); + return null != samplingRate && Float.parseFloat(samplingRate) > 0.f; } catch (NumberFormatException nfe) { return false; } @@ -142,6 +143,22 @@ public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { } }; + private static final EventResolver CORRELATION_ID_RESOLVER = new EventResolver() { + @Override + public boolean isResolvable(LogEvent logEvent) { + final String correlationId = + logEvent.getContextData().getValue(PowertoolsLoggedFields.CORRELATION_ID.getName()); + return null != correlationId; + } + + @Override + public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { + final String correlationId = + logEvent.getContextData().getValue(PowertoolsLoggedFields.CORRELATION_ID.getName()); + jsonWriter.writeString(correlationId); + } + }; + private static final EventResolver SERVICE_RESOLVER = (final LogEvent logEvent, final JsonWriter jsonWriter) -> { final String service = logEvent.getContextData().getValue(PowertoolsLoggedFields.SERVICE.getName()); @@ -214,6 +231,7 @@ public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { { FUNCTION_REQUEST_ID.getName(), FUNCTION_REQ_RESOLVER }, { FUNCTION_COLD_START.getName(), COLD_START_RESOLVER }, { FUNCTION_TRACE_ID.getName(), XRAY_TRACE_RESOLVER }, + { CORRELATION_ID.getName(), CORRELATION_ID_RESOLVER }, { SAMPLING_RATE.getName(), SAMPLING_RATE_RESOLVER }, { "region", REGION_RESOLVER }, { "account_id", ACCOUNT_ID_RESOLVER } diff --git a/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaEcsLayout.json b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaEcsLayout.json index 19f13f199..58b30f60e 100644 --- a/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaEcsLayout.json +++ b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaEcsLayout.json @@ -45,13 +45,13 @@ "$resolver": "thread", "field": "name" }, - "cloud.provider" : "aws", - "cloud.service.name" : "lambda", - "cloud.region" : { + "cloud.provider": "aws", + "cloud.service.name": "lambda", + "cloud.region": { "$resolver": "powertools", "field": "region" }, - "cloud.account.id" : { + "cloud.account.id": { "$resolver": "powertools", "field": "account_id" }, @@ -83,7 +83,11 @@ "$resolver": "powertools", "field": "xray_trace_id" }, + "correlation.id": { + "$resolver": "powertools", + "field": "correlation_id" + }, "": { "$resolver": "powertools" } -} \ No newline at end of file +} diff --git a/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json index 8b811ee5f..793006502 100644 --- a/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json +++ b/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json @@ -65,6 +65,10 @@ "$resolver": "powertools", "field": "xray_trace_id" }, + "correlation_id": { + "$resolver": "powertools", + "field": "correlation_id" + }, "": { "$resolver": "powertools" } diff --git a/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverArgumentsTest.java b/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverArgumentsTest.java index 546d54579..573eaddbf 100644 --- a/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverArgumentsTest.java +++ b/powertools-logging/powertools-logging-log4j/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolverArgumentsTest.java @@ -90,7 +90,8 @@ void shouldLogArgumentsAsJsonWhenUsingRawJson() { .contains( "\"input\":{\"awsRegion\":\"us-east-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") .contains("\"message\":\"1212abcd\"") - .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\""); + .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\"") + .contains("\"correlation_id\":\"1212abcd\""); // Reserved keys should be ignored PowertoolsLoggedFields.stringValues().stream().forEach(reservedKey -> { assertThat(contentOf(logFile)).doesNotContain("\"" + reservedKey + "\":\"shouldBeIgnored\""); @@ -122,7 +123,8 @@ void shouldLogArgumentsAsJsonWhenUsingKeyValue() { .contains( "\"input\":{\"awsRegion\":\"us-east-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") .contains("\"message\":\"1212abcd\"") - .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\""); + .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\"") + .contains("\"correlation_id\":\"1212abcd\""); // Reserved keys should be ignored PowertoolsLoggedFields.stringValues().stream().forEach(reservedKey -> { diff --git a/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaEcsEncoder.java b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaEcsEncoder.java index a1a7daff1..6a82d8e67 100644 --- a/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaEcsEncoder.java +++ b/powertools-logging/powertools-logging-logback/src/main/java/software/amazon/lambda/powertools/logging/logback/LambdaEcsEncoder.java @@ -15,6 +15,7 @@ package software.amazon.lambda.powertools.logging.logback; import static java.nio.charset.StandardCharsets.UTF_8; +import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.CORRELATION_ID; import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_ARN; import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_COLD_START; import static software.amazon.lambda.powertools.logging.internal.PowertoolsLoggedFields.FUNCTION_MEMORY_SIZE; @@ -72,6 +73,7 @@ public class LambdaEcsEncoder extends EncoderBase { protected static final String FUNCTION_VERSION_ATTR_NAME = "faas.version"; protected static final String FUNCTION_MEMORY_ATTR_NAME = "faas.memory"; protected static final String FUNCTION_TRACE_ID_ATTR_NAME = "trace.id"; + protected static final String CORRELATION_ID_ATTR_NAME = "correlation.id"; protected static final String ECS_VERSION = "1.2.0"; protected static final String CLOUD_PROVIDER = "aws"; @@ -156,6 +158,11 @@ private void serializeFunctionInfo(JsonSerializer serializer, String arn, Map { assertThat(contentOf(logFile)).doesNotContain("\"" + reservedKey + "\":\"shouldBeIgnored\""); @@ -168,7 +169,8 @@ void shouldLogArgumentsAsJsonWhenUsingKeyValue() { "\"input\":{\"awsRegion\":\"eu-central-1\",\"body\":\"plop\",\"eventSource\":\"eb\",\"messageAttributes\":{\"keyAttribute\":{\"stringListValues\":[\"val1\",\"val2\",\"val3\"]}},\"messageId\":\"1212abcd\"}") .contains("\"message\":\"1212abcd\"") // Should auto-escape double quotes around id - .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\""); + .contains("\"message\":\"Message body = plop and id = \\\"1212abcd\\\"\"") + .contains("\"correlation_id\":\"1212abcd\""); // Reserved keys should be ignored PowertoolsLoggedFields.stringValues().stream().forEach(reservedKey -> { assertThat(contentOf(logFile)).doesNotContain("\"" + reservedKey + "\":\"shouldBeIgnored\""); From 71c632b70a4d1ec3146102e4c2ab5e3fa0f793d0 Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Mon, 3 Nov 2025 11:42:16 +0100 Subject: [PATCH 08/76] feat(large-messages): Add function interface for Large Messages Utility (#2257) * Add functional API for LargeMessages utility. * Add E2E tests for functional large messages. * Use format specifiers for logging in LargeMessageE2ET.java. --- .../handlers/largemessage-functional/pom.xml | 46 +++ .../lambda/powertools/e2e/Function.java | 85 ++++++ .../src/main/resources/log4j2.xml | 16 + powertools-e2e-tests/handlers/pom.xml | 1 + .../lambda/powertools/LargeMessageE2ET.java | 62 ++-- powertools-large-messages/pom.xml | 18 +- .../largemessages/LargeMessage.java | 12 +- .../largemessages/LargeMessages.java | 156 ++++++++++ .../internal/LargeMessageAspect.java | 13 +- .../internal/LargeMessageFunction.java | 35 +++ .../internal/LargeMessageProcessor.java | 68 +++-- .../LargeMessageProcessorFactory.java | 7 +- .../largemessages/LargeMessagesTest.java | 287 ++++++++++++++++++ .../internal/LargeMessageAspectTest.java | 102 ++++--- .../test/resources/simplelogger.properties | 7 + 15 files changed, 810 insertions(+), 105 deletions(-) create mode 100644 powertools-e2e-tests/handlers/largemessage-functional/pom.xml create mode 100644 powertools-e2e-tests/handlers/largemessage-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java create mode 100644 powertools-e2e-tests/handlers/largemessage-functional/src/main/resources/log4j2.xml create mode 100644 powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessages.java create mode 100644 powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageFunction.java create mode 100644 powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/LargeMessagesTest.java create mode 100644 powertools-large-messages/src/test/resources/simplelogger.properties diff --git a/powertools-e2e-tests/handlers/largemessage-functional/pom.xml b/powertools-e2e-tests/handlers/largemessage-functional/pom.xml new file mode 100644 index 000000000..4afb633f2 --- /dev/null +++ b/powertools-e2e-tests/handlers/largemessage-functional/pom.xml @@ -0,0 +1,46 @@ + + 4.0.0 + + + software.amazon.lambda + e2e-test-handlers-parent + 2.5.0 + + + e2e-test-handler-largemessage-functional + jar + E2E test handler – Large message functional + + + + software.amazon.awssdk + dynamodb + + + software.amazon.lambda + powertools-large-messages + + + software.amazon.lambda + powertools-logging-log4j + + + software.amazon.lambda + powertools-logging + + + com.amazonaws + aws-lambda-java-events + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + + diff --git a/powertools-e2e-tests/handlers/largemessage-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/largemessage-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java new file mode 100644 index 000000000..05a336500 --- /dev/null +++ b/powertools-e2e-tests/handlers/largemessage-functional/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -0,0 +1,85 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.e2e; + +import static software.amazon.lambda.powertools.logging.PowertoolsLogging.withLogging; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; + +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; +import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; +import software.amazon.awssdk.utils.BinaryUtils; +import software.amazon.awssdk.utils.Md5Utils; +import software.amazon.lambda.powertools.largemessages.LargeMessages; + +public class Function implements RequestHandler { + + private static final String TABLE_FOR_ASYNC_TESTS = System.getenv("TABLE_FOR_ASYNC_TESTS"); + private DynamoDbClient client; + + public Function() { + if (client == null) { + client = DynamoDbClient.builder() + .httpClient(UrlConnectionHttpClient.builder().build()) + .region(Region.of(System.getenv("AWS_REGION"))) + .build(); + } + } + + public SQSBatchResponse handleRequest(SQSEvent event, Context context) { + return withLogging(context, () -> { + for (SQSMessage message : event.getRecords()) { + LargeMessages.processLargeMessage(message, msg -> processRawMessage(msg, context)); + } + return SQSBatchResponse.builder().build(); + }); + } + + private Void processRawMessage(SQSMessage sqsMessage, Context context) { + String bodyMD5 = md5(sqsMessage.getBody()); + if (!sqsMessage.getMd5OfBody().equals(bodyMD5)) { + throw new SecurityException( + String.format("message digest does not match, expected %s, got %s", sqsMessage.getMd5OfBody(), + bodyMD5)); + } + + Map item = new HashMap<>(); + item.put("functionName", AttributeValue.builder().s(context.getFunctionName()).build()); + item.put("id", AttributeValue.builder().s(sqsMessage.getMessageId()).build()); + item.put("bodyMD5", AttributeValue.builder().s(bodyMD5).build()); + item.put("bodySize", + AttributeValue.builder().n(String.valueOf(sqsMessage.getBody().getBytes(StandardCharsets.UTF_8).length)) + .build()); + + client.putItem(PutItemRequest.builder().tableName(TABLE_FOR_ASYNC_TESTS).item(item).build()); + + return null; + } + + private String md5(String message) { + return BinaryUtils.toHex(Md5Utils.computeMD5Hash(message.getBytes(StandardCharsets.UTF_8))); + } +} diff --git a/powertools-e2e-tests/handlers/largemessage-functional/src/main/resources/log4j2.xml b/powertools-e2e-tests/handlers/largemessage-functional/src/main/resources/log4j2.xml new file mode 100644 index 000000000..8925f70b9 --- /dev/null +++ b/powertools-e2e-tests/handlers/largemessage-functional/src/main/resources/log4j2.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 86e867548..f171422c5 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -27,6 +27,7 @@ batch largemessage + largemessage-functional largemessage_idempotent logging-log4j logging-logback diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LargeMessageE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LargeMessageE2ET.java index 74247ca2e..0de2dca60 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LargeMessageE2ET.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LargeMessageE2ET.java @@ -16,9 +16,10 @@ import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,6 +41,7 @@ import software.amazon.lambda.powertools.testutils.Infrastructure; import software.amazon.lambda.powertools.testutils.RetryUtils; +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class LargeMessageE2ET { private static final Logger LOG = LoggerFactory.getLogger(LargeMessageE2ET.class); @@ -55,25 +57,34 @@ class LargeMessageE2ET { .region(region) .build(); - private static Infrastructure infrastructure; - private static String functionName; - private static String bucketName; - private static String queueUrl; - private static String tableName; + private Infrastructure infrastructure; + private String functionName; + private String bucketName; + private String queueUrl; + private String tableName; private String messageId; + private String currentPathToFunction; + + private void setupInfrastructure(String pathToFunction) { + // Do not re-deploy the same function + if (pathToFunction.equals(currentPathToFunction)) { + return; + } + + // Destroy any existing infrastructure before re-deploying + if (infrastructure != null) { + infrastructure.destroy(); + } - @BeforeAll - @Timeout(value = 5, unit = TimeUnit.MINUTES) - static void setup() { String random = UUID.randomUUID().toString().substring(0, 6); bucketName = "largemessagebucket" + random; String queueName = "largemessagequeue" + random; infrastructure = Infrastructure.builder() - .testName(LargeMessageE2ET.class.getSimpleName()) + .testName(LargeMessageE2ET.class.getSimpleName() + "-" + pathToFunction) .queue(queueName) .largeMessagesBucket(bucketName) - .pathToFunction("largemessage") + .pathToFunction(pathToFunction) .timeoutInSeconds(60) .build(); @@ -81,19 +92,24 @@ static void setup() { functionName = outputs.get(FUNCTION_NAME_OUTPUT); queueUrl = outputs.get("QueueURL"); tableName = outputs.get("TableNameForAsyncTests"); + currentPathToFunction = pathToFunction; - LOG.info("Testing '" + LargeMessageE2ET.class.getSimpleName() + "'"); + LOG.info("Testing '{}' with {}", LargeMessageE2ET.class.getSimpleName(), pathToFunction); } @AfterAll - static void tearDown() { + void cleanup() { if (infrastructure != null) { infrastructure.destroy(); } } @AfterEach - void reset() { + void tearDown() { + reset(); + } + + private void reset() { if (messageId != null) { Map itemToDelete = new HashMap<>(); itemToDelete.put("functionName", AttributeValue.builder().s(functionName).build()); @@ -103,8 +119,12 @@ void reset() { } } - @Test - void bigSQSMessageOffloadedToS3_shouldLoadFromS3() throws IOException { + @ParameterizedTest + @ValueSource(strings = { "largemessage", "largemessage-functional" }) + @Timeout(value = 5, unit = TimeUnit.MINUTES) + void bigSQSMessageOffloadedToS3_shouldLoadFromS3(String pathToFunction) throws IOException { + setupInfrastructure(pathToFunction); + // GIVEN final ExtendedClientConfiguration extendedClientConfig = new ExtendedClientConfiguration() .withPayloadSupportEnabled(s3Client, bucketName); @@ -146,8 +166,12 @@ void bigSQSMessageOffloadedToS3_shouldLoadFromS3() throws IOException { assertThat(items.get(0).get("bodyMD5").s()).isEqualTo("22bde5e7b05fa80bc7be45bdd4bc6c75"); } - @Test - void smallSQSMessage_shouldNotReadFromS3() { + @ParameterizedTest + @ValueSource(strings = { "largemessage", "largemessage-functional" }) + @Timeout(value = 5, unit = TimeUnit.MINUTES) + void smallSQSMessage_shouldNotReadFromS3(String pathToFunction) { + setupInfrastructure(pathToFunction); + // GIVEN final ExtendedClientConfiguration extendedClientConfig = new ExtendedClientConfiguration() .withPayloadSupportEnabled(s3Client, bucketName); diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index a405941d7..bc7bbf03a 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -14,11 +14,11 @@ --> + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 -A suite of utilities for AWS Lambda Functions that makes handling large messages in SQS and SNS easier. + A suite of utilities for AWS Lambda Functions that makes handling large messages in SQS and SNS easier. software.amazon.lambda @@ -41,6 +41,13 @@ software.amazon.lambda powertools-common + + software.amazon.lambda + powertools-common + ${project.version} + test-jar + test + com.amazonaws aws-lambda-java-events @@ -102,6 +109,11 @@ mockito-core test + + org.mockito + mockito-junit-jupiter + test + org.slf4j slf4j-simple diff --git a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessage.java b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessage.java index 4e556966c..eb5b368a5 100644 --- a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessage.java +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessage.java @@ -20,15 +20,15 @@ import java.lang.annotation.Target; /** - *

Use this annotation to handle large messages (> 256 KB) from SQS or SNS. + *

Use this annotation to handle large messages (> 1 MB) from SQS or SNS. * When large messages are sent to an SQS Queue or SNS Topic, they are offloaded to S3 and only a reference is passed in the message/record.

* *

{@code @LargeMessage} automatically retrieves and deletes messages * which have been offloaded to S3 using the {@code amazon-sqs-java-extended-client-lib} or {@code amazon-sns-java-extended-client-lib} * client libraries.

* - *

This version of the {@code @LargeMessage} is compatible with version - * 1.1.0+ of {@code amazon-sqs-java-extended-client-lib} / {@code amazon-sns-java-extended-client-lib}.

+ *

This version of the {@code @LargeMessage} is compatible with version 1.1.0+ and 2.0.0+ + * of {@code amazon-sqs-java-extended-client-lib} / {@code amazon-sns-java-extended-client-lib}.

*
*

Put this annotation on a method where the first parameter is either a {@link com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage} or {@link com.amazonaws.services.lambda.runtime.events.SNSEvent.SNSRecord}. *
@@ -54,9 +54,11 @@ * *

* - *

Note 1: Retrieving payloads and deleting objects from S3 will increase the duration of the + *

Note 1: The message object (SQSMessage or SNSRecord) is modified in-place to avoid duplicating + * the large blob in memory. The message body will be replaced with the S3 object content.

+ *

Note 2: Retrieving payloads and deleting objects from S3 will increase the duration of the * Lambda function.

- *

Note 2: Make sure to configure your function with enough memory to be able to retrieve S3 objects.

+ *

Note 3: Make sure to configure your function with enough memory to be able to retrieve S3 objects.

*/ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) diff --git a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessages.java b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessages.java new file mode 100644 index 000000000..52675d3eb --- /dev/null +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/LargeMessages.java @@ -0,0 +1,156 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.largemessages; + +import java.util.Optional; +import java.util.function.Function; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import software.amazon.lambda.powertools.largemessages.internal.LargeMessageProcessor; +import software.amazon.lambda.powertools.largemessages.internal.LargeMessageProcessorFactory; + +/** + * Functional API for processing large messages without AspectJ. + *

+ * Use this class to handle large messages (> 1 MB) from SQS or SNS. + * When large messages are sent to an SQS Queue or SNS Topic, they are offloaded to S3 and only a reference is passed in the message/record. + *

+ * {@code LargeMessages} automatically retrieves and optionally deletes messages + * which have been offloaded to S3 using the {@code amazon-sqs-java-extended-client-lib} or {@code amazon-sns-java-extended-client-lib} + * client libraries. + *

+ * This version is compatible with version 1.1.0+ and 2.0.0+ of {@code amazon-sqs-java-extended-client-lib} / {@code amazon-sns-java-extended-client-lib}. + *

+ * SQS Example: + *

+ * public class SqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> {
+ *     private final BatchMessageHandler<SQSEvent, SQSBatchResponse> handler;
+ *
+ *     public SqsBatchHandler() {
+ *         handler = new BatchMessageHandlerBuilder()
+ *                 .withSqsBatchHandler()
+ *                 .buildWithRawMessageHandler(this::processMessage);
+ *     }
+ *
+ *     @Override
+ *     public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) {
+ *         return handler.processBatch(sqsEvent, context);
+ *     }
+ *
+ *     private void processMessage(SQSEvent.SQSMessage sqsMessage) {
+ *         LargeMessages.processLargeMessage(sqsMessage, this::handleProcessedMessage);
+ *     }
+ *
+ *     private void handleProcessedMessage(SQSEvent.SQSMessage processedMessage) {
+ *         // processedMessage.getBody() will contain the content of the S3 Object
+ *     }
+ * }
+ * 
+ *

+ * To disable the deletion of S3 objects: + *

+ * LargeMessages.processLargeMessage(sqsMessage, this::handleProcessedMessage, false);
+ * 
+ *

+ * For multi-argument methods, use a lambda to pass additional parameters: + *

+ * public void handleRequest(SQSEvent event, Context context) {
+ *     event.getRecords().forEach(message ->
+ *         LargeMessages.processLargeMessage(message, processedMsg -> processMessage(processedMsg, context))
+ *     );
+ * }
+ *
+ * private void processMessage(SQSMessage processedMessage, Context context) {
+ *     // processedMessage.getBody() will contain the content of the S3 Object
+ * }
+ * 
+ *

+ * Note 1: The message object (SQSMessage or SNSRecord) is modified in-place to avoid duplicating + * the large blob in memory. The message body will be replaced with the S3 object content. + *

+ * Note 2: Retrieving payloads and deleting objects from S3 will increase the duration of the Lambda function. + *

+ * Note 3: Make sure to configure your function with enough memory to be able to retrieve S3 objects. + * + * @see LargeMessage + */ +public final class LargeMessages { + + private static final Logger LOG = LoggerFactory.getLogger(LargeMessages.class); + + private LargeMessages() { + // Utility class + } + + /** + * Process a large message and execute the function with the processed message. + *

+ * The S3 object will be deleted after processing (default behavior). + * To disable S3 object deletion, use {@link #processLargeMessage(Object, Function, boolean)}. + *

+ * Example usage: + *

+     * String returnValueOfFunction = LargeMessages.processLargeMessage(sqsMessage, this::handleMessage);
+     * String returnValueOfFunction = LargeMessages.processLargeMessage(sqsMessage, processedMsg -> processOrder(processedMsg, orderId, amount));
+     * 
+ * + * @param message the message to process (SQSMessage or SNSRecord) + * @param function the function to execute with the processed message + * @param the message type + * @param the return type of the function + * @return the result of the function execution + */ + public static R processLargeMessage(T message, Function function) { + return processLargeMessage(message, function, true); + } + + /** + * Process a large message and execute the function with the processed message. + *

+ * Example usage: + *

+     * String returnValueOfFunction = LargeMessages.processLargeMessage(sqsMessage, this::handleMessage, false);
+     * String returnValueOfFunction = LargeMessages.processLargeMessage(sqsMessage, processedMsg -> processOrder(processedMsg, orderId, amount), false);
+     * 
+ * + * @param message the message to process (SQSMessage or SNSRecord) + * @param function the function to execute with the processed message + * @param deleteS3Object whether to delete the S3 object after processing + * @param the message type + * @param the return type of the function + * @return the result of the function execution + */ + public static R processLargeMessage(T message, Function function, boolean deleteS3Object) { + Optional> processor = LargeMessageProcessorFactory.get(message); + + if (!processor.isPresent()) { + LOG.warn("Unsupported message type [{}], proceeding without large message processing", + message.getClass()); + return function.apply(message); + } + + try { + @SuppressWarnings("unchecked") + LargeMessageProcessor typedProcessor = (LargeMessageProcessor) processor.get(); + return typedProcessor.process(message, function::apply, deleteS3Object); + } catch (RuntimeException e) { + throw e; + } catch (Throwable t) { + throw new LargeMessageProcessingException("Failed to process large message", t); + } + } +} diff --git a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageAspect.java b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageAspect.java index 2aa81691f..0a8b93095 100644 --- a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageAspect.java +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageAspect.java @@ -15,12 +15,14 @@ package software.amazon.lambda.powertools.largemessages.internal; import java.util.Optional; + import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.largemessages.LargeMessage; /** @@ -31,17 +33,17 @@ public class LargeMessageAspect { private static final Logger LOG = LoggerFactory.getLogger(LargeMessageAspect.class); - @SuppressWarnings({"EmptyMethod"}) + @SuppressWarnings({ "EmptyMethod" }) @Pointcut("@annotation(largeMessage)") public void callAt(LargeMessage largeMessage) { + // Pointcut method - body intentionally empty } + @SuppressWarnings("unchecked") @Around(value = "callAt(largeMessage) && execution(@LargeMessage * *.*(..))", argNames = "pjp,largeMessage") - public Object around(ProceedingJoinPoint pjp, - LargeMessage largeMessage) throws Throwable { + public Object around(ProceedingJoinPoint pjp, LargeMessage largeMessage) throws Throwable { Object[] proceedArgs = pjp.getArgs(); - // we need a message to process if (proceedArgs.length == 0) { LOG.warn("@LargeMessage annotation is placed on a method without any message to process, proceeding"); return pjp.proceed(proceedArgs); @@ -56,7 +58,8 @@ public Object around(ProceedingJoinPoint pjp, return pjp.proceed(proceedArgs); } - return largeMessageProcessor.get().process(pjp, largeMessage.deleteS3Object()); + return ((LargeMessageProcessor) largeMessageProcessor.get()).process(message, + msg -> pjp.proceed(proceedArgs), largeMessage.deleteS3Object()); } } diff --git a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageFunction.java b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageFunction.java new file mode 100644 index 000000000..1690eedac --- /dev/null +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageFunction.java @@ -0,0 +1,35 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.largemessages.internal; + +/** + * Functional interface for large message processing. + *

+ * This interface is similar to {@link java.util.function.Function} but throws {@link Throwable} + * instead of no exceptions. This is necessary to support AspectJ's {@code ProceedingJoinPoint.proceed()} + * which throws {@code Throwable}, allowing exceptions to bubble up naturally without wrapping. + *

+ * This interface should not be exposed to user-facing APIs such as + * {@link software.amazon.lambda.powertools.largemessages.LargeMessages}. These should use plain + * {@link java.util.function.Function}. + * + * @param the input type (message type) + * @param the return type of the function + */ +@FunctionalInterface +public interface LargeMessageFunction { + @SuppressWarnings("java:S112") // Throwable is required for AspectJ compatibility + R apply(T processedMessage) throws Throwable; +} diff --git a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessor.java b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessor.java index 5478931f1..c41af0cea 100644 --- a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessor.java +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessor.java @@ -17,9 +17,10 @@ import static java.lang.String.format; import java.nio.charset.StandardCharsets; -import org.aspectj.lang.ProceedingJoinPoint; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import software.amazon.awssdk.core.exception.SdkException; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.lambda.powertools.largemessages.LargeMessageConfig; @@ -28,33 +29,44 @@ import software.amazon.payloadoffloading.S3Dao; /** - * Abstract processor for Large Messages. Handle the download from S3 and replace the actual S3 pointer with the content - * of the S3 Object leveraging the payloadoffloading library. + * Abstract processor for Large Messages. + *

+ * Handles the download from S3 and replaces the S3 pointer with the actual content + * of the S3 Object, leveraging the payloadoffloading library. * - * @param any message type that support Large Messages with S3 pointers - * ({@link com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage} and {@link com.amazonaws.services.lambda.runtime.events.SNSEvent.SNSRecord} at the moment) + * @param any message type that supports Large Messages with S3 pointers + * ({@link com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage} + * and {@link com.amazonaws.services.lambda.runtime.events.SNSEvent.SNSRecord}) */ -abstract class LargeMessageProcessor { +public abstract class LargeMessageProcessor { protected static final String RESERVED_ATTRIBUTE_NAME = "ExtendedPayloadSize"; private static final Logger LOG = LoggerFactory.getLogger(LargeMessageProcessor.class); private final S3Client s3Client = LargeMessageConfig.get().getS3Client(); private final S3BackedPayloadStore payloadStore = new S3BackedPayloadStore(new S3Dao(s3Client), "DUMMY"); - public Object process(ProceedingJoinPoint pjp, boolean deleteS3Object) throws Throwable { - Object[] proceedArgs = pjp.getArgs(); - T message = (T) proceedArgs[0]; - + /** + * Process a large message using a functional interface. + * + * @param message the message to process + * @param function the function to execute with the processed message + * @param deleteS3Object whether to delete the S3 object after processing + * @param the return type of the wrapped function + * @return the result of the function execution + * @throws Throwable if an error occurs during processing + */ + public R process(T message, LargeMessageFunction function, boolean deleteS3Object) throws Throwable { if (!isLargeMessage(message)) { LOG.warn("Not a large message, proceeding"); - return pjp.proceed(proceedArgs); + return function.apply(message); } String payloadPointer = getMessageContent(message); if (payloadPointer == null) { LOG.warn("No content in the message, proceeding"); - return pjp.proceed(proceedArgs); + return function.apply(message); } + // legacy attribute (sqs only) payloadPointer = payloadPointer.replace("com.amazon.sqs.javamessaging.MessageS3Pointer", "software.amazon.payloadoffloading.PayloadS3Pointer"); @@ -73,7 +85,7 @@ public Object process(ProceedingJoinPoint pjp, boolean deleteS3Object) throws Th updateMessageContent(message, s3ObjectContent); removeLargeMessageAttributes(message); - Object response = pjp.proceed(proceedArgs); + R result = function.apply(message); if (deleteS3Object) { if (LOG.isInfoEnabled()) { @@ -82,45 +94,45 @@ public Object process(ProceedingJoinPoint pjp, boolean deleteS3Object) throws Th deleteS3Object(payloadPointer); } - return response; + return result; } /** - * Retrieve the message id + * Retrieve the message ID. * - * @param message the message itself - * @return the id of the message (String format) + * @param message the message + * @return the message ID */ protected abstract String getMessageId(T message); /** - * Retrieve the content of the message (ex: body of an SQSMessage) + * Retrieve the content of the message (e.g., body of an SQSMessage). * - * @param message the message itself - * @return the content of the message (String format) + * @param message the message + * @return the message content */ protected abstract String getMessageContent(T message); /** - * Update the message content of the message (ex: body of an SQSMessage) + * Update the message content (e.g., body of an SQSMessage). * - * @param message the message itself - * @param messageContent the new content of the message (String format) + * @param message the message + * @param messageContent the new message content */ protected abstract void updateMessageContent(T message, String messageContent); /** - * Check if the message is actually a large message (indicator in message attributes) + * Check if the message is a large message (based on message attributes). * - * @param message the message itself - * @return true if the message is a large message + * @param message the message + * @return true if the message is a large message, false otherwise */ protected abstract boolean isLargeMessage(T message); /** - * Remove the large message indicator (in message attributes) + * Remove the large message indicator from message attributes. * - * @param message the message itself + * @param message the message */ protected abstract void removeLargeMessageAttributes(T message); diff --git a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessorFactory.java b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessorFactory.java index 26c33738a..06ee92968 100644 --- a/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessorFactory.java +++ b/powertools-large-messages/src/main/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageProcessorFactory.java @@ -14,14 +14,15 @@ package software.amazon.lambda.powertools.largemessages.internal; +import java.util.Optional; + import com.amazonaws.services.lambda.runtime.events.SNSEvent.SNSRecord; import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import java.util.Optional; -class LargeMessageProcessorFactory { +public final class LargeMessageProcessorFactory { private LargeMessageProcessorFactory() { - // not intended to be instantiated + // Utility class } public static Optional> get(Object message) { diff --git a/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/LargeMessagesTest.java b/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/LargeMessagesTest.java new file mode 100644 index 000000000..7bd3e80e2 --- /dev/null +++ b/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/LargeMessagesTest.java @@ -0,0 +1,287 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.largemessages; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import java.io.ByteArrayInputStream; +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.amazonaws.services.lambda.runtime.events.KinesisEvent.KinesisEventRecord; +import com.amazonaws.services.lambda.runtime.events.SNSEvent; +import com.amazonaws.services.lambda.runtime.events.SNSEvent.SNS; +import com.amazonaws.services.lambda.runtime.events.SNSEvent.SNSRecord; +import com.amazonaws.services.lambda.runtime.events.SQSEvent.MessageAttribute; +import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; + +import software.amazon.awssdk.core.ResponseInputStream; +import software.amazon.awssdk.http.AbortableInputStream; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; +import software.amazon.awssdk.services.s3.model.GetObjectRequest; +import software.amazon.awssdk.services.s3.model.GetObjectResponse; +import software.amazon.awssdk.services.s3.model.S3Exception; + +@ExtendWith(MockitoExtension.class) +class LargeMessagesTest { + + private static final String BIG_MSG = "A biiiiiiiig message"; + private static final String BUCKET_NAME = "bucketname"; + private static final String BUCKET_KEY = "c71eb2ae-37e0-4265-8909-32f4153faddf"; + private static final String BIG_MESSAGE_BODY = "[\"software.amazon.payloadoffloading.PayloadS3Pointer\", {\"s3BucketName\":\"" + + BUCKET_NAME + + "\", \"s3Key\":\"" + BUCKET_KEY + "\"}]"; + + @Mock + private S3Client s3Client; + + @BeforeEach + void init() throws NoSuchFieldException, IllegalAccessException { + // need to clean the s3Client with introspection (singleton) + Field client = LargeMessageConfig.class.getDeclaredField("s3Client"); + client.setAccessible(true); + client.set(LargeMessageConfig.get(), null); + LargeMessageConfig.init().withS3Client(s3Client); + } + + @Test + void testProcessLargeSQSMessage_shouldRetrieveFromS3AndDelete() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + + // when + String result = LargeMessages.processLargeMessage(sqsMessage, SQSMessage::getBody); + + // then + assertThat(result).isEqualTo(BIG_MSG); + ArgumentCaptor delete = ArgumentCaptor.forClass(DeleteObjectRequest.class); + verify(s3Client).deleteObject(delete.capture()); + assertThat(delete.getValue().bucket()).isEqualTo(BUCKET_NAME); + assertThat(delete.getValue().key()).isEqualTo(BUCKET_KEY); + } + + @Test + void testProcessLargeSQSMessage_withDeleteDisabled_shouldNotDelete() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + + // when + String result = LargeMessages.processLargeMessage(sqsMessage, SQSMessage::getBody, false); + + // then + assertThat(result).isEqualTo(BIG_MSG); + verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); + } + + @Test + void testProcessLargeSNSMessage_shouldRetrieveFromS3AndDelete() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + SNSRecord snsRecord = snsRecordWithMessage(BIG_MESSAGE_BODY, true); + + // when + String result = LargeMessages.processLargeMessage(snsRecord, msg -> msg.getSNS().getMessage()); + + // then + assertThat(result).isEqualTo(BIG_MSG); + verify(s3Client).deleteObject(any(DeleteObjectRequest.class)); + } + + @Test + void testProcessSmallMessage_shouldNotInteractWithS3() { + // given + SQSMessage sqsMessage = sqsMessageWithBody("Small message", false); + + // when + String result = LargeMessages.processLargeMessage(sqsMessage, SQSMessage::getBody); + + // then + assertThat(result).isEqualTo("Small message"); + verifyNoInteractions(s3Client); + } + + @Test + void testProcessUnsupportedMessageType_shouldCallHandlerDirectly() { + // given + KinesisEventRecord kinesisRecord = new KinesisEventRecord(); + kinesisRecord.setEventID("kinesis-123"); + + // when + String result = LargeMessages.processLargeMessage(kinesisRecord, KinesisEventRecord::getEventID); + + // then + assertThat(result).isEqualTo("kinesis-123"); + verifyNoInteractions(s3Client); + } + + @Test + void testProcessMessageWithNullBody_shouldCallHandler() { + // given + SQSMessage sqsMessage = sqsMessageWithBody(null, true); + + // when + String result = LargeMessages.processLargeMessage(sqsMessage, SQSMessage::getBody); + + // then + assertThat(result).isNull(); + verifyNoInteractions(s3Client); + } + + @Test + void testProcessMessage_whenS3GetFails_shouldThrowException() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))) + .thenThrow(S3Exception.create("Access denied", new Exception("Permission denied"))); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + + // when / then + assertThatThrownBy(() -> LargeMessages.processLargeMessage(sqsMessage, SQSMessage::getBody)) + .isInstanceOf(LargeMessageProcessingException.class) + .hasMessageContaining("Failed processing S3 record"); + } + + @Test + void testProcessMessage_whenS3DeleteFails_shouldThrowException() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + when(s3Client.deleteObject(any(DeleteObjectRequest.class))) + .thenThrow(S3Exception.create("Access denied", new Exception("Permission denied"))); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + + // when / then + assertThatThrownBy(() -> LargeMessages.processLargeMessage(sqsMessage, SQSMessage::getBody)) + .isInstanceOf(LargeMessageProcessingException.class) + .hasMessageContaining("Failed deleting S3 record"); + } + + @Test + void testProcessMessage_whenHandlerThrowsRuntimeException_shouldPropagate() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + + // when / then + assertThatThrownBy(() -> LargeMessages.processLargeMessage(sqsMessage, msg -> { + throw new IllegalStateException("Handler error"); + })) + .isInstanceOf(IllegalStateException.class) + .hasMessage("Handler error"); + } + + @Test + void testProcessLargeMessage_withMultiParam_shouldRetrieveFromS3AndDelete() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + String orderId = "order-123"; + + // when + String result = LargeMessages.processLargeMessage(sqsMessage, msg -> processOrderSimple(msg, orderId)); + + // then + assertThat(result).isEqualTo("order-123-processed"); + verify(s3Client).deleteObject(any(DeleteObjectRequest.class)); + } + + @Test + void testProcessLargeMessage_withMultiParamAndDeleteDisabled_shouldNotDelete() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + String orderId = "order-456"; + + // when + String result = LargeMessages.processLargeMessage(sqsMessage, msg -> processOrderSimple(msg, orderId), false); + + // then + assertThat(result).isEqualTo("order-456-processed"); + verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); + } + + @Test + void testProcessLargeMessage_shouldModifyMessageInPlace() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + String originalBody = sqsMessage.getBody(); + + // when + LargeMessages.processLargeMessage(sqsMessage, msg -> { + assertThat(msg.getBody()).isEqualTo(BIG_MSG); + return null; + }); + + // then - verify the original message object was modified + assertThat(sqsMessage.getBody()).isEqualTo(BIG_MSG); + assertThat(sqsMessage.getBody()).isNotEqualTo(originalBody); + } + + private String processOrderSimple(SQSMessage message, String orderId) { + assertThat(message.getBody()).isEqualTo(BIG_MSG); + return orderId + "-processed"; + } + + private ResponseInputStream s3ObjectWithLargeMessage() { + return new ResponseInputStream<>(GetObjectResponse.builder().build(), + AbortableInputStream.create(new ByteArrayInputStream(BIG_MSG.getBytes()))); + } + + private SQSMessage sqsMessageWithBody(String messageBody, boolean largeMessage) { + SQSMessage sqsMessage = new SQSMessage(); + sqsMessage.setBody(messageBody); + if (messageBody != null) { + sqsMessage.setMd5OfBody("dummy-md5"); + } + + if (largeMessage) { + Map attributeMap = new HashMap<>(); + MessageAttribute payloadAttribute = new MessageAttribute(); + payloadAttribute.setStringValue("300450"); + payloadAttribute.setDataType("Number"); + attributeMap.put("ExtendedPayloadSize", payloadAttribute); + + sqsMessage.setMessageAttributes(attributeMap); + sqsMessage.setMd5OfMessageAttributes("dummy-md5"); + } + return sqsMessage; + } + + private SNSRecord snsRecordWithMessage(String messageBody, boolean largeMessage) { + SNS sns = new SNS().withMessage(messageBody); + if (largeMessage) { + sns.setMessageAttributes(Collections.singletonMap("ExtendedPayloadSize", + new SNSEvent.MessageAttribute())); + } + return new SNSRecord().withSns(sns); + } +} diff --git a/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageAspectTest.java b/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageAspectTest.java index c364a89d9..b84709ddc 100644 --- a/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageAspectTest.java +++ b/powertools-large-messages/src/test/java/software/amazon/lambda/powertools/largemessages/internal/LargeMessageAspectTest.java @@ -22,17 +22,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; import static software.amazon.lambda.powertools.largemessages.internal.LargeSQSMessageProcessor.calculateMessageAttributesMd5; import static software.amazon.lambda.powertools.largemessages.internal.LargeSQSMessageProcessor.calculateMessageBodyMd5; -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.KinesisEvent.KinesisEventRecord; -import com.amazonaws.services.lambda.runtime.events.SNSEvent; -import com.amazonaws.services.lambda.runtime.events.SNSEvent.SNS; -import com.amazonaws.services.lambda.runtime.events.SNSEvent.SNSRecord; -import com.amazonaws.services.lambda.runtime.events.SQSEvent.MessageAttribute; -import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; import java.io.ByteArrayInputStream; import java.lang.reflect.Field; import java.nio.ByteBuffer; @@ -41,11 +33,22 @@ import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; + import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.amazonaws.services.lambda.runtime.events.KinesisEvent.KinesisEventRecord; +import com.amazonaws.services.lambda.runtime.events.SNSEvent; +import com.amazonaws.services.lambda.runtime.events.SNSEvent.SNS; +import com.amazonaws.services.lambda.runtime.events.SNSEvent.SNSRecord; +import com.amazonaws.services.lambda.runtime.events.SQSEvent.MessageAttribute; +import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; + import software.amazon.awssdk.core.ResponseInputStream; import software.amazon.awssdk.http.AbortableInputStream; import software.amazon.awssdk.services.s3.S3Client; @@ -53,11 +56,13 @@ import software.amazon.awssdk.services.s3.model.GetObjectRequest; import software.amazon.awssdk.services.s3.model.GetObjectResponse; import software.amazon.awssdk.services.s3.model.S3Exception; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; import software.amazon.lambda.powertools.largemessages.LargeMessage; import software.amazon.lambda.powertools.largemessages.LargeMessageConfig; import software.amazon.lambda.powertools.largemessages.LargeMessageProcessingException; -public class LargeMessageAspectTest { +@ExtendWith(MockitoExtension.class) +class LargeMessageAspectTest { private static final String BIG_MSG = "A biiiiiiiig message"; private static final String BIG_MSG_MD5 = "919ebd392d8cb7161f95cb612a903d42"; @@ -65,19 +70,17 @@ public class LargeMessageAspectTest { private static final String BUCKET_NAME = "bucketname"; private static final String BUCKET_KEY = "c71eb2ae-37e0-4265-8909-32f4153faddf"; - private static final String BIG_MESSAGE_BODY = - "[\"software.amazon.payloadoffloading.PayloadS3Pointer\", {\"s3BucketName\":\"" + BUCKET_NAME + - "\", \"s3Key\":\"" + BUCKET_KEY + "\"}]"; + private static final String BIG_MESSAGE_BODY = "[\"software.amazon.payloadoffloading.PayloadS3Pointer\", {\"s3BucketName\":\"" + + BUCKET_NAME + + "\", \"s3Key\":\"" + BUCKET_KEY + "\"}]"; @Mock private S3Client s3Client; - @Mock - private Context context; + + private final TestLambdaContext context = new TestLambdaContext(); @BeforeEach - public void init() throws NoSuchFieldException, IllegalAccessException { - openMocks(this); - setupContext(); + void init() throws NoSuchFieldException, IllegalAccessException { // need to clean the s3Client with introspection (singleton) Field client = LargeMessageConfig.class.getDeclaredField("s3Client"); client.setAccessible(true); @@ -86,13 +89,13 @@ public void init() throws NoSuchFieldException, IllegalAccessException { } @LargeMessage - private String processSQSMessage(SQSMessage sqsMessage, Context context) { + private String processSQSMessage(SQSMessage sqsMessage, TestLambdaContext context) { return sqsMessage.getBody(); } @LargeMessage private String processSQSMessageWithMd5Checks(SQSMessage transformedMessage, String initialBodyMD5, - String initialAttributesMD5) { + String initialAttributesMD5) { assertThat(transformedMessage.getMd5OfBody()).isNotEqualTo(initialBodyMD5); assertThat(transformedMessage.getMd5OfBody()).isEqualTo(BIG_MSG_MD5); @@ -108,7 +111,7 @@ private String processSNSMessageWithoutContext(SNSRecord snsRecord) { } @LargeMessage(deleteS3Object = false) - private String processSQSMessageNoDelete(SQSMessage sqsMessage, Context context) { + private String processSQSMessageNoDelete(SQSMessage sqsMessage, TestLambdaContext context) { return sqsMessage.getBody(); } @@ -122,8 +125,15 @@ private String processNoMessage() { return "Hello World"; } + @LargeMessage + private void verifyMessageObjectIsModified(SQSMessage sqsMessage) { + // This test verifies the message object itself is modified, not a copy + assertThat(sqsMessage.getBody()).isEqualTo(BIG_MSG); + assertThat(sqsMessage.getMd5OfBody()).isEqualTo(BIG_MSG_MD5); + } + @Test - public void testLargeSQSMessageWithDefaultDeletion() { + void testLargeSQSMessageWithDefaultDeletion() { // given when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); @@ -136,8 +146,7 @@ public void testLargeSQSMessageWithDefaultDeletion() { ArgumentCaptor delete = ArgumentCaptor.forClass(DeleteObjectRequest.class); verify(s3Client).deleteObject(delete.capture()); Assertions.assertThat(delete.getValue()) - .satisfies((Consumer) deleteObjectRequest -> - { + .satisfies((Consumer) deleteObjectRequest -> { assertThat(deleteObjectRequest.bucket()) .isEqualTo(BUCKET_NAME); @@ -147,7 +156,7 @@ public void testLargeSQSMessageWithDefaultDeletion() { } @Test - public void testLargeSQSMessage_shouldChangeMd5OfBodyAndAttributes() { + void testLargeSQSMessage_shouldChangeMd5OfBodyAndAttributes() { // given when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); @@ -179,12 +188,12 @@ public void testLargeSQSMessage_shouldChangeMd5OfBodyAndAttributes() { } @Test - public void testLargeSNSMessageWithDefaultDeletion() { + void testLargeSNSMessageWithDefaultDeletion() { // given when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); SNSRecord snsRecord = snsRecordWithMessage(BIG_MESSAGE_BODY, true); - //when + // when String message = processSNSMessageWithoutContext(snsRecord); // then @@ -192,8 +201,7 @@ public void testLargeSNSMessageWithDefaultDeletion() { ArgumentCaptor delete = ArgumentCaptor.forClass(DeleteObjectRequest.class); verify(s3Client).deleteObject(delete.capture()); Assertions.assertThat(delete.getValue()) - .satisfies((Consumer) deleteObjectRequest -> - { + .satisfies((Consumer) deleteObjectRequest -> { assertThat(deleteObjectRequest.bucket()) .isEqualTo(BUCKET_NAME); @@ -203,7 +211,7 @@ public void testLargeSNSMessageWithDefaultDeletion() { } @Test - public void testLargeSQSMessageWithNoDeletion_shouldNotDelete() { + void testLargeSQSMessageWithNoDeletion_shouldNotDelete() { // given when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); @@ -217,7 +225,7 @@ public void testLargeSQSMessageWithNoDeletion_shouldNotDelete() { } @Test - public void testKinesisMessage_shouldProceedWithoutS3() { + void testKinesisMessage_shouldProceedWithoutS3() { // given KinesisEventRecord kinesisEventRecord = new KinesisEventRecord(); kinesisEventRecord.setEventID("kinesis_id1234567890"); @@ -231,7 +239,7 @@ public void testKinesisMessage_shouldProceedWithoutS3() { } @Test - public void testNoMessage_shouldProceedWithoutS3() { + void testNoMessage_shouldProceedWithoutS3() { // when String message = processNoMessage(); @@ -241,7 +249,7 @@ public void testNoMessage_shouldProceedWithoutS3() { } @Test - public void testSmallMessage_shouldProceedWithoutS3() { + void testSmallMessage_shouldProceedWithoutS3() { // given SQSMessage sqsMessage = sqsMessageWithBody("This is small message", false); @@ -255,7 +263,7 @@ public void testSmallMessage_shouldProceedWithoutS3() { } @Test - public void testNullMessage_shouldProceedWithoutS3() { + void testNullMessage_shouldProceedWithoutS3() { // given SQSMessage sqsMessage = sqsMessageWithBody(null, true); @@ -268,7 +276,7 @@ public void testNullMessage_shouldProceedWithoutS3() { } @Test - public void testGetS3ObjectException_shouldThrowLargeMessageProcessingException() { + void testGetS3ObjectException_shouldThrowLargeMessageProcessingException() { // given when(s3Client.getObject(any(GetObjectRequest.class))).thenThrow(S3Exception.create("Permission denied", new Exception("User is not allowed to access bucket " + BUCKET_NAME))); @@ -281,7 +289,7 @@ public void testGetS3ObjectException_shouldThrowLargeMessageProcessingException( } @Test - public void testDeleteS3ObjectException_shouldThrowLargeMessageProcessingException() { + void testDeleteS3ObjectException_shouldThrowLargeMessageProcessingException() { // given when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); when(s3Client.deleteObject(any(DeleteObjectRequest.class))).thenThrow(S3Exception.create("Permission denied", @@ -294,6 +302,22 @@ public void testDeleteS3ObjectException_shouldThrowLargeMessageProcessingExcepti .hasMessage(format("Failed deleting S3 record [%s]", BIG_MESSAGE_BODY)); } + @Test + void testMessageObjectIsModifiedInPlace() { + // given + when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); + SQSMessage sqsMessage = sqsMessageWithBody(BIG_MESSAGE_BODY, true); + String originalBody = sqsMessage.getBody(); + + // when + verifyMessageObjectIsModified(sqsMessage); + + // then - verify the original message object was modified + assertThat(sqsMessage.getBody()).isEqualTo(BIG_MSG); + assertThat(sqsMessage.getBody()).isNotEqualTo(originalBody); + assertThat(sqsMessage.getMd5OfBody()).isEqualTo(BIG_MSG_MD5); + } + private ResponseInputStream s3ObjectWithLargeMessage() { return new ResponseInputStream<>(GetObjectResponse.builder().build(), AbortableInputStream.create(new ByteArrayInputStream(BIG_MSG.getBytes()))); @@ -304,7 +328,7 @@ private SQSMessage sqsMessageWithBody(String messageBody, boolean largeMessage) } private SQSMessage sqsMessageWithBody(String messageBody, boolean largeMessage, - Map optionalAttributes) { + Map optionalAttributes) { SQSMessage sqsMessage = new SQSMessage(); sqsMessage.setBody(messageBody); if (messageBody != null) { @@ -338,10 +362,4 @@ private SNSRecord snsRecordWithMessage(String messageBody, boolean largeMessage) return new SNSRecord().withSns(sns); } - private void setupContext() { - when(context.getFunctionName()).thenReturn("testFunction"); - when(context.getInvokedFunctionArn()).thenReturn("testArn"); - when(context.getFunctionVersion()).thenReturn("1"); - when(context.getMemoryLimitInMB()).thenReturn(1024); - } } diff --git a/powertools-large-messages/src/test/resources/simplelogger.properties b/powertools-large-messages/src/test/resources/simplelogger.properties new file mode 100644 index 000000000..559c22385 --- /dev/null +++ b/powertools-large-messages/src/test/resources/simplelogger.properties @@ -0,0 +1,7 @@ +org.slf4j.simpleLogger.logFile=target/large-messages-test.log +org.slf4j.simpleLogger.defaultLogLevel=warn +org.slf4j.simpleLogger.showDateTime=true +org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS +org.slf4j.simpleLogger.showThreadName=false +org.slf4j.simpleLogger.showLogName=true +org.slf4j.simpleLogger.showShortLogName=false From f106143a2beafd24e0258fb8318cbcacdebb6e7e Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Mon, 3 Nov 2025 17:56:19 +0100 Subject: [PATCH 09/76] fix(idempotency): Validate payload for optimistic idempotent writes. (#2261) * Validate payload for optimistic idempotent writes. * Re-throw IdempotencyValidationException instead of wrapping in a persistence layer exception. --- .../internal/IdempotencyHandler.java | 2 + .../persistence/BasePersistenceStore.java | 45 ++++++++++----- .../persistence/BasePersistenceStoreTest.java | 56 +++++++++++++++++-- 3 files changed, 84 insertions(+), 19 deletions(-) diff --git a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java index 77d3d49b0..d4e0d2222 100644 --- a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java @@ -109,6 +109,8 @@ private Object processIdempotency() throws Throwable { } } catch (IdempotencyKeyException ike) { throw ike; + } catch (IdempotencyValidationException ive) { + throw ive; } catch (Exception e) { throw new IdempotencyPersistenceLayerException( "Failed to save in progress record to idempotency store. If you believe this is a Powertools for AWS Lambda (Java) bug, please open an issue.", diff --git a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java index 7e93dc00c..00ddc4336 100644 --- a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java @@ -16,10 +16,6 @@ import static software.amazon.lambda.powertools.common.internal.LambdaConstants.LAMBDA_FUNCTION_NAME_ENV; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectWriter; -import io.burt.jmespath.Expression; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; @@ -33,8 +29,15 @@ import java.util.Spliterators; import java.util.stream.Stream; import java.util.stream.StreamSupport; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectWriter; + +import io.burt.jmespath.Expression; import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; @@ -125,8 +128,7 @@ public void saveSuccess(JsonNode data, Object result, Instant now) { DataRecord.Status.COMPLETED, getExpiryEpochSecond(now), responseJson, - getHashedPayload(data) - ); + getHashedPayload(data)); LOG.debug("Function successfully executed. Saving record to persistence store with idempotency key: {}", dataRecord.getIdempotencyKey()); updateRecord(dataRecord); @@ -157,8 +159,8 @@ public void saveInProgress(JsonNode data, Instant now, OptionalInt remainingTime OptionalLong inProgressExpirationMsTimestamp = OptionalLong.empty(); if (remainingTimeInMs.isPresent()) { - inProgressExpirationMsTimestamp = - OptionalLong.of(now.plus(remainingTimeInMs.getAsInt(), ChronoUnit.MILLIS).toEpochMilli()); + inProgressExpirationMsTimestamp = OptionalLong + .of(now.plus(remainingTimeInMs.getAsInt(), ChronoUnit.MILLIS).toEpochMilli()); } DataRecord dataRecord = new DataRecord( @@ -167,10 +169,23 @@ public void saveInProgress(JsonNode data, Instant now, OptionalInt remainingTime getExpiryEpochSecond(now), null, getHashedPayload(data), - inProgressExpirationMsTimestamp - ); + inProgressExpirationMsTimestamp); LOG.debug("saving in progress record for idempotency key: {}", dataRecord.getIdempotencyKey()); - putRecord(dataRecord, now); + + try { + putRecord(dataRecord, now); + } catch (IdempotencyItemAlreadyExistsException iaee) { + // Similar to getRecord, we need to call validatePayload before returning a data record. + // PR https://github.com/aws-powertools/powertools-lambda-java/pull/1821 introduced returning a data record + // through IdempotencyItemAlreadyExistsException to save DynamoDB calls when using DDB as store. + Optional dr = iaee.getDataRecord(); + if (dr.isPresent()) { + // throws IdempotencyValidationException if payload validation is enabled and failing + validatePayload(data, dr.get()); + } + + throw iaee; + } } /** @@ -188,7 +203,7 @@ public void deleteRecord(JsonNode data, Throwable throwable) { String idemPotencyKey = hashedIdempotencyKey.get(); LOG.debug("Function raised an exception {}. " + - "Clearing in progress record in persistence store for idempotency key: {}", + "Clearing in progress record in persistence store for idempotency key: {}", throwable.getClass(), idemPotencyKey); @@ -255,9 +270,9 @@ private Optional getHashedIdempotencyKey(JsonNode data) { private boolean isMissingIdemPotencyKey(JsonNode data) { if (data.isContainerNode()) { - Stream stream = - StreamSupport.stream(Spliterators.spliteratorUnknownSize(data.elements(), Spliterator.ORDERED), - false); + Stream stream = StreamSupport.stream( + Spliterators.spliteratorUnknownSize(data.elements(), Spliterator.ORDERED), + false); return stream.allMatch(JsonNode::isNull); } return data.isNull(); diff --git a/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java index d5d45c78f..8e46de1cc 100644 --- a/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java +++ b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java @@ -28,6 +28,7 @@ import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.tests.EventLoader; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.DoubleNode; import com.fasterxml.jackson.databind.node.TextNode; @@ -145,8 +146,8 @@ void saveInProgress_jmespath_NotFound_shouldThrowException() { assertThatThrownBy( () -> persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty())) - .isInstanceOf(IdempotencyKeyException.class) - .hasMessageContaining("No data found to create a hashed idempotency key"); + .isInstanceOf(IdempotencyKeyException.class) + .hasMessageContaining("No data found to create a hashed idempotency key"); assertThat(status).isEqualTo(-1); } @@ -181,7 +182,7 @@ void saveInProgress_withLocalCache_NotExpired_ShouldThrowException() { assertThatThrownBy( () -> persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty())) - .isInstanceOf(IdempotencyItemAlreadyExistsException.class); + .isInstanceOf(IdempotencyItemAlreadyExistsException.class); assertThat(status).isEqualTo(-1); } @@ -243,7 +244,8 @@ void saveSuccess_withCacheEnabled_shouldSaveInCache() throws JsonProcessingExcep DataRecord cachedDr = cache.get("testFunction#8d6a8f173b46479eff55e0997864a514"); assertThat(cachedDr.getStatus()).isEqualTo(DataRecord.Status.COMPLETED); assertThat(cachedDr.getExpiryTimestamp()).isEqualTo(now.plus(3600, ChronoUnit.SECONDS).getEpochSecond()); - assertThat(cachedDr.getResponseData()).isEqualTo(JsonConfig.get().getObjectMapper().writeValueAsString(product)); + assertThat(cachedDr.getResponseData()) + .isEqualTo(JsonConfig.get().getObjectMapper().writeValueAsString(product)); assertThat(cachedDr.getIdempotencyKey()).isEqualTo("testFunction#8d6a8f173b46479eff55e0997864a514"); assertThat(cachedDr.getPayloadHash()).isEmpty(); } @@ -325,6 +327,52 @@ void getRecord_invalidPayload_shouldThrowValidationException() { assertThatThrownBy( () -> persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), Instant.now())) + .isInstanceOf(IdempotencyValidationException.class); + } + + @Test + void saveInProgress_invalidPayload_shouldThrowValidationException() { + APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); + persistenceStore = new BasePersistenceStore() { + @Override + public DataRecord getRecord(String idempotencyKey) throws IdempotencyItemNotFoundException { + return new DataRecord(idempotencyKey, DataRecord.Status.INPROGRESS, + Instant.now().plus(3600, ChronoUnit.SECONDS).getEpochSecond(), "Response", "different hash"); + } + + @Override + public void putRecord(DataRecord dataRecord, Instant now) throws IdempotencyItemAlreadyExistsException { + DataRecord existingRecord = new DataRecord( + dataRecord.getIdempotencyKey(), + DataRecord.Status.INPROGRESS, + Instant.now().plus(3600, ChronoUnit.SECONDS).getEpochSecond(), + null, + "different hash"); + throw new IdempotencyItemAlreadyExistsException("Item already exists", new Exception(), existingRecord); + } + + @Override + public void updateRecord(DataRecord dataRecord) { + // Not needed for this test. + } + + @Override + public void deleteRecord(String idempotencyKey) { + // Not needed for this test. + + } + }; + + persistenceStore.configure(IdempotencyConfig.builder() + .withEventKeyJMESPath("powertools_json(body).id") + .withPayloadValidationJMESPath("powertools_json(body).message") + .build(), + "myfunc"); + + Instant now = Instant.now(); + OptionalInt remainingTime = OptionalInt.empty(); + JsonNode eventJson = JsonConfig.get().getObjectMapper().valueToTree(event); + assertThatThrownBy(() -> persistenceStore.saveInProgress(eventJson, now, remainingTime)) .isInstanceOf(IdempotencyValidationException.class); } From 68b6c026cb62785c23e88ca88f601ff4833e3f63 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 18:08:56 +0100 Subject: [PATCH 10/76] chore: bump aws.sdk.version from 2.36.3 to 2.37.2 (#2253) Bumps `aws.sdk.version` from 2.36.3 to 2.37.2. Updates `software.amazon.awssdk:url-connection-client` from 2.36.3 to 2.37.2 Updates `software.amazon.awssdk:sdk-core` from 2.37.1 to 2.37.2 Updates `software.amazon.awssdk:s3` from 2.36.3 to 2.37.2 Updates `software.amazon.awssdk:kinesis` from 2.36.3 to 2.37.2 Updates `software.amazon.awssdk:sqs` from 2.36.3 to 2.37.2 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.37.1 to 2.37.2 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.37.2 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sdk-core dependency-version: 2.37.2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.37.2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.37.2 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.37.2 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-version: 2.37.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 19b404c82..75b283d3d 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ 11 11 1.9.20.1 - 2.37.1 + 2.37.2 From 93d9ee60b69d4de6caab6c41d1f8a7ee15b0dd7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 18:09:08 +0100 Subject: [PATCH 11/76] chore: bump com.fasterxml.jackson:jackson-bom from 2.20.0 to 2.20.1 (#2254) Bumps [com.fasterxml.jackson:jackson-bom](https://github.com/FasterXML/jackson-bom) from 2.20.0 to 2.20.1. - [Commits](https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.20.0...jackson-bom-2.20.1) --- updated-dependencies: - dependency-name: com.fasterxml.jackson:jackson-bom dependency-version: 2.20.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 449baba05..a790145c1 100644 --- a/pom.xml +++ b/pom.xml @@ -84,7 +84,7 @@ 3.1.2 2.25.2 2.0.17 - 2.20.0 + 2.20.1 2.36.3 2.20.0 2.2.0 From 25f02a9c2cd022729a0751f7bdbf4a38f8e8410d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 18:09:24 +0100 Subject: [PATCH 12/76] chore: bump github/codeql-action from 4.31.1 to 4.31.2 (#2255) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.1 to 4.31.2. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/5fe9434cd24fe243e33e7f3305f8a5b519b70280...0499de31b99561a6d14a36a5f662c2a54f91beee) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.31.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 632e4b0d9..996135fa9 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@5fe9434cd24fe243e33e7f3305f8a5b519b70280 # v3.29.5 + uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5 with: sarif_file: results.sarif From 357f1206801ce8ada3784ecf398ecc8015a722ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 18:10:31 +0100 Subject: [PATCH 13/76] chore: bump squidfunk/mkdocs-material in /docs (#2259) Bumps squidfunk/mkdocs-material from `f5c556a` to `58dee36`. --- updated-dependencies: - dependency-name: squidfunk/mkdocs-material dependency-version: 58dee36ad85b0ae4836522ee6d3f0150d828bca9a1f7d3bfbf430bca771c1441 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Dockerfile b/docs/Dockerfile index 9b8bde4c5..88d6ca284 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,4 +1,4 @@ -FROM squidfunk/mkdocs-material@sha256:f5c556a6d30ce0c1c0df10e3c38c79bbcafdaea4b1c1be366809d0d4f6f9d57f +FROM squidfunk/mkdocs-material@sha256:58dee36ad85b0ae4836522ee6d3f0150d828bca9a1f7d3bfbf430bca771c1441 COPY requirements.txt /tmp/ RUN pip install --require-hashes -r /tmp/requirements.txt From 41f3ef517db2561e596963590b9beb47a2bd6b0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Nov 2025 11:12:25 +0100 Subject: [PATCH 14/76] chore: bump aws.sdk.version from 2.36.3 to 2.37.3 (#2258) Bumps `aws.sdk.version` from 2.36.3 to 2.37.3. Updates `software.amazon.awssdk:bom` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:http-client-spi` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:url-connection-client` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:s3` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:dynamodb` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:lambda` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:kinesis` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:cloudwatch` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:xray` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:sqs` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:cloudformation` from 2.36.3 to 2.37.3 Updates `software.amazon.awssdk:sts` from 2.36.3 to 2.37.3 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.37.3 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.37.3 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.37.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.37.3 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.37.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.37.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.37.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.37.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-version: 2.37.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.37.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.37.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-version: 2.37.3 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 7f9bbc6fa..a164315fe 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ 11 1.4.0 3.16.1 - 2.36.3 + 2.37.3 1.9.20.1 diff --git a/pom.xml b/pom.xml index a790145c1..dbf1ac898 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ 2.25.2 2.0.17 2.20.1 - 2.36.3 + 2.37.3 2.20.0 2.2.0 UTF-8 diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index f171422c5..82b7a1de3 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ 3.6.1 1.14.1 3.14.1 - 2.36.3 + 2.37.3 1.9.20.1 true From 93582c5a895252b7c81ab1757f6048e60e44d596 Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Tue, 4 Nov 2025 15:31:35 +0100 Subject: [PATCH 15/76] Update UA configurator to comply with 50 chars limit. (#2268) --- .../internal/UserAgentConfigurator.java | 35 +++++++- .../internal/UserAgentConfiguratorTest.java | 81 ++++++++++++++++++- 2 files changed, 111 insertions(+), 5 deletions(-) diff --git a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java index 7deca89f1..27b69d5ad 100644 --- a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java +++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/UserAgentConfigurator.java @@ -82,18 +82,29 @@ static String getVersionFromProperties(String propertyFileName, String versionKe /** * Configures the AWS SDK to use Powertools user agent by setting the sdk.ua.appId system property. - * If the property is already set and not empty, appends the Powertools user agent with a "/" separator. + * Preserves any user-provided value and replaces any existing Powertools user agent. + * Enforces a 50 character limit to comply with AWS SDK recommendations. * This should be called during library initialization to ensure the user agent is properly configured. */ public static void configureUserAgent(String ptFeature) { try { String existingValue = System.getProperty(SDK_USER_AGENT_APP_ID); String powertoolsUserAgent = getUserAgent(ptFeature); + String newValue; - if (existingValue != null && !existingValue.isEmpty()) { - System.setProperty(SDK_USER_AGENT_APP_ID, existingValue + "/" + powertoolsUserAgent); + if (existingValue == null || existingValue.isEmpty()) { + newValue = powertoolsUserAgent; } else { - System.setProperty(SDK_USER_AGENT_APP_ID, powertoolsUserAgent); + String userValue = extractUserValue(existingValue); + if (userValue.isEmpty()) { + newValue = powertoolsUserAgent; + } else { + newValue = userValue + "/" + powertoolsUserAgent; + } + } + + if (newValue.length() <= 50) { + System.setProperty(SDK_USER_AGENT_APP_ID, newValue); } } catch (Exception e) { // We don't re-raise since we don't want to break the user if something in this logic doesn't work @@ -101,6 +112,22 @@ public static void configureUserAgent(String ptFeature) { } } + /** + * Extracts the user-provided value from the existing user agent string by removing any Powertools user agent. + * A Powertools user agent follows the pattern "PT/{FEATURE}/{VERSION} PTENV/{ENV}". + * + * @param existingValue the existing user agent string + * @return the user-provided value without Powertools user agent, or empty string if none exists + */ + static String extractUserValue(String existingValue) { + if (existingValue == null || existingValue.isEmpty()) { + return ""; + } + // Remove Powertools user agent pattern: PT/{FEATURE}/{VERSION} PTENV/{ENV} + String result = existingValue.replaceAll("/?PT/[^/]+/[^ ]+ PTENV/[^ ]+", ""); + return result.trim(); + } + /** * Retrieves the user agent string for the Powertools for AWS Lambda. * It follows the pattern PT/{PT_FEATURE}/{PT_VERSION} PTENV/{PT_EXEC_ENV} diff --git a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/UserAgentConfiguratorTest.java b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/UserAgentConfiguratorTest.java index fbe4529d8..33050d8b4 100644 --- a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/UserAgentConfiguratorTest.java +++ b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/UserAgentConfiguratorTest.java @@ -131,7 +131,7 @@ void testConfigureUserAgent() { } @Test - void testConfigureUserAgent_WithExistingValue() { + void testConfigureUserAgent_WithExistingUserValue() { System.setProperty("sdk.ua.appId", "UserValueABC123"); UserAgentConfigurator.configureUserAgent("test-feature"); @@ -139,6 +139,64 @@ void testConfigureUserAgent_WithExistingValue() { .isEqualTo("UserValueABC123/PT/TEST-FEATURE/" + VERSION + " PTENV/NA"); } + @Test + void testConfigureUserAgent_ReplacePowertoolsUserAgent() { + System.setProperty("sdk.ua.appId", "PT/BATCH/" + VERSION + " PTENV/NA"); + UserAgentConfigurator.configureUserAgent("logging-log4j"); + + assertThat(System.getProperty("sdk.ua.appId")) + .isEqualTo("PT/LOGGING-LOG4J/" + VERSION + " PTENV/NA"); + } + + @Test + void testConfigureUserAgent_PreserveUserValueAndReplacePowertools() { + System.setProperty("sdk.ua.appId", "UserValue/PT/BATCH/" + VERSION + " PTENV/NA"); + UserAgentConfigurator.configureUserAgent("tracing"); + + assertThat(System.getProperty("sdk.ua.appId")) + .isEqualTo("UserValue/PT/TRACING/" + VERSION + " PTENV/NA"); + } + + @Test + void testConfigureUserAgent_ExceedsLimit() { + System.setProperty("sdk.ua.appId", "VeryLongUserValueThatExceedsTheLimitWhenCombined"); + UserAgentConfigurator.configureUserAgent("test-feature"); + + // Should not update if it would exceed 50 characters + assertThat(System.getProperty("sdk.ua.appId")) + .isEqualTo("VeryLongUserValueThatExceedsTheLimitWhenCombined"); + } + + @Test + void testExtractUserValue_NoUserValue() { + String result = UserAgentConfigurator.extractUserValue("PT/BATCH/" + VERSION + " PTENV/NA"); + assertThat(result).isEmpty(); + } + + @Test + void testExtractUserValue_WithUserValue() { + String result = UserAgentConfigurator.extractUserValue("UserValue/PT/BATCH/" + VERSION + " PTENV/NA"); + assertThat(result).isEqualTo("UserValue"); + } + + @Test + void testExtractUserValue_EmptyString() { + String result = UserAgentConfigurator.extractUserValue(""); + assertThat(result).isEmpty(); + } + + @Test + void testExtractUserValue_NullString() { + String result = UserAgentConfigurator.extractUserValue(null); + assertThat(result).isEmpty(); + } + + @Test + void testExtractUserValue_OnlyUserValue() { + String result = UserAgentConfigurator.extractUserValue("MyCustomValue"); + assertThat(result).isEqualTo("MyCustomValue"); + } + @Test void testConfigureUserAgent_WithEmptyExistingValue() { System.setProperty("sdk.ua.appId", ""); @@ -148,4 +206,25 @@ void testConfigureUserAgent_WithEmptyExistingValue() { .isEqualTo("PT/TEST-FEATURE/" + VERSION + " PTENV/NA"); } + @Test + @SetEnvironmentVariable(key = AWS_EXECUTION_ENV, value = "AWS_Lambda_java11") + void testConfigureUserAgent_MultipleUtilities() { + System.clearProperty("sdk.ua.appId"); + + // First utility + UserAgentConfigurator.configureUserAgent("batch"); + assertThat(System.getProperty("sdk.ua.appId")) + .isEqualTo("PT/BATCH/" + VERSION + " PTENV/AWS_Lambda_java11"); + + // Second utility - should replace, not append + UserAgentConfigurator.configureUserAgent("logging-log4j"); + assertThat(System.getProperty("sdk.ua.appId")) + .isEqualTo("PT/LOGGING-LOG4J/" + VERSION + " PTENV/AWS_Lambda_java11"); + + // Third utility - should replace again + UserAgentConfigurator.configureUserAgent("tracing"); + assertThat(System.getProperty("sdk.ua.appId")) + .isEqualTo("PT/TRACING/" + VERSION + " PTENV/AWS_Lambda_java11"); + } + } From d1c5b46ea263ebafabecf64a0b72a2652806be35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Nov 2025 15:33:36 +0100 Subject: [PATCH 16/76] chore: bump org.junit.jupiter:junit-jupiter from 5.14.0 to 5.14.1 (#2265) Bumps [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit-framework) from 5.14.0 to 5.14.1. - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r5.14.0...r5.14.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter dependency-version: 5.14.1 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index ce887884c..3a07e6e6c 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -9,7 +9,7 @@ UTF-8 2.221.1 [10.0.0,11.0.0) - 5.14.0 + 5.14.1 From 578cb8616a3a76c921ef01a319bcb601e153fb31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Nov 2025 15:34:09 +0100 Subject: [PATCH 17/76] chore: bump aws.sdk.version from 2.37.3 to 2.37.4 (#2264) Bumps `aws.sdk.version` from 2.37.3 to 2.37.4. Updates `software.amazon.awssdk:bom` from 2.37.3 to 2.37.4 Updates `software.amazon.awssdk:http-client-spi` from 2.37.3 to 2.37.4 Updates `software.amazon.awssdk:url-connection-client` from 2.37.2 to 2.37.4 Updates `software.amazon.awssdk:s3` from 2.37.2 to 2.37.4 Updates `software.amazon.awssdk:dynamodb` from 2.37.3 to 2.37.4 Updates `software.amazon.awssdk:lambda` from 2.37.3 to 2.37.4 Updates `software.amazon.awssdk:kinesis` from 2.37.2 to 2.37.4 Updates `software.amazon.awssdk:cloudwatch` from 2.37.3 to 2.37.4 Updates `software.amazon.awssdk:xray` from 2.37.3 to 2.37.4 Updates `software.amazon.awssdk:sqs` from 2.37.2 to 2.37.4 Updates `software.amazon.awssdk:cloudformation` from 2.37.3 to 2.37.4 Updates `software.amazon.awssdk:sts` from 2.37.3 to 2.37.4 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.37.4 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.37.4 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.37.4 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.37.4 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.37.4 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.37.4 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.37.4 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.37.4 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.37.4 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.37.4 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.37.4 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.37.4 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index a164315fe..edd774e74 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ 11 1.4.0 3.16.1 - 2.37.3 + 2.37.4 1.9.20.1 diff --git a/pom.xml b/pom.xml index dbf1ac898..bc5389841 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ 2.25.2 2.0.17 2.20.1 - 2.37.3 + 2.37.4 2.20.0 2.2.0 UTF-8 diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 82b7a1de3..827feccea 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ 3.6.1 1.14.1 3.14.1 - 2.37.3 + 2.37.4 1.9.20.1 true From 9251e18ab11eb647d9d040c6a6822c73fbe5822f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Nov 2025 17:00:10 +0100 Subject: [PATCH 18/76] chore: bump io.github.ascopes:protobuf-maven-plugin (#2269) Bumps [io.github.ascopes:protobuf-maven-plugin](https://github.com/ascopes/protobuf-maven-plugin) from 3.3.0 to 3.10.2. - [Release notes](https://github.com/ascopes/protobuf-maven-plugin/releases) - [Commits](https://github.com/ascopes/protobuf-maven-plugin/compare/v3.3.0...v3.10.2) --- updated-dependencies: - dependency-name: io.github.ascopes:protobuf-maven-plugin dependency-version: 3.10.2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-kafka/tools/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-kafka/tools/pom.xml b/examples/powertools-examples-kafka/tools/pom.xml index 80ed6c264..e6f2654d1 100644 --- a/examples/powertools-examples-kafka/tools/pom.xml +++ b/examples/powertools-examples-kafka/tools/pom.xml @@ -62,7 +62,7 @@ io.github.ascopes protobuf-maven-plugin - 3.3.0 + 3.10.2 From 9ca99332edc2732cce3a7eeb9b2acf79aa489abf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 5 Nov 2025 11:53:05 +0100 Subject: [PATCH 19/76] chore(ci): bump version to 2.6.0 (#2270) * chore(ci): bump version to 2.6.0 * Restore CHANGELOG.md from main. --------- Co-authored-by: Powertools for AWS Lambda (Java) Bot <151832416+aws-powertools-bot@users.noreply.github.com> Co-authored-by: Philipp Page --- README.md | 6 +++--- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/README.md | 4 ++-- .../infra/sam-graalvm/README.md | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- .../powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- .../powertools-examples-core-utilities/gradle/build.gradle | 6 +++--- .../kotlin/build.gradle.kts | 6 +++--- .../powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- .../powertools-examples-core-utilities/serverless/pom.xml | 2 +- .../powertools-examples-core-utilities/terraform/pom.xml | 2 +- .../powertools-examples-idempotency/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-idempotency/sam/pom.xml | 2 +- examples/powertools-examples-kafka/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- .../powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-common/pom.xml | 2 +- powertools-e2e-tests/handlers/batch/pom.xml | 2 +- .../handlers/idempotency-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/idempotency-generics/pom.xml | 2 +- powertools-e2e-tests/handlers/idempotency/pom.xml | 2 +- .../handlers/largemessage-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/largemessage/pom.xml | 2 +- .../handlers/largemessage_idempotent/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-log4j/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-logback/pom.xml | 2 +- powertools-e2e-tests/handlers/metrics/pom.xml | 2 +- powertools-e2e-tests/handlers/parameters/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/handlers/tracing/pom.xml | 2 +- powertools-e2e-tests/handlers/validation-alb-event/pom.xml | 2 +- .../handlers/validation-apigw-event/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-idempotency/powertools-idempotency-core/pom.xml | 2 +- .../powertools-idempotency-dynamodb/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-logging/powertools-logging-log4j/pom.xml | 2 +- powertools-logging/powertools-logging-logback/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- .../powertools-parameters-appconfig/pom.xml | 2 +- .../powertools-parameters-dynamodb/pom.xml | 2 +- powertools-parameters/powertools-parameters-secrets/pom.xml | 2 +- powertools-parameters/powertools-parameters-ssm/pom.xml | 2 +- powertools-parameters/powertools-parameters-tests/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 62 files changed, 69 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index b53d5aeb1..58d3901dc 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your software.amazon.lambda powertools-tracing - 2.5.0 + 2.6.0 software.amazon.lambda powertools-logging - 2.5.0 + 2.6.0 software.amazon.lambda powertools-metrics - 2.5.0 + 2.6.0 ... diff --git a/examples/pom.xml b/examples/pom.xml index 71d924c26..9ce451a33 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ software.amazon.lambda powertools-examples - 2.5.0 + 2.6.0 pom Powertools for AWS Lambda (Java) - Examples diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 75b283d3d..707b9bcfb 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ 4.0.0 software.amazon.lambda.examples - 2.5.0 + 2.6.0 powertools-examples-batch jar Powertools for AWS Lambda (Java) - Examples - Batch diff --git a/examples/powertools-examples-cloudformation/README.md b/examples/powertools-examples-cloudformation/README.md index b26007d44..35184b6d2 100644 --- a/examples/powertools-examples-cloudformation/README.md +++ b/examples/powertools-examples-cloudformation/README.md @@ -15,7 +15,7 @@ Run the following in your shell: ```bash cd infra/sam sam build -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.5.0718 +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.6.0718 ``` ### Deploy with CDK @@ -32,5 +32,5 @@ To build and deploy this application for the first time, run the following in yo cd infra/cdk mvn package cdk synth -cdk deploy -c BucketNameParam=my-unique-bucket-2.5.0718 +cdk deploy -c BucketNameParam=my-unique-bucket-2.6.0718 ``` \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md index b577855ce..01365932e 100644 --- a/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md +++ b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md @@ -40,7 +40,7 @@ sam build ## Deploy the sample application ```shell -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.5.0718 +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.6.0718 ``` This sample is based on Serverless Application Model (SAM). To deploy it, check out the instructions for getting started with SAM in [the examples directory](../../../README.md) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index edd774e74..389245bbc 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ 4.0.0 software.amazon.lambda.examples - 2.5.0 + 2.6.0 powertools-examples-cloudformation jar diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 8a5ac6601..756c082b8 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda.examples - 2.5.0 + 2.6.0 powertools-examples-core-utilities-cdk jar diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 3a07e6e6c..4d3414c63 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -4,7 +4,7 @@ 4.0.0 software.amazon.lambda.examples cdk - 2.5.0 + 2.6.0 UTF-8 2.221.1 diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle index 9b89d923a..36524f0b1 100644 --- a/examples/powertools-examples-core-utilities/gradle/build.gradle +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -29,8 +29,8 @@ dependencies { implementation 'com.amazonaws:aws-lambda-java-events:3.16.0' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2' implementation 'org.aspectj:aspectjrt:1.9.20.1' - aspect 'software.amazon.lambda:powertools-tracing:2.5.0' - aspect 'software.amazon.lambda:powertools-logging-log4j:2.5.0' - aspect 'software.amazon.lambda:powertools-metrics:2.5.0' + aspect 'software.amazon.lambda:powertools-tracing:2.6.0' + aspect 'software.amazon.lambda:powertools-logging-log4j:2.6.0' + aspect 'software.amazon.lambda:powertools-metrics:2.6.0' } diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index 85ec540a9..c3713c344 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -15,9 +15,9 @@ dependencies { implementation("com.amazonaws:aws-lambda-java-events:3.16.0") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.2") implementation("org.aspectj:aspectjrt:1.9.20.1") - aspect("software.amazon.lambda:powertools-tracing:2.5.0") - aspect("software.amazon.lambda:powertools-logging-log4j:2.5.0") - aspect("software.amazon.lambda:powertools-metrics:2.5.0") + aspect("software.amazon.lambda:powertools-tracing:2.6.0") + aspect("software.amazon.lambda:powertools-logging-log4j:2.6.0") + aspect("software.amazon.lambda:powertools-metrics:2.6.0") implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.24") } diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index 71b93be66..b879ae49d 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -4,7 +4,7 @@ Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM GraalVM software.amazon.lambda.examples - 2.5.0 + 2.6.0 powertools-examples-core-utilities-sam-graalvm jar diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index 60d6b796f..a5e3881b2 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -4,7 +4,7 @@ Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM software.amazon.lambda.examples - 2.5.0 + 2.6.0 powertools-examples-core-utilities-sam jar diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index 01e734f6f..8ef3b556d 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -4,7 +4,7 @@ Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Serverless software.amazon.lambda.examples - 2.5.0 + 2.6.0 powertools-examples-core-utilities-serverless jar diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index 4b7d19fe4..d320a1d40 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -4,7 +4,7 @@ Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Terraform software.amazon.lambda.examples - 2.5.0 + 2.6.0 powertools-examples-core-utilities-terraform jar diff --git a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml index 6ee70def3..3fa68c7e0 100644 --- a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml +++ b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.5.0 + 2.6.0 powertools-examples-idempotency-sam-graalvm jar Powertools for AWS Lambda (Java) - Examples - Idempotency GraalVM diff --git a/examples/powertools-examples-idempotency/sam/pom.xml b/examples/powertools-examples-idempotency/sam/pom.xml index d84651e15..0654867f9 100644 --- a/examples/powertools-examples-idempotency/sam/pom.xml +++ b/examples/powertools-examples-idempotency/sam/pom.xml @@ -17,7 +17,7 @@ 4.0.0 software.amazon.lambda.examples - 2.5.0 + 2.6.0 powertools-examples-idempotency jar Powertools for AWS Lambda (Java) - Examples - Idempotency diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index fe56c6b1e..5428655bd 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.5.0 + 2.6.0 powertools-examples-kafka jar Powertools for AWS Lambda (Java) - Examples - Kafka diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index e72df3086..bfebbe57d 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.5.0 + 2.6.0 powertools-examples-parameters-sam-graalvm jar Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index e2ee15f26..17a1efa79 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.5.0 + 2.6.0 powertools-examples-parameters-sam jar Powertools for AWS Lambda (Java) - Examples - Parameters diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index cdf131482..9b03e3046 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.5.0 + 2.6.0 powertools-examples-serialization-sam-graalvm jar Powertools for AWS Lambda (Java) - Examples - Serialization GraalVM diff --git a/examples/powertools-examples-serialization/sam/pom.xml b/examples/powertools-examples-serialization/sam/pom.xml index 683faf783..7120d3a91 100644 --- a/examples/powertools-examples-serialization/sam/pom.xml +++ b/examples/powertools-examples-serialization/sam/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.5.0 + 2.6.0 powertools-examples-serialization-sam jar Powertools for AWS Lambda (Java) - Examples - Serialization diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index b84fac5bb..5826e7456 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.5.0 + 2.6.0 powertools-examples-validation jar Powertools for AWS Lambda (Java) - Examples - Validation diff --git a/mkdocs.yml b/mkdocs.yml index 62e211728..da4303c38 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -127,7 +127,7 @@ extra_javascript: extra: powertools: - version: 2.5.0 + version: 2.6.0 version: provider: mike default: latest diff --git a/pom.xml b/pom.xml index bc5389841..66d15731b 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ software.amazon.lambda powertools-parent - 2.5.0 + 2.6.0 pom Powertools for AWS Lambda (Java) - Parent diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index edb849cea..ccf926d39 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda powertools-parent - 2.5.0 + 2.6.0 A suite of utilities that makes batch message processing using AWS Lambda easier. diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index f6d8ad33f..015179833 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 2.5.0 + 2.6.0 Powertools for AWS Lambda (Java) - Cloudformation diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 3583a4dc0..2610a8c4f 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 2.5.0 + 2.6.0 Powertools for AWS Lambda (Java) - Common Internal Utilities diff --git a/powertools-e2e-tests/handlers/batch/pom.xml b/powertools-e2e-tests/handlers/batch/pom.xml index 21161f9e7..a630ba09b 100644 --- a/powertools-e2e-tests/handlers/batch/pom.xml +++ b/powertools-e2e-tests/handlers/batch/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.5.0 + 2.6.0 e2e-test-handler-batch diff --git a/powertools-e2e-tests/handlers/idempotency-functional/pom.xml b/powertools-e2e-tests/handlers/idempotency-functional/pom.xml index c8893c6e9..73c8780d3 100644 --- a/powertools-e2e-tests/handlers/idempotency-functional/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency-functional/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.5.0 + 2.6.0 e2e-test-handler-idempotency-functional diff --git a/powertools-e2e-tests/handlers/idempotency-generics/pom.xml b/powertools-e2e-tests/handlers/idempotency-generics/pom.xml index 5387d4059..d89aa33e2 100644 --- a/powertools-e2e-tests/handlers/idempotency-generics/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency-generics/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.5.0 + 2.6.0 e2e-test-handler-idempotency-generics diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml index 7c0e957fe..ea84e7b26 100644 --- a/powertools-e2e-tests/handlers/idempotency/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.5.0 + 2.6.0 e2e-test-handler-idempotency diff --git a/powertools-e2e-tests/handlers/largemessage-functional/pom.xml b/powertools-e2e-tests/handlers/largemessage-functional/pom.xml index 4afb633f2..094c54841 100644 --- a/powertools-e2e-tests/handlers/largemessage-functional/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage-functional/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.5.0 + 2.6.0 e2e-test-handler-largemessage-functional diff --git a/powertools-e2e-tests/handlers/largemessage/pom.xml b/powertools-e2e-tests/handlers/largemessage/pom.xml index 469a9a48a..3004a884c 100644 --- a/powertools-e2e-tests/handlers/largemessage/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.5.0 + 2.6.0 e2e-test-handler-largemessage diff --git a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml index 1e89edd67..74a7b2999 100644 --- a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.5.0 + 2.6.0 e2e-test-handler-large-msg-idempotent diff --git a/powertools-e2e-tests/handlers/logging-functional/pom.xml b/powertools-e2e-tests/handlers/logging-functional/pom.xml index 5d774fe21..a8f79df30 100644 --- a/powertools-e2e-tests/handlers/logging-functional/pom.xml +++ b/powertools-e2e-tests/handlers/logging-functional/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.5.0 + 2.6.0 e2e-test-handler-logging-functional diff --git a/powertools-e2e-tests/handlers/logging-log4j/pom.xml b/powertools-e2e-tests/handlers/logging-log4j/pom.xml index dddc84152..0ed761274 100644 --- a/powertools-e2e-tests/handlers/logging-log4j/pom.xml +++ b/powertools-e2e-tests/handlers/logging-log4j/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.5.0 + 2.6.0 e2e-test-handler-logging-log4j diff --git a/powertools-e2e-tests/handlers/logging-logback/pom.xml b/powertools-e2e-tests/handlers/logging-logback/pom.xml index 4f3a28c72..d8523f721 100644 --- a/powertools-e2e-tests/handlers/logging-logback/pom.xml +++ b/powertools-e2e-tests/handlers/logging-logback/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.5.0 + 2.6.0 e2e-test-handler-logging-logback diff --git a/powertools-e2e-tests/handlers/metrics/pom.xml b/powertools-e2e-tests/handlers/metrics/pom.xml index 84006df9a..3b60d2aba 100644 --- a/powertools-e2e-tests/handlers/metrics/pom.xml +++ b/powertools-e2e-tests/handlers/metrics/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.5.0 + 2.6.0 e2e-test-handler-metrics diff --git a/powertools-e2e-tests/handlers/parameters/pom.xml b/powertools-e2e-tests/handlers/parameters/pom.xml index e0a58f9bd..495b51311 100644 --- a/powertools-e2e-tests/handlers/parameters/pom.xml +++ b/powertools-e2e-tests/handlers/parameters/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.5.0 + 2.6.0 e2e-test-handler-parameters diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 827feccea..2e024375c 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -4,7 +4,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.5.0 + 2.6.0 pom Handlers for End-to-End tests Fake handlers that use Powertools for AWS Lambda (Java). diff --git a/powertools-e2e-tests/handlers/tracing/pom.xml b/powertools-e2e-tests/handlers/tracing/pom.xml index 5f6943ca6..2aaae55f5 100644 --- a/powertools-e2e-tests/handlers/tracing/pom.xml +++ b/powertools-e2e-tests/handlers/tracing/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.5.0 + 2.6.0 e2e-test-handler-tracing diff --git a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml index e9e8f5c47..c966194b1 100644 --- a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.5.0 + 2.6.0 e2e-test-handler-validation-alb-event diff --git a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml index 21e80c20e..85b8f47dc 100644 --- a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.5.0 + 2.6.0 e2e-test-handler-validation-apigw-event diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 557611a93..1898e309f 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ powertools-parent software.amazon.lambda - 2.5.0 + 2.6.0 powertools-e2e-tests diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 8961701d3..7bfe38ef4 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ software.amazon.lambda powertools-parent - 2.5.0 + 2.6.0 powertools-idempotency diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index 64907072f..0e784c0f5 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -21,7 +21,7 @@ software.amazon.lambda powertools-idempotency - 2.5.0 + 2.6.0 powertools-idempotency-core diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index a55947d4b..ea562ea89 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -21,7 +21,7 @@ software.amazon.lambda powertools-idempotency - 2.5.0 + 2.6.0 powertools-idempotency-dynamodb diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index fd001f575..ac8c95f05 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -21,7 +21,7 @@ powertools-parent software.amazon.lambda - 2.5.0 + 2.6.0 powertools-kafka diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index bc7bbf03a..e29327c7d 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ software.amazon.lambda powertools-parent - 2.5.0 + 2.6.0 powertools-large-messages diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 2fa4680fb..bcc28c6c2 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -21,7 +21,7 @@ powertools-parent software.amazon.lambda - 2.5.0 + 2.6.0 Powertools for AWS Lambda (Java) - Logging diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index b4800ab54..fc47ac84d 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 2.5.0 + 2.6.0 ../../pom.xml diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 0cfbb6104..2269303f5 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -6,7 +6,7 @@ powertools-parent software.amazon.lambda - 2.5.0 + 2.6.0 ../../pom.xml diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 74b4ca7c4..ae2871b41 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 2.5.0 + 2.6.0 Powertools for AWS Lambda (Java) - Metrics diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index f1e4eae57..72d3133d3 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ powertools-parent software.amazon.lambda - 2.5.0 + 2.6.0 powertools-parameters diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index eefc46ac5..454102ede 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 2.5.0 + 2.6.0 ../../pom.xml diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index cfc8f64d3..594761bee 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 2.5.0 + 2.6.0 ../../pom.xml diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index 9d3ca0500..4b4616cb2 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 2.5.0 + 2.6.0 ../../pom.xml diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index a277337a7..0e31ed729 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 2.5.0 + 2.6.0 ../../pom.xml diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index 61fb4c3b9..b1b655fe8 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda powertools-parent - 2.5.0 + 2.6.0 ../../pom.xml diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 1ed2ee259..79b6653fa 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ powertools-parent software.amazon.lambda - 2.5.0 + 2.6.0 powertools-serialization diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index c1f1433a1..3b1e58ccd 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 2.5.0 + 2.6.0 Powertools for AWS Lambda (Java) - Tracing diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 57f8f25fb..2c119972d 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 2.5.0 + 2.6.0 Powertools for AWS Lambda (Java) - Validation From e928caedcf5fbbb0e43fbffd38b7d9a617fd3fef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Nov 2025 16:39:04 +0100 Subject: [PATCH 20/76] chore: bump com.amazonaws:aws-lambda-java-runtime-interface-client (#2272) Bumps [com.amazonaws:aws-lambda-java-runtime-interface-client](https://github.com/aws/aws-lambda-java-libs) from 2.8.6 to 2.8.7. - [Commits](https://github.com/aws/aws-lambda-java-libs/commits) --- updated-dependencies: - dependency-name: com.amazonaws:aws-lambda-java-runtime-interface-client dependency-version: 2.8.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-idempotency/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 389245bbc..e04a8dc43 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -77,7 +77,7 @@ com.amazonaws aws-lambda-java-runtime-interface-client - 2.8.6 + 2.8.7 diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index b879ae49d..cfd640aa1 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -49,7 +49,7 @@ com.amazonaws aws-lambda-java-runtime-interface-client - 2.8.6 + 2.8.7 org.apache.logging.log4j diff --git a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml index 3fa68c7e0..dff0082f1 100644 --- a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml +++ b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml @@ -43,7 +43,7 @@ com.amazonaws aws-lambda-java-runtime-interface-client - 2.8.6 + 2.8.7 diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index bfebbe57d..9c62498d5 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -49,7 +49,7 @@ com.amazonaws aws-lambda-java-runtime-interface-client - 2.8.6 + 2.8.7 org.apache.logging.log4j diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index 9b03e3046..7df5805ce 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -36,7 +36,7 @@ com.amazonaws aws-lambda-java-runtime-interface-client - 2.8.6 + 2.8.7 diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 2e024375c..95a09d1d9 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -129,7 +129,7 @@ com.amazonaws aws-lambda-java-runtime-interface-client - 2.8.6 + 2.8.7 From f08e8731aaac85014d752c58294c9e11e89b5d03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Nov 2025 16:39:08 +0100 Subject: [PATCH 21/76] chore: bump aws.sdk.version from 2.37.4 to 2.37.5 (#2271) Bumps `aws.sdk.version` from 2.37.4 to 2.37.5. Updates `software.amazon.awssdk:bom` from 2.37.4 to 2.37.5 Updates `software.amazon.awssdk:http-client-spi` from 2.37.4 to 2.37.5 Updates `software.amazon.awssdk:url-connection-client` from 2.37.2 to 2.37.5 Updates `software.amazon.awssdk:s3` from 2.37.2 to 2.37.5 Updates `software.amazon.awssdk:dynamodb` from 2.37.4 to 2.37.5 Updates `software.amazon.awssdk:lambda` from 2.37.4 to 2.37.5 Updates `software.amazon.awssdk:kinesis` from 2.37.2 to 2.37.5 Updates `software.amazon.awssdk:cloudwatch` from 2.37.4 to 2.37.5 Updates `software.amazon.awssdk:xray` from 2.37.4 to 2.37.5 Updates `software.amazon.awssdk:sqs` from 2.37.2 to 2.37.5 Updates `software.amazon.awssdk:cloudformation` from 2.37.4 to 2.37.5 Updates `software.amazon.awssdk:sts` from 2.37.4 to 2.37.5 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.37.5 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.37.5 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.37.5 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index e04a8dc43..a88339746 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ 11 1.4.0 3.16.1 - 2.37.4 + 2.37.5 1.9.20.1 diff --git a/pom.xml b/pom.xml index 66d15731b..d262fbc6b 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ 2.25.2 2.0.17 2.20.1 - 2.37.4 + 2.37.5 2.20.0 2.2.0 UTF-8 diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 95a09d1d9..599588c1d 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ 3.6.1 1.14.1 3.14.1 - 2.37.4 + 2.37.5 1.9.20.1 true From 1ed3daeba384f12ec5df3ea11d42ef038c791246 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 11:08:51 +0100 Subject: [PATCH 22/76] chore: bump aws.sdk.version from 2.37.2 to 2.37.5 (#2273) Bumps `aws.sdk.version` from 2.37.2 to 2.37.5. Updates `software.amazon.awssdk:url-connection-client` from 2.37.2 to 2.37.5 Updates `software.amazon.awssdk:sdk-core` from 2.37.2 to 2.37.5 Updates `software.amazon.awssdk:s3` from 2.37.2 to 2.37.5 Updates `software.amazon.awssdk:kinesis` from 2.37.2 to 2.37.5 Updates `software.amazon.awssdk:sqs` from 2.37.2 to 2.37.5 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.37.2 to 2.37.5 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sdk-core dependency-version: 2.37.5 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.37.5 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.37.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-version: 2.37.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 707b9bcfb..103a8a4b4 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ 11 11 1.9.20.1 - 2.37.2 + 2.37.5 From 6b032bbaaf019283931ec9fc83d147e9b271cd65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 11:09:06 +0100 Subject: [PATCH 23/76] chore: bump software.amazon.awscdk:aws-cdk-lib from 2.221.1 to 2.222.0 (#2274) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.221.1 to 2.222.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.alpha.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.221.1...v2.222.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-version: 2.222.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 4d3414c63..9348d52d9 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -7,7 +7,7 @@ 2.6.0 UTF-8 - 2.221.1 + 2.222.0 [10.0.0,11.0.0) 5.14.1 diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 1898e309f..09bfe8271 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ 11 11 10.4.2 - 2.221.1 + 2.222.0 From e39cc6041417eb13896bc2a0b41a3c4eafd84b11 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 11:09:18 +0100 Subject: [PATCH 24/76] chore: bump sam/build-java21 (#2275) Bumps sam/build-java21 from `72709a0` to `51709ae`. --- updated-dependencies: - dependency-name: sam/build-java21 dependency-version: 51709ae612478654f833998a3455519d0524157230757cf6327e402213811e38 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/src/test/resources/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/src/test/resources/docker/Dockerfile b/powertools-e2e-tests/src/test/resources/docker/Dockerfile index 8d26b4770..1abb53643 100644 --- a/powertools-e2e-tests/src/test/resources/docker/Dockerfile +++ b/powertools-e2e-tests/src/test/resources/docker/Dockerfile @@ -1,5 +1,5 @@ # Use the official AWS SAM base image for Java 21 -FROM public.ecr.aws/sam/build-java21@sha256:72709a010ebfc993fb402c1bce599a0b754110b873d1a58e60c52136e8c8f3f1 +FROM public.ecr.aws/sam/build-java21@sha256:51709ae612478654f833998a3455519d0524157230757cf6327e402213811e38 # Install GraalVM dependencies RUN curl -4 -L https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz From 691aeea73b55126072065237b8bed41b58d497ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 11:01:37 +0100 Subject: [PATCH 25/76] chore: bump aws.sdk.version from 2.37.5 to 2.38.2 (#2276) Bumps `aws.sdk.version` from 2.37.5 to 2.38.2. Updates `software.amazon.awssdk:url-connection-client` from 2.37.5 to 2.38.2 Updates `software.amazon.awssdk:sdk-core` from 2.37.5 to 2.38.2 Updates `software.amazon.awssdk:s3` from 2.37.5 to 2.38.2 Updates `software.amazon.awssdk:kinesis` from 2.37.5 to 2.38.2 Updates `software.amazon.awssdk:sqs` from 2.37.5 to 2.38.2 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.37.5 to 2.38.2 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.38.2 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sdk-core dependency-version: 2.38.2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.38.2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.38.2 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.38.2 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-version: 2.38.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 103a8a4b4..518cdbdd4 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ 11 11 1.9.20.1 - 2.37.5 + 2.38.2 From 7c6d6ea17aad11987a276aa06ac4fc63c38e58f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 11:01:45 +0100 Subject: [PATCH 26/76] chore: bump software.constructs:constructs from 10.4.2 to 10.4.3 (#2277) Bumps [software.constructs:constructs](https://github.com/aws/constructs) from 10.4.2 to 10.4.3. - [Release notes](https://github.com/aws/constructs/releases) - [Commits](https://github.com/aws/constructs/compare/v10.4.2...v10.4.3) --- updated-dependencies: - dependency-name: software.constructs:constructs dependency-version: 10.4.3 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 09bfe8271..ddf1a360a 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -30,7 +30,7 @@ 11 11 - 10.4.2 + 10.4.3 2.222.0 From 598f004a0f56fad3e0e4f70594ff28fe1de4f616 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 09:56:26 +0100 Subject: [PATCH 27/76] chore: bump aws.sdk.version from 2.37.5 to 2.38.3 (#2278) Bumps `aws.sdk.version` from 2.37.5 to 2.38.3. Updates `software.amazon.awssdk:bom` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:http-client-spi` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:url-connection-client` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:s3` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:dynamodb` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:lambda` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:kinesis` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:cloudwatch` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:xray` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:sqs` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:cloudformation` from 2.37.5 to 2.38.3 Updates `software.amazon.awssdk:sts` from 2.37.5 to 2.38.3 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.38.3 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.38.3 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.38.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.38.3 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.38.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.38.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.38.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.38.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-version: 2.38.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.38.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.38.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-version: 2.38.3 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index a88339746..392d08b4b 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ 11 1.4.0 3.16.1 - 2.37.5 + 2.38.3 1.9.20.1 diff --git a/pom.xml b/pom.xml index d262fbc6b..4d5ff7248 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ 2.25.2 2.0.17 2.20.1 - 2.37.5 + 2.38.3 2.20.0 2.2.0 UTF-8 diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 599588c1d..bf2e78f8c 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ 3.6.1 1.14.1 3.14.1 - 2.37.5 + 2.38.3 1.9.20.1 true From 7cdac1d58edf24648bd0e9875f01748e43391f0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 09:56:40 +0100 Subject: [PATCH 28/76] chore: bump software.amazon.awscdk:aws-cdk-lib from 2.222.0 to 2.223.0 (#2279) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.222.0 to 2.223.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.alpha.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.222.0...v2.223.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-version: 2.223.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 9348d52d9..4ca38877d 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -7,7 +7,7 @@ 2.6.0 UTF-8 - 2.222.0 + 2.223.0 [10.0.0,11.0.0) 5.14.1 diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index ddf1a360a..860593819 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ 11 11 10.4.3 - 2.222.0 + 2.223.0 From d550de02ffe2ef6901db9cb35b8338f61097bfb4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 09:57:42 +0100 Subject: [PATCH 29/76] chore: bump squidfunk/mkdocs-material in /docs (#2280) Bumps squidfunk/mkdocs-material from `58dee36` to `980e11f`. --- updated-dependencies: - dependency-name: squidfunk/mkdocs-material dependency-version: 980e11fed03b8e7851e579be9f34b1210f516c9f0b4da1a1457f21a460bd6628 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Dockerfile b/docs/Dockerfile index 88d6ca284..56c4c19ea 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,4 +1,4 @@ -FROM squidfunk/mkdocs-material@sha256:58dee36ad85b0ae4836522ee6d3f0150d828bca9a1f7d3bfbf430bca771c1441 +FROM squidfunk/mkdocs-material@sha256:980e11fed03b8e7851e579be9f34b1210f516c9f0b4da1a1457f21a460bd6628 COPY requirements.txt /tmp/ RUN pip install --require-hashes -r /tmp/requirements.txt From addad65980bd89fd992f6fe91de7b70af68b5135 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 09:57:55 +0100 Subject: [PATCH 30/76] chore: bump actions/dependency-review-action from 4.8.1 to 4.8.2 (#2281) Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 4.8.1 to 4.8.2. - [Release notes](https://github.com/actions/dependency-review-action/releases) - [Commits](https://github.com/actions/dependency-review-action/compare/40c09b7dc99638e5ddb0bfd91c1673effc064d8a...3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261) --- updated-dependencies: - dependency-name: actions/dependency-review-action dependency-version: 4.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-dependencies-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-dependencies-check.yml b/.github/workflows/security-dependencies-check.yml index 692acd64d..c6e06950f 100644 --- a/.github/workflows/security-dependencies-check.yml +++ b/.github/workflows/security-dependencies-check.yml @@ -26,6 +26,6 @@ jobs: - name: Checkout Repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Verify Contents - uses: actions/dependency-review-action@40c09b7dc99638e5ddb0bfd91c1673effc064d8a # v4.8.1 + uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2 with: config-file: './.github/dependency-review-config.yml' From f9f227cf5a289da7c701057c9228296811ebfb3a Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Thu, 13 Nov 2025 10:19:29 +0100 Subject: [PATCH 31/76] docs: Document new functional API (#2282) * Initial version of documentation revamp adding functional API and usage patterns. * Update tracing docs highlighting functional API. * Update metrics docs highlighting functional API. * Update parameters docs highlithing functional approach. * Update validation docs highlithing functional API. * Update README.md Co-authored-by: Stefano Vozza * Update docs/core/logging.md Co-authored-by: Stefano Vozza * Update docs/core/logging.md Co-authored-by: Stefano Vozza * Update docs/core/logging.md Co-authored-by: Stefano Vozza * Update docs/core/logging.md Co-authored-by: Stefano Vozza * Update docs/core/metrics.md Co-authored-by: Stefano Vozza * Update docs/core/tracing.md Co-authored-by: Stefano Vozza * Update docs/index.md Co-authored-by: Stefano Vozza * Update docs/core/logging.md Co-authored-by: Stefano Vozza * Update docs/utilities/idempotency.md Co-authored-by: Stefano Vozza * Update docs/utilities/idempotency.md Co-authored-by: Stefano Vozza --------- Co-authored-by: Stefano Vozza --- README.md | 14 +- docs/FAQs.md | 4 + docs/core/logging.md | 264 ++++++++++++++++--- docs/core/metrics.md | 82 +++--- docs/core/tracing.md | 142 +++++----- docs/index.md | 39 ++- docs/processes/maintainers.md | 2 +- docs/usage-patterns.md | 183 +++++++++++++ docs/utilities/idempotency.md | 427 +++++++++++++++++++++++++------ docs/utilities/large_messages.md | 271 +++++++++++++++----- docs/utilities/parameters.md | 44 ++-- docs/utilities/validation.md | 23 +- mkdocs.yml | 4 +- 13 files changed, 1167 insertions(+), 332 deletions(-) create mode 100644 docs/usage-patterns.md diff --git a/README.md b/README.md index 58d3901dc..e771db05c 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your software.amazon.lambda - powertools-logging + powertools-logging-log4j 2.6.0 @@ -116,6 +116,7 @@ Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lam aspect 'software.amazon.lambda:powertools-logging:{{ powertools.version }}' aspect 'software.amazon.lambda:powertools-tracing:{{ powertools.version }}' aspect 'software.amazon.lambda:powertools-metrics:{{ powertools.version }}' + implementation 'software.amazon.lambda:powertools-logging-log4j:{{ powertools.version }}' implementation "org.aspectj:aspectjrt:1.9.22" } @@ -126,10 +127,10 @@ Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lam ### Java Compatibility -Powertools for AWS Lambda (Java) supports all Java version from 11 up to 21 as well as the -[corresponding Lambda runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). +Powertools for AWS Lambda (Java) supports all Java versions from 11 to 25 in line with the [corresponding Lambda runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). + For the modules that provide annotations, Powertools for AWS Lambda (Java) leverages the **aspectj** library. -You may need to add the good version of `aspectjrt` to your dependencies based on the JDK used for building your function: +You may need to add the appropriate version of `aspectjrt` to your dependencies based on the JDK used for building your function: ```xml @@ -142,12 +143,13 @@ You may need to add the good version of `aspectjrt` to your dependencies based o

JDK - aspectj dependency matrix +Use the following [dependency matrix](https://github.com/eclipse-aspectj/aspectj/blob/master/docs/release/JavaVersionCompatibility.adoc) to understand which AspectJ version to use based on your JDK version: + | JDK version | aspectj version | |-------------|------------------------| | `11-17` | `1.9.20.1` (or higher) | | `21` | `1.9.21` (or higher) | - -More info [here](https://github.com/aws-powertools/powertools-lambda-java/pull/1519/files#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5R191). +| `25` | `1.9.25` (or higher) |
diff --git a/docs/FAQs.md b/docs/FAQs.md index 75f699c91..cea4b774f 100644 --- a/docs/FAQs.md +++ b/docs/FAQs.md @@ -7,6 +7,8 @@ description: Frequently Asked Questions Many utilities in this library use `aspectj-maven-plugin` to compile-time weave (CTW) aspects into the project. In case you want to use `Lombok` or other compile-time preprocessor for your project, it is required to change `aspectj-maven-plugin` configuration to enable in-place weaving feature. Otherwise the plugin will ignore changes introduced by `Lombok` and will use `.java` files as a source. +Alternatively, you can use the [functional approach](./usage-patterns.md#functional-approach) which does not require AspectJ configuration. + To enable in-place weaving feature you need to use following `aspectj-maven-plugin` configuration: ```xml hl_lines="2-6" @@ -31,6 +33,8 @@ To enable in-place weaving feature you need to use following `aspectj-maven-plug Many utilities use `aspectj-maven-plugin` to compile-time weave (CTW) aspects into the project. When using it with Kotlin projects, it is required to `forceAjcCompile`. No explicit configuration should be required for gradle projects. +Alternatively, you can use the [functional approach](./usage-patterns.md#functional-approach) which does not require AspectJ configuration. + To enable `forceAjcCompile` you need to use following `aspectj-maven-plugin` configuration: ```xml hl_lines="2" diff --git a/docs/core/logging.md b/docs/core/logging.md index db01a3ec0..8358087d2 100644 --- a/docs/core/logging.md +++ b/docs/core/logging.md @@ -23,13 +23,12 @@ Logging provides an opinionated logger with output structured as JSON. You can find complete examples in the [project repository](https://github.com/aws-powertools/powertools-lambda-java/tree/v2/examples/powertools-examples-core-utilities){target="_blank"}. ### Installation -Depending on preference, you must choose to use either _log4j2_ or _logback_ as your log provider. In both cases you need to configure _aspectj_ -to weave the code and make sure the annotation is processed. +Depending on preference, you must choose to use either _log4j2_ or _logback_ as your log provider. If you use the AspectJ annotation approach, you must configure _aspectj_ to weave the code and make sure the annotation is processed. If you prefer the [functional approach](../usage-patterns.md#functional-approach), AspectJ configuration is not required. #### Maven === "log4j2" - ```xml hl_lines="3-7 24-27" + ```xml hl_lines="3-12 30-33" ... @@ -37,10 +36,16 @@ to weave the code and make sure the annotation is processed. powertools-logging-log4j {{ powertools.version }} + + software.amazon.lambda + powertools-logging + {{ powertools.version }} + ... ... + ... @@ -82,7 +87,7 @@ to weave the code and make sure the annotation is processed. === "logback" - ```xml hl_lines="3-7 24-27" + ```xml hl_lines="3-12 30-33" ... @@ -90,10 +95,16 @@ to weave the code and make sure the annotation is processed. powertools-logging-logback {{ powertools.version }} + + software.amazon.lambda + powertools-logging + {{ powertools.version }} + ... ... + ... @@ -137,10 +148,10 @@ to weave the code and make sure the annotation is processed. === "log4j2" - ```groovy hl_lines="3 11" + ```groovy hl_lines="3 11-12" plugins { id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' // Not needed when using the functional approach } repositories { @@ -148,7 +159,8 @@ to weave the code and make sure the annotation is processed. } dependencies { - aspect 'software.amazon.lambda:powertools-logging-log4j:{{ powertools.version }}' + aspect 'software.amazon.lambda:powertools-logging:{{ powertools.version }}' // Not needed when using the functional approach + implementation 'software.amazon.lambda:powertools-logging-log4j:{{ powertools.version }}' } sourceCompatibility = 11 @@ -157,10 +169,10 @@ to weave the code and make sure the annotation is processed. === "logback" - ```groovy hl_lines="3 11" + ```groovy hl_lines="3 11-12" plugins { id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' // Not needed when using the functional approach } repositories { @@ -168,7 +180,8 @@ to weave the code and make sure the annotation is processed. } dependencies { - aspect 'software.amazon.lambda:powertools-logging-logback:{{ powertools.version }}' + aspect 'software.amazon.lambda:powertools-logging:{{ powertools.version }}' // Not needed when using the functional approach + implementation 'software.amazon.lambda:powertools-logging-logback:{{ powertools.version }}' } sourceCompatibility = 11 @@ -317,9 +330,9 @@ If you set `POWERTOOLS_LOG_LEVEL` lower than ALC, we will emit a warning informi ## Basic Usage -To use Lambda Powertools for AWS Lambda Logging, use the `@Logging` annotation in your code and the standard _SLF4J_ logger: +You can use Powertools for AWS Lambda Logging with either the `@Logging` annotation or the functional API: -=== "PaymentFunction.java" +=== "@Logging annotation" ```java hl_lines="8 10 12 14" import org.slf4j.Logger; @@ -341,6 +354,30 @@ To use Lambda Powertools for AWS Lambda Logging, use the `@Logging` annotation i } ``` +=== "Functional API" + + ```java hl_lines="8 11 12 14 17" + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.logging.PowertoolsLogging; + // ... other imports + + public class PaymentFunction implements RequestHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, () -> { + LOGGER.info("Collecting payment"); + // ... + LOGGER.debug("order={}, amount={}", order.getId(), order.getAmount()); + // ... + return new APIGatewayProxyResponseEvent().withStatusCode(200); + }); + } + } + ``` + ## Standard structured keys Your logs will always include the following keys in your structured logging: @@ -376,11 +413,10 @@ The following keys will also be added to all your structured logs (unless [confi #### Logging a correlation ID -You can set a correlation ID using the `correlationIdPath` attribute of the `@Logging`annotation, -by passing a [JMESPath expression](https://jmespath.org/tutorial.html){target="_blank"}, +You can set a correlation ID using the `correlationIdPath` parameter by passing a [JMESPath expression](https://jmespath.org/tutorial.html){target="_blank"}, including our custom [JMESPath Functions](../utilities/serialization.md#built-in-functions). -=== "AppCorrelationIdPath.java" +=== "@Logging annotation" ```java hl_lines="5" public class AppCorrelationIdPath implements RequestHandler { @@ -395,6 +431,24 @@ including our custom [JMESPath Functions](../utilities/serialization.md#built-in } } ``` + +=== "Functional API" + + ```java hl_lines="6" + public class AppCorrelationIdPath implements RequestHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppCorrelationIdPath.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, "headers.my_request_id_header", input, () -> { + // ... + LOGGER.info("Collecting payment"); + // ... + return new APIGatewayProxyResponseEvent().withStatusCode(200); + }); + } + } + ``` === "Example HTTP Event" ```json hl_lines="3" @@ -422,7 +476,7 @@ including our custom [JMESPath Functions](../utilities/serialization.md#built-in To ease routine tasks like extracting correlation ID from popular event sources, we provide [built-in JMESPath expressions](#built-in-correlation-id-expressions). -=== "AppCorrelationId.java" +=== "@Logging annotation" ```java hl_lines="1 7" import software.amazon.lambda.powertools.logging.CorrelationIdPaths; @@ -440,6 +494,26 @@ we provide [built-in JMESPath expressions](#built-in-correlation-id-expressions) } ``` +=== "Functional API" + + ```java hl_lines="1 8" + import software.amazon.lambda.powertools.logging.CorrelationIdPaths; + + public class AppCorrelationId implements RequestHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppCorrelationId.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, CorrelationIdPaths.API_GATEWAY_REST, input, () -> { + // ... + LOGGER.info("Collecting payment"); + // ... + return new APIGatewayProxyResponseEvent().withStatusCode(200); + }); + } + } + ``` + === "Example Event" ```json hl_lines="3" @@ -668,10 +742,9 @@ You can remove additional keys added with the MDC using `MDC.remove("key")`. #### Clearing state Logger is commonly initialized in the global scope. Due to [Lambda Execution Context reuse](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-context.html){target="_blank"}, -this means that custom keys, added with the MDC can be persisted across invocations. If you want all custom keys to be deleted, you can use -`clearState=true` attribute on the `@Logging` annotation. +this means that custom keys, added with the MDC can be persisted across invocations. You can clear state using `clearState=true` on the `@Logging` annotation, or use the functional API which handles cleanup automatically. -=== "CreditCardFunction.java" +=== "@Logging annotation" ```java hl_lines="5 8" public class CreditCardFunction implements RequestHandler { @@ -716,15 +789,18 @@ this means that custom keys, added with the MDC can be persisted across invocati `clearState` is based on `MDC.clear()`. State clearing is automatically done at the end of the execution of the handler if set to `true`. +???+ tip + When using the functional API with `PowertoolsLogging.withLogging()`, state is automatically cleared at the end of execution, so you don't need to manage it manually. + ## Logging incoming event -When debugging in non-production environments, you can instruct the `@Logging` annotation to log the incoming event with `logEvent` param or via `POWERTOOLS_LOGGER_LOG_EVENT` env var. +When debugging in non-production environments, you can log the incoming event using the `@Logging` annotation with the `logEvent` parameter, via the `POWERTOOLS_LOGGER_LOG_EVENT` environment variable, or manually with the functional API. ???+ warning - This is disabled by default to prevent sensitive info being logged + This is disabled by default to prevent sensitive info being logged. -=== "AppLogEvent.java" +=== "@Logging annotation" ```java hl_lines="5" public class AppLogEvent implements RequestHandler { @@ -738,17 +814,36 @@ When debugging in non-production environments, you can instruct the `@Logging` a } ``` +=== "Functional API" + + ```java hl_lines="1 9" + import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; + + public class AppLogEvent implements RequestHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppLogEvent.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, () -> { + LOGGER.info("Handler Event", entry("event", input)); + // ... + return new APIGatewayProxyResponseEvent().withStatusCode(200); + }); + } + } + ``` + ???+ note - If you use this on a RequestStreamHandler, the SDK must duplicate input streams in order to log them. + If you use this on a RequestStreamHandler, the SDK must duplicate input streams in order to log them when used together with the `@Logging` annotation. ## Logging handler response -When debugging in non-production environments, you can instruct the `@Logging` annotation to log the response with `logResponse` param or via `POWERTOOLS_LOGGER_LOG_RESPONSE` env var. +When debugging in non-production environments, you can log the response using the `@Logging` annotation with the `logResponse` parameter, via the `POWERTOOLS_LOGGER_LOG_RESPONSE` environment variable, or manually with the functional API. ???+ warning - This is disabled by default to prevent sensitive info being logged + This is disabled by default to prevent sensitive info being logged. -=== "AppLogResponse.java" +=== "@Logging annotation" ```java hl_lines="5" public class AppLogResponse implements RequestHandler { @@ -762,18 +857,41 @@ When debugging in non-production environments, you can instruct the `@Logging` a } ``` +=== "Functional API" + + ```java hl_lines="1 11" + import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; + + public class AppLogResponse implements RequestHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppLogResponse.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, () -> { + // ... + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent().withStatusCode(200); + LOGGER.info("Handler Response", entry("response", response)); + return response; + }); + } + } + ``` + ???+ note - If you use this on a RequestStreamHandler, Powertools must duplicate output streams in order to log them. + If you use this on a RequestStreamHandler, Powertools must duplicate output streams in order to log them when used together with the `@Logging` annotation. ## Logging handler uncaught exception By default, AWS Lambda logs any uncaught exception that might happen in the handler. However, this log is not structured -and does not contain any additional context. You can instruct the `@Logging` annotation to log this kind of exception +and does not contain any additional context. When using the `@Logging` annotation, you can enable structured exception logging with `logError` param or via `POWERTOOLS_LOGGER_LOG_ERROR` env var. ???+ warning - This is disabled by default to prevent double logging + This is disabled by default to prevent double logging. -=== "AppLogResponse.java" +???+ note + This feature is only available when using the `@Logging` annotation. When using the functional API, you must catch and log exceptions manually using try-catch blocks. + +=== "@Logging annotation" ```java hl_lines="5" public class AppLogError implements RequestHandler { @@ -787,6 +905,29 @@ with `logError` param or via `POWERTOOLS_LOGGER_LOG_ERROR` env var. } ``` +=== "Functional API" + + ```java hl_lines="1 9 12-13" + import org.slf4j.MarkerFactory; + + public class AppLogError implements RequestHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppLogError.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, () -> { + try { + // ... + return new APIGatewayProxyResponseEvent().withStatusCode(200); + } catch (Exception e) { + LOGGER.error(MarkerFactory.getMarker("FATAL"), "Exception in Lambda Handler", e); + throw e; + } + }); + } + } + ``` + ## Advanced ### Buffering logs @@ -1050,7 +1191,10 @@ You can manually control the log buffer using the `PowertoolsLogging` utility cl Use the `@Logging` annotation to automatically flush buffered logs when an uncaught exception is raised in your Lambda function. This is enabled by default (`flushBufferOnUncaughtError = true`), but you can explicitly configure it if needed. -=== "PaymentFunction.java" +???+ warning + This feature is only available when using the `@Logging` annotation. When using the functional API, you must manually flush the buffer in exception handlers. + +=== "@Logging annotation" ```java hl_lines="5 11" public class PaymentFunction implements RequestHandler { @@ -1068,6 +1212,30 @@ Use the `@Logging` annotation to automatically flush buffered logs when an uncau } ``` +=== "Functional API" + + ```java hl_lines="14" + import software.amazon.lambda.powertools.logging.PowertoolsLogging; + + public class PaymentFunction implements RequestHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, () -> { + try { + LOGGER.debug("a debug log"); // this is buffered + // do stuff + throw new RuntimeException("Something went wrong"); + } catch (Exception e) { + PowertoolsLogging.flushBuffer(); // Manually flush buffered logs + throw e; + } + }); + } + } + ``` + #### Buffering workflows ##### Manual flush @@ -1161,13 +1329,13 @@ sequenceDiagram ## Sampling debug logs -You can dynamically set a percentage of your logs to`DEBUG` level to be included in the logger output, regardless of configured log leve, using the`POWERTOOLS_LOGGER_SAMPLE_RATE` environment variable or -via `samplingRate` attribute on the `@Logging` annotation. +You can dynamically set a percentage of your logs to`DEBUG` level to be included in the logger output, regardless of configured log level, using the`POWERTOOLS_LOGGER_SAMPLE_RATE` environment variable, +via the `samplingRate` attribute on the `@Logging` annotation, or as a parameter in the functional API. !!! info - Configuration on environment variable is given precedence over sampling rate configuration on annotation, provided it's in valid value range. + Configuration via environment variable is given precedence over sampling rate configuration, provided it's in valid value range. -=== "Sampling via annotation attribute" +=== "@Logging annotation" ```java hl_lines="5" public class App implements RequestHandler { @@ -1182,6 +1350,23 @@ via `samplingRate` attribute on the `@Logging` annotation. } ``` +=== "Functional API" + + ```java hl_lines="6" + public class App implements RequestHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, 0.5, () -> { + // will eventually be logged based on the sampling rate + LOGGER.debug("Handle payment"); + return new APIGatewayProxyResponseEvent().withStatusCode(200); + }); + } + } + ``` + === "Sampling via environment variable" ```yaml hl_lines="8" @@ -1198,7 +1383,7 @@ via `samplingRate` attribute on the `@Logging` annotation. ## Built-in Correlation ID expressions -You can use any of the following built-in JMESPath expressions as part of `@Logging(correlationIdPath = ...)`: +You can use any of the following built-in JMESPath expressions with the `@Logging` annotation or the functional API: ???+ note "Note: Any object key named with `-` must be escaped" For example, **`request.headers."x-amzn-trace-id"`**. @@ -1237,8 +1422,7 @@ The `JsonTemplateLayout` is automatically configured with the provided template: "field": "name" }, "message": { - "$resolver": "powertools", - "field": "message" + "$resolver": "message" }, "error": { "message": { @@ -1299,6 +1483,10 @@ The `JsonTemplateLayout` is automatically configured with the provided template: "$resolver": "powertools", "field": "xray_trace_id" }, + "correlation_id": { + "$resolver": "powertools", + "field": "correlation_id" + }, "": { "$resolver": "powertools" } diff --git a/docs/core/metrics.md b/docs/core/metrics.md index 71c56bb8b..e7f7bd87f 100644 --- a/docs/core/metrics.md +++ b/docs/core/metrics.md @@ -48,6 +48,7 @@ Visit the AWS documentation for a complete explanation for [Amazon CloudWatch co ... + ... @@ -89,10 +90,10 @@ Visit the AWS documentation for a complete explanation for [Amazon CloudWatch co === "Gradle" - ```groovy hl_lines="3 11" + ```groovy hl_lines="3 11 12" plugins { id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' // Not needed when using the functional approach } repositories { @@ -100,7 +101,8 @@ Visit the AWS documentation for a complete explanation for [Amazon CloudWatch co } dependencies { - aspect 'software.amazon.lambda:powertools-metrics:{{ powertools.version }}' + aspect 'software.amazon.lambda:powertools-metrics:{{ powertools.version }}' // Not needed when using the functional approach + implementation 'software.amazon.lambda:powertools-metrics:{{ powertools.version }}' // Use this instead of 'aspect' when using the functional approach } sourceCompatibility = 11 @@ -127,27 +129,12 @@ Metrics has three global settings that will be used across all metrics emitted. The `Metrics` Singleton can be configured by three different interfaces. The following order of precedence applies: 1. `@FlushMetrics` annotation -2. `MetricsBuilder` using Builder pattern (see [Advanced section](#usage-without-metrics-annotation)) +2. `MetricsBuilder` using Builder pattern (see [Advanced section](#usage-without-flushmetrics-annotation)) 3. Environment variables (recommended) For most use-cases, we recommend using Environment variables and only overwrite settings in code where needed using either the `@FlushMetrics` annotation or `MetricsBuilder` if the annotation cannot be used. -=== "template.yaml" - - ```yaml hl_lines="9 10" - Resources: - HelloWorldFunction: - Type: AWS::Serverless::Function - Properties: - ... - Runtime: java11 - Environment: - Variables: - POWERTOOLS_SERVICE_NAME: payment - POWERTOOLS_METRICS_NAMESPACE: ServerlessAirline - ``` - -=== "MetricsEnabledHandler.java" +=== "@FlushMetrics annotation" ```java hl_lines="9" import software.amazon.lambda.powertools.metrics.FlushMetrics; @@ -165,9 +152,45 @@ For most use-cases, we recommend using Environment variables and only overwrite } ``` -`Metrics` is implemented as a Singleton to keep track of your aggregate metrics in memory and make them accessible anywhere in your code. To guarantee that metrics are flushed properly the `@FlushMetrics` annotation must be added on the lambda handler. +=== "MetricsBuilder" + + ```java hl_lines="7-8" + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.MetricsBuilder; + + public class MetricsEnabledHandler implements RequestHandler { + + private static final Metrics metrics = MetricsBuilder.builder() + .withNamespace("ServerlessAirline") + .withService("payment") + .build(); + + @Override + public Object handleRequest(Object input, Context context) { + // ... + metrics.flush(); + } + } + ``` + +=== "Environment variables" + + ```yaml hl_lines="9 10" + Resources: + HelloWorldFunction: + Type: AWS::Serverless::Function + Properties: + ... + Runtime: java11 + Environment: + Variables: + POWERTOOLS_SERVICE_NAME: payment + POWERTOOLS_METRICS_NAMESPACE: ServerlessAirline + ``` + +`Metrics` is implemented as a Singleton to keep track of your aggregate metrics in memory and make them accessible anywhere in your code. The `@FlushMetrics` annotation automatically flushes metrics at the end of the Lambda handler execution. Alternatively, you can use the functional approach and manually flush metrics using `metrics.flush()`. -!!!info "You can use the Metrics utility without the `@FlushMetrics` annotation and flush manually. Read more in the [advanced section below](#usage-without-metrics-annotation)." +!!!info "Read more about the functional approach in the [advanced section below](#usage-without-flushmetrics-annotation)." ## Creating metrics @@ -381,7 +404,7 @@ You can use `addMetadata` for advanced use cases, where you want to add metadata This will not be available during metrics visualization, use Dimensions for this purpose. !!! info - Adding metadata with a key that is the same as an existing metric will be ignored + Adding metadata with a key that is the same as an existing metric will be ignored. === "App.java" @@ -468,7 +491,7 @@ You can create metrics with different configurations e.g. different namespace an === "App.java" - ```java hl_lines="12-18" + ```java hl_lines="12-22" import software.amazon.lambda.powertools.metrics.Metrics; import software.amazon.lambda.powertools.metrics.MetricsFactory; import software.amazon.lambda.powertools.metrics.model.DimensionSet; @@ -504,22 +527,22 @@ You can create metrics with different configurations e.g. different namespace an ### Usage without `@FlushMetrics` annotation -The `Metrics` Singleton provides all configuration options via `MetricsBuilder` in addition to the `@FlushMetrics` annotation. This can be useful if work in an environment or framework that does not leverage the vanilla Lambda `handleRequest` method. +You can use the **functional API** approach (see [usage patterns](../usage-patterns.md#functional-approach)) to work with Metrics without the `@FlushMetrics` annotation. The `Metrics` Singleton provides all configuration options via `MetricsBuilder`. This approach eliminates the AspectJ runtime dependency and is useful if you work in an environment or with a framework that does not leverage the vanilla Lambda `handleRequest` method. !!!info "The environment variables for Service and Namespace configuration still apply but can be overwritten with `MetricsBuilder` if needed." -The following example shows how to configure a custom `Metrics` Singleton using the Builder pattern. Note that it is necessary to manually flush metrics now. +The following example shows how to configure a custom `Metrics` Singleton using the Builder pattern. With the functional approach, you must manually flush metrics using `metrics.flush()`. === "App.java" - ```java hl_lines="7-12 19 23" + ```java hl_lines="7-12 19 24" import software.amazon.lambda.powertools.metrics.Metrics; import software.amazon.lambda.powertools.metrics.MetricsBuilder; import software.amazon.lambda.powertools.metrics.model.DimensionSet; import software.amazon.lambda.powertools.metrics.model.MetricUnit; public class App implements RequestHandler { - // Create and configure a Metrics singleton without annotation + // Create and configure a Metrics singleton using the functional approach private static final Metrics metrics = MetricsBuilder.builder() .withNamespace("ServerlessAirline") .withRaiseOnEmptyMetrics(true) @@ -533,8 +556,9 @@ The following example shows how to configure a custom `Metrics` Singleton using // Dimensions are also optional. metrics.captureColdStartMetric(context, DimensionSet.of("FunctionName", "MyFunction", "Service", "payment")); - // Add metrics to the custom metrics singleton + // Add metrics metrics.addMetric("CustomMetric", 1, MetricUnit.COUNT); + // Manually flush metrics metrics.flush(); } } diff --git a/docs/core/tracing.md b/docs/core/tracing.md index 8129d45ba..95fbe6d06 100644 --- a/docs/core/tracing.md +++ b/docs/core/tracing.md @@ -20,7 +20,7 @@ a provides functionality to reduce the overhead of performing common tracing tas === "Maven" - ```xml hl_lines="3-7 16 18 24-27" + ```xml hl_lines="3-7 25-28" ... @@ -32,6 +32,7 @@ a provides functionality to reduce the overhead of performing common tracing tas ... + ... @@ -73,10 +74,10 @@ a provides functionality to reduce the overhead of performing common tracing tas === "Gradle" - ```groovy hl_lines="3 11" + ```groovy hl_lines="3 11 12" plugins { id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' // Not needed when using the functional approach } repositories { @@ -84,11 +85,12 @@ a provides functionality to reduce the overhead of performing common tracing tas } dependencies { - aspect 'software.amazon.lambda:powertools-tracing:{{ powertools.version }}' + aspect 'software.amazon.lambda:powertools-tracing:{{ powertools.version }}' // Not needed when using the functional approach + implementation 'software.amazon.lambda:powertools-tracing:{{ powertools.version }}' // Use this instead of 'aspect' when using the functional approach } - sourceCompatibility = 11 - targetCompatibility = 11 + sourceCompatibility = 11 // or higher + targetCompatibility = 11 // or higher ``` ## Initialization @@ -118,11 +120,13 @@ The Powertools for AWS Lambda (Java) service name is used as the X-Ray namespace ### Lambda handler -To enable Powertools for AWS Lambda (Java) tracing to your function add the `@Tracing` annotation to your `handleRequest` method or on -any method will capture the method as a separate subsegment automatically. You can optionally choose to customize -segment name that appears in traces. +You can enable tracing using either the `@Tracing` annotation or the functional API. -=== "Tracing annotation" +**With the `@Tracing` annotation**, add it to your `handleRequest` method or any method to capture it as a separate subsegment automatically. You can optionally customize the segment name that appears in traces. + +**With the functional API**, use `TracingUtils.withSubsegment()` to manually create subsegments without AspectJ configuration. + +=== "@Tracing annotation" ```java hl_lines="3 10 15" public class App implements RequestHandler { @@ -146,6 +150,25 @@ segment name that appears in traces. } ``` +=== "Functional API" + + ```java hl_lines="1 6 7 8 10 11 12" + import software.amazon.lambda.powertools.tracing.TracingUtils; + + public class App implements RequestHandler { + + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + TracingUtils.withSubsegment("businessLogic1", subsegment -> { + // Business logic 1 + }); + + TracingUtils.withSubsegment("businessLogic2", subsegment -> { + // Business logic 2 + }); + } + } + ``` + === "Custom Segment names" ```java hl_lines="3" @@ -157,22 +180,25 @@ segment name that appears in traces. } ``` -When using this `@Tracing` annotation, Utility performs these additional tasks to ease operations: +When using the `@Tracing` annotation, the utility performs these additional tasks to ease operations: * Creates a `ColdStart` annotation to easily filter traces that have had an initialization overhead. * Creates a `Service` annotation if service parameter or `POWERTOOLS_SERVICE_NAME` is set. * Captures any response, or full exceptions generated by the handler, and include as tracing metadata. +By default, the `@Tracing` annotation uses `captureMode=ENVIRONMENT_VAR`, which means it will only record method responses and exceptions if you set +the environment variables `POWERTOOLS_TRACER_CAPTURE_RESPONSE` and `POWERTOOLS_TRACER_CAPTURE_ERROR` to `true`. You can override this behavior by +specifying a different `captureMode` to always record response, exception, both, or neither. -By default, this annotation will automatically record method responses and exceptions. You can change the default behavior by setting -the environment variables `POWERTOOLS_TRACER_CAPTURE_RESPONSE` and `POWERTOOLS_TRACER_CAPTURE_ERROR` as needed. Optionally, you can override behavior by -different supported `captureMode` to record response, exception or both. +!!! note + When using the functional API with `TracingUtils.withSubsegment()`, response and exception capture is not automatic. You can manually add metadata using `TracingUtils.putMetadata()` as needed. -!!! warning "Returning sensitive information from your Lambda handler or functions, where `Tracing` is used?" - You can disable annotation from capturing their responses and exception as tracing metadata with **`captureMode=DISABLED`** - or globally by setting environment variables **`POWERTOOLS_TRACER_CAPTURE_RESPONSE`** and **`POWERTOOLS_TRACER_CAPTURE_ERROR`** to **`false`** +!!! warning "Returning sensitive information from your Lambda handler or functions?" + When using the `@Tracing` annotation, you can disable it from capturing responses and exceptions as tracing metadata with **`captureMode=DISABLED`** + or globally by setting the environment variables **`POWERTOOLS_TRACER_CAPTURE_RESPONSE`** and **`POWERTOOLS_TRACER_CAPTURE_ERROR`** to **`false`**. + When using the functional API, you have full control over what metadata is captured. -=== "Disable on annotation" +=== "@Tracing annotation - Disable on method" ```java hl_lines="3" public class App implements RequestHandler { @@ -183,7 +209,7 @@ different supported `captureMode` to record response, exception or both. } ``` -=== "Disable Globally" +=== "@Tracing annotation - Disable Globally" ```yaml hl_lines="11 12" Resources: @@ -200,6 +226,20 @@ different supported `captureMode` to record response, exception or both. POWERTOOLS_TRACER_CAPTURE_ERROR: false ``` +=== "Functional API" + + ```java hl_lines="6 7 8" + import software.amazon.lambda.powertools.tracing.TracingUtils; + + public class App implements RequestHandler { + + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + TracingUtils.withSubsegment("businessLogic", subsegment -> { + // With functional API, you control what metadata is captured + }); + } + ``` + ### Annotations & Metadata **Annotations** are key-values associated with traces and indexed by AWS X-Ray. You can use them to filter traces and to @@ -272,32 +312,13 @@ specific fields from received event due to security. } ``` -## Utilities - -Tracing modules comes with certain utility method when you don't want to use annotation for capturing a code block -under a subsegment, or you are doing multithreaded programming. Refer examples below. +## Advanced usage -=== "Functional Api" +### Multi-threaded programming - ```java hl_lines="7 8 9 11 12 13" - import software.amazon.lambda.powertools.tracing.Tracing; - import software.amazon.lambda.powertools.tracing.TracingUtils; - - public class App implements RequestHandler { - - public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { - TracingUtils.withSubsegment("loggingResponse", subsegment -> { - // Some business logic - }); - - TracingUtils.withSubsegment("localNamespace", "loggingResponse", subsegment -> { - // Some business logic - }); - } - } - ``` +When working with multiple threads, you need to pass the trace entity to ensure proper trace context propagation. -=== "Multi Threaded Programming" +=== "Multi-threaded example" ```java hl_lines="7 9 10 11" import static software.amazon.lambda.powertools.tracing.TracingUtils.withEntitySubsegment; @@ -317,25 +338,33 @@ under a subsegment, or you are doing multithreaded programming. Refer examples b ## Instrumenting SDK clients and HTTP calls -Powertools for Lambda (Java) cannot intercept SDK clients instantiation to add X-Ray instrumentation. You should make sure to instrument the SDK clients explicitly. Refer details on -[how to instrument SDK client with Xray](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java.html#xray-sdk-java-awssdkclients) -and [outgoing http calls](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java.html#xray-sdk-java-httpclients). For example: +### AWS SDK for Java 2.x -=== "LambdaHandler.java" +Powertools for AWS Lambda (Java) includes the `aws-xray-recorder-sdk-aws-sdk-v2-instrumentor` library, which **automatically instruments all AWS SDK v2 clients** when you add the `powertools-tracing` dependency to your project. This means downstream calls to AWS services are traced without any additional configuration. - ```java hl_lines="1 2 7" - import com.amazonaws.xray.AWSXRay; - import com.amazonaws.xray.handlers.TracingHandler; +If you need more control over which clients are instrumented, you can manually add the `TracingInterceptor` to specific clients: + +=== "Manual instrumentation (optional)" + + ```java hl_lines="1 2 3 8 9 10 11" + import com.amazonaws.xray.interceptors.TracingInterceptor; + import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; + import software.amazon.awssdk.services.dynamodb.DynamoDbClient; public class LambdaHandler { - private AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard() - .withRegion(Regions.fromName(System.getenv("AWS_REGION"))) - .withRequestHandlers(new TracingHandler(AWSXRay.getGlobalRecorder())) + private DynamoDbClient client = DynamoDbClient.builder() + .region(Region.US_WEST_2) + .overrideConfiguration(ClientOverrideConfiguration.builder() + .addExecutionInterceptor(new TracingInterceptor()) + .build() + ) .build(); // ... } ``` +For more details, refer to the [AWS X-Ray documentation on tracing AWS SDK calls](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java-awssdkclients.html) and [outgoing HTTP calls](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java-httpclients.html). + ## Testing your code When using `@Tracing` annotation, your Junit test cases needs to be configured to create parent Segment required by [AWS X-Ray SDK for Java](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java.html). @@ -351,7 +380,7 @@ used internally via AWS X-Ray SDK to configure itself properly for lambda runtim === "Maven (pom.xml)" - ```xml hl_lines="4-13" + ```xml ... @@ -370,9 +399,9 @@ used internally via AWS X-Ray SDK to configure itself properly for lambda runtim ``` -=== "Gradle (build.gradle) " +=== "Gradle (build.gradle)" - ```json hl_lines="2-4" + ```json // Configures environment variable to avoid initialization of AWS X-Ray segments for each tests test { environment "LAMBDA_TASK_ROOT", "handler" @@ -418,6 +447,3 @@ Below is an example configuration needed for each test case. // test logic } ``` - - - diff --git a/docs/index.md b/docs/index.md index 9c5c803cb..655c16e03 100644 --- a/docs/index.md +++ b/docs/index.md @@ -26,6 +26,7 @@ This project separates core utilities that will be available in other runtimes v ## Install + -**Manual installation** Powertools for AWS Lambda (Java) dependencies are available in Maven Central. You can use your favourite dependency management tool to install it * [Maven](https://maven.apache.org/) @@ -90,7 +91,7 @@ Powertools for AWS Lambda (Java) dependencies are available in Maven Central. Yo software.amazon.lambda - powertools-logging + powertools-logging-log4j {{ powertools.version }} @@ -107,7 +108,8 @@ Powertools for AWS Lambda (Java) dependencies are available in Maven Central. Yo ... ... - + + ... @@ -175,7 +177,8 @@ Powertools for AWS Lambda (Java) dependencies are available in Maven Central. Yo } dependencies { - aspect 'software.amazon.lambda:powertools-logging:{{ powertools.version }}' + // Note: This AspectJ configuration is not needed when using the functional approach + aspect 'software.amazon.lambda:powertools-logging-log4j:{{ powertools.version }}' aspect 'software.amazon.lambda:powertools-tracing:{{ powertools.version }}' aspect 'software.amazon.lambda:powertools-metrics:{{ powertools.version }}' } @@ -184,28 +187,15 @@ Powertools for AWS Lambda (Java) dependencies are available in Maven Central. Yo targetCompatibility = 11 ``` -???+ tip "Why a different configuration?" - Powertools for AWS Lambda (Java) is using [AspectJ](https://eclipse.dev/aspectj/doc/released/progguide/starting.html) internally - to handle annotations. Recently, in order to support Java 17 we had to move to `dev.aspectj:aspectj-maven-plugin` because - `org.codehaus.mojo:aspectj-maven-plugin` does not support Java 17. - Under the hood, `org.codehaus.mojo:aspectj-maven-plugin` is based on AspectJ 1.9.7, - while `dev.aspectj:aspectj-maven-plugin` is based on AspectJ 1.9.8, compiled for Java 11+. +???+ tip "Don't want to use AspectJ?" + Powertools for AWS Lambda (Java) now provides a functional API that doesn't require AspectJ configuration. Learn more about the [functional approach](./usage-patterns.md#functional-approach). ### Java Compatibility -Powertools for AWS Lambda (Java) supports all Java version from 11 up to 21 as well as the -[corresponding Lambda runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). +Powertools for AWS Lambda (Java) supports all Java versions from 11 to 25 in line with the [corresponding Lambda runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). -For the following modules, Powertools for AWS Lambda (Java) leverages the **aspectj** library to provide annotations: -- Logging -- Metrics -- Tracing -- Parameters -- Idempotency -- Validation -- Large messages +In addition to the functional approach, [Logging](./core/logging.md), [Metrics](./core/metrics.md), [Tracing](./core/tracing.md), [Parameters](./utilities/parameters.md), [Idempotency](./utilities/idempotency.md), [Validation](./utilities/validation.md), and [Large Messages](./utilities/large_messages.md) utilities support annotations using AspectJ, which require configuration of the `aspectjrt` runtime library. - -You may need to add the good version of `aspectjrt` to your dependencies based on the jdk used for building your function: +You may need to add the appropriate version of `aspectjrt` to your dependencies based on the JDK used for building your function: ```xml @@ -215,17 +205,18 @@ You may need to add the good version of `aspectjrt` to your dependencies based o ``` -Use the following [dependency matrix](https://github.com/eclipse-aspectj/aspectj/blob/master/docs/dist/doc/JavaVersionCompatibility.md) between this library and the JDK: +Use the following [dependency matrix](https://github.com/eclipse-aspectj/aspectj/blob/master/docs/release/JavaVersionCompatibility.adoc) to understand which AspectJ version to use based on your JDK version: | JDK version | aspectj version | |-------------|------------------------| | `11-17` | `1.9.20.1` (or higher) | | `21` | `1.9.21` (or higher) | +| `25` | `1.9.25` (or higher) | ## Environment variables !!! info - **Explicit parameters take precedence over environment variables.** + Explicit parameters take precedence over environment variables. | Environment variable | Description | Utility | | -------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------- | diff --git a/docs/processes/maintainers.md b/docs/processes/maintainers.md index 8f7f6a8fd..f2839c532 100644 --- a/docs/processes/maintainers.md +++ b/docs/processes/maintainers.md @@ -17,7 +17,6 @@ This is document explains who the maintainers are, their responsibilities, and h | Maintainer | GitHub ID | Affiliation | | --------------- | -------------------------------------------------------------------- | ----------- | | Philipp Page | [phipag](https://github.com/phipag){target="\_blank" rel="nofollow"} | Amazon | -| Simon Thulbourn | [sthulb](https://github.com/sthulb){target="\_blank" rel="nofollow"} | Amazon | ## Emeritus @@ -25,6 +24,7 @@ Previous active maintainers who contributed to this project. | Maintainer | GitHub ID | Affiliation | | --------------------- | -------------------------------------------------------------------------------------- | ------------- | +| Simon Thulbourn | [sthulb](https://github.com/sthulb){target="\_blank" rel="nofollow"} | Former Amazon | | Jerome Van Der Linden | [jeromevdl](https://github.com/jeromevdl){target="\_blank" rel="nofollow"} | Amazon | | Michele Ricciardi | [mriccia](https://github.com/mriccia){target="\_blank" rel="nofollow"} | Amazon | | Scott Gerring | [scottgerring](https://github.com/scottgerring){target="\_blank" rel="nofollow"} | DataDog | diff --git a/docs/usage-patterns.md b/docs/usage-patterns.md new file mode 100644 index 000000000..e66538937 --- /dev/null +++ b/docs/usage-patterns.md @@ -0,0 +1,183 @@ +--- +title: Usage patterns +description: Getting to know the Powertools for AWS Lambda toolkit +--- + + + +Powertools for AWS Lambda (Java) is a collection of utilities designed to help you build serverless applications on AWS. + +The toolkit is modular, so you can pick and choose the utilities you need for your application, but also combine them for a complete solution for your serverless applications. + +## Patterns + +Many of the utilities provided can be used with different patterns, depending on your preferences and the structure of your code. + +### AspectJ Annotation + +If you prefer using annotations to apply cross-cutting concerns to your Lambda handlers, the AspectJ annotation pattern is a good fit. This approach lets you decorate methods with Powertools utilities using annotations, applying their functionality with minimal code changes. + +This pattern works well when you want to keep your business logic clean and separate concerns using aspect-oriented programming. + + +!!! note + This approach requires configuring AspectJ compile-time weaving in your build tool (Maven or Gradle). See the [installation guide](./index.md#install) for setup instructions. + +=== "Logging" + + ```java + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.logging.CorrelationIdPaths; + import software.amazon.lambda.powertools.logging.Logging; + + public class App implements RequestHandler { + private static final Logger log = LoggerFactory.getLogger(App.class); + + @Logging(logEvent = true, correlationIdPath = CorrelationIdPaths.API_GATEWAY_REST) + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + log.info("Processing request"); + return new APIGatewayProxyResponseEvent().withStatusCode(200).withBody("Success"); + } + } + ``` + +=== "Metrics" + + ```java + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; + import software.amazon.lambda.powertools.metrics.FlushMetrics; + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.MetricsFactory; + import software.amazon.lambda.powertools.metrics.model.MetricUnit; + + public class App implements RequestHandler { + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @FlushMetrics(namespace = "ServerlessApp", service = "payment") + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + metrics.addMetric("SuccessfulBooking", 1, MetricUnit.COUNT); + return new APIGatewayProxyResponseEvent().withStatusCode(200).withBody("Success"); + } + } + ``` + +=== "Tracing" + + ```java + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; + import software.amazon.lambda.powertools.tracing.Tracing; + import software.amazon.lambda.powertools.tracing.TracingUtils; + + public class App implements RequestHandler { + + @Tracing + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + TracingUtils.putAnnotation("operation", "payment"); + return processPayment(); + } + + @Tracing + private APIGatewayProxyResponseEvent processPayment() { + return new APIGatewayProxyResponseEvent().withStatusCode(200).withBody("Success"); + } + } + ``` + +### Functional Approach + +If you prefer a more functional programming style or want to avoid AspectJ configuration, you can use the Powertools for AWS Lambda (Java) utilities directly in your code. This approach is more explicit and provides full control over how the utilities are applied. + +This pattern is ideal when you want to avoid AspectJ setup or prefer a more imperative style. It also eliminates the AspectJ runtime dependency, making your deployment package more lightweight. + +=== "Logging" + + ```java + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.logging.CorrelationIdPaths; + import software.amazon.lambda.powertools.logging.PowertoolsLogging; + + public class App implements RequestHandler { + private static final Logger log = LoggerFactory.getLogger(App.class); + + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + return PowertoolsLogging.withLogging( + context, + 0.7, + CorrelationIdPaths.API_GATEWAY_REST, + input, + () -> processRequest(input)); + } + + private APIGatewayProxyResponseEvent processRequest(APIGatewayProxyRequestEvent input) { + // do something with input + log.info("Processing request"); + return new APIGatewayProxyResponseEvent().withStatusCode(200).withBody("Success"); + } + } + ``` + +=== "Metrics" + + ```java + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.MetricsFactory; + import software.amazon.lambda.powertools.metrics.model.MetricUnit; + + public class App implements RequestHandler { + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + try { + metrics.addMetric("SuccessfulBooking", 1, MetricUnit.COUNT); + return new APIGatewayProxyResponseEvent().withStatusCode(200).withBody("Success"); + } finally { + metrics.flush(); + } + } + } + ``` + +=== "Tracing" + + ```java + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; + import software.amazon.lambda.powertools.tracing.TracingUtils; + + public class App implements RequestHandler { + + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + TracingUtils.withSubsegment("processPayment", subsegment -> { + subsegment.putAnnotation("operation", "payment"); + // Business logic here + }); + return new APIGatewayProxyResponseEvent().withStatusCode(200).withBody("Success"); + } + } + ``` + + +!!! note + The functional approach is available for all utilities. Further examples and detailed usage can be found in the individual documentation pages for each utility. diff --git a/docs/utilities/idempotency.md b/docs/utilities/idempotency.md index 83f256e6b..cecc65d7b 100644 --- a/docs/utilities/idempotency.md +++ b/docs/utilities/idempotency.md @@ -29,7 +29,7 @@ times with the same parameters**. This makes idempotent operations safe to retry === "Maven" - ```xml hl_lines="3-7 16 18 24-27" + ```xml hl_lines="3-7 16 18 25-28" ... @@ -41,6 +41,7 @@ times with the same parameters**. This makes idempotent operations safe to retry ... + ... @@ -82,10 +83,10 @@ times with the same parameters**. This makes idempotent operations safe to retry === "Gradle" - ```groovy hl_lines="3 11" + ```groovy hl_lines="3 11 12" plugins { id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' // Not needed when using the functional approach } repositories { @@ -93,7 +94,8 @@ times with the same parameters**. This makes idempotent operations safe to retry } dependencies { - aspect 'software.amazon.lambda:powertools-idempotency-dynamodb:{{ powertools.version }}' + aspect 'software.amazon.lambda:powertools-idempotency-core:{{ powertools.version }}' // Not needed when using the functional approach + implementation 'software.amazon.lambda:powertools-idempotency-dynamodb:{{ powertools.version }}' } sourceCompatibility = 11 // or higher @@ -104,7 +106,7 @@ times with the same parameters**. This makes idempotent operations safe to retry Before getting started, you need to create a persistent storage layer where the idempotency utility can store its state - your Lambda functions will need read and write access to it. -As of now, Amazon DynamoDB is the only supported persistent storage layer, so you'll need to create a table first. +As of now, Amazon DynamoDB is the only supported persistent storage layer, so you'll need to create a table first or [bring your own persistence store](#bring-your-own-persistent-store). **Default table configuration** @@ -148,29 +150,29 @@ Resources: ``` !!! warning "Warning: Large responses with DynamoDB persistence layer" - When using this utility with DynamoDB, your function's responses must be [smaller than 400KB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Limits.html#limits-items). + When using this utility with DynamoDB, your function's responses must be [smaller than 400KB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Constraints.html#limits-items). Larger items cannot be written to DynamoDB and will cause exceptions. !!! info "Info: DynamoDB" - Each function invocation will generally make 2 requests to DynamoDB. If the - result returned by your Lambda is less than 1kb, you can expect 2 WCUs per invocation. For retried invocations, you will - see 1WCU and 1RCU. Review the [DynamoDB pricing documentation](https://aws.amazon.com/dynamodb/pricing/) to + Each function invocation will generally make 1 request to DynamoDB. If the + result returned by your Lambda is less than 1kb, you can expect 1 WCUs per invocation. For retried invocations, you will + see 1 WCU. In some cases, the utility might make 2 requests to DynamoDB in which case you will see 1 RCU and 1 WCU. Review the [DynamoDB pricing documentation](https://aws.amazon.com/dynamodb/pricing/) to estimate the cost. -### Idempotent annotation +### Basic usage -You can quickly start by initializing the `DynamoDBPersistenceStore` and using it with the `@Idempotent` annotation on your Lambda handler. +You can use Powertools for AWS Lambda Idempotency with either the `@Idempotent` annotation or the functional API. !!! warning "Important" Initialization and configuration of the `DynamoDBPersistenceStore` must be performed outside the handler, preferably in the constructor. -=== "App.java" +=== "@Idempotent annotation" ```java hl_lines="5-9 12 19" public class App implements RequestHandler { public App() { - // we need to initialize idempotency store before the handleRequest method is called + // We need to initialize idempotency store before the handleRequest method is called Idempotency.config().withPersistenceStore( DynamoDBPersistenceStore.builder() .withTableName(System.getenv("TABLE_NAME")) @@ -191,6 +193,33 @@ You can quickly start by initializing the `DynamoDBPersistenceStore` and using i ``` +=== "Functional API" + + ```java hl_lines="5-9 13-14" + public class App implements RequestHandler { + + public App() { + // We need to initialize idempotency store before the handleRequest method is called + Idempotency.config().withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .build() + ).configure(); + } + + public SubscriptionResult handleRequest(final Subscription event, final Context context) { + Idempotency.registerLambdaContext(context); + return Idempotency.makeIdempotent(this::processSubscription, event, SubscriptionResult.class); + } + + private SubscriptionResult processSubscription(Subscription event) { + SubscriptionPayment payment = createSubscriptionPayment(event.getUsername(), event.getProductId()); + return new SubscriptionResult(payment.getId(), "success", 200); + } + } + + ``` + === "Example event" ```json @@ -200,25 +229,32 @@ You can quickly start by initializing the `DynamoDBPersistenceStore` and using i } ``` -#### Idempotent annotation on another method +#### Making non-handler methods idempotent -You can use the `@Idempotent` annotation for any synchronous Java function, not only the `handleRequest` one. +You can make any synchronous Java function idempotent, not only the `handleRequest` handler. -When using `@Idempotent` annotation on another method, you must tell which parameter in the method signature has the data we should use: +**With the `@Idempotent` annotation**, you must specify which parameter contains the idempotency key: - If the method only has one parameter, it will be used by default. - If there are 2 or more parameters, you must set the `@IdempotencyKey` on the parameter to use. +**With the functional API**, you explicitly pass the idempotency key: + + - For single-parameter methods, use `Idempotency.makeIdempotent(this::method, param, ReturnType.class)` + - For multi-parameter methods, use `Idempotency.makeIdempotent(idempotencyKey, () -> method(param1, param2), ReturnType.class)` + !!! info "The parameter must be serializable in JSON. We use Jackson internally to (de)serialize objects" -=== "AppSqsEvent.java" +=== "@Idempotent annotation" This example also demonstrates how you can integrate with [Batch utility](batch.md), so you can process each record in an idempotent manner. - ```java hl_lines="19 23-25 30-31" - public class AppSqsEvent implements RequestHandler { + ```java hl_lines="6-15 17-19 27-28" + public class SqsBatchHandler implements RequestHandler { + + private final BatchMessageHandler handler; - public AppSqsEvent() { + public SqsBatchHandler() { Idempotency.config() .withPersistenceStore( DynamoDBPersistenceStore.builder() @@ -226,31 +262,66 @@ When using `@Idempotent` annotation on another method, you must tell which param .build() ).withConfig( IdempotencyConfig.builder() - .withEventKeyJMESPath("messageId") // see Choosing a payload subset section + .withEventKeyJMESPath("messageId") .build() ).configure(); - } + + handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithRawMessageHandler(this::processMessage); + } @Override - @SqsBatch(SampleMessageHandler.class) - public String handleRequest(SQSEvent input, Context context) { - dummy("hello", "world"); - return "{\"statusCode\": 200}"; + public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + return handler.processBatch(sqsEvent, context); } @Idempotent - private String dummy(String argOne, @IdempotencyKey String argTwo) { - return "something"; + private void processMessage(@IdempotencyKey SQSEvent.SQSMessage message) { + // Process message } + } + ``` + +=== "Functional API" + + This example also demonstrates how you can integrate with the [Batch utility](batch.md), so you can process each record in an idempotent manner. **Note: The JMESPath function still applies even when passing the idempotency key manually.** + + ```java hl_lines="6-15 17-19 24 29" + public class SqsBatchHandler implements RequestHandler { - public static class SampleMessageHandler implements SqsMessageHandler { - @Override - @Idempotent - // no need to use @IdempotencyKey as there is only one parameter - public String process(SQSMessage message) { - String returnVal = doSomething(message.getBody()); - return returnVal; - } + private final BatchMessageHandler handler; + + public SqsBatchHandler() { + Idempotency.config() + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .build() + ).withConfig( + IdempotencyConfig.builder() + .withEventKeyJMESPath("messageId") + .build() + ).configure(); + + handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithRawMessageHandler(this::processMessage); + } + + @Override + public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + Idempotency.registerLambdaContext(context); + return handler.processBatch(sqsEvent, context); + } + + private void processMessage(SQSEvent.SQSMessage message) { + Idempotency.makeIdempotent(this::handleMessage, message, Void.class); + } + + private Void handleMessage(SQSEvent.SQSMessage message) { + // Process message + return null; } } ``` @@ -304,9 +375,9 @@ Imagine the function executes successfully, but the client never receives the re To alter this behaviour, you can use the [JMESPath built-in function](serialization.md#jmespath-functions) `powertools_json()` to treat the payload as a JSON object rather than a string. -=== "PaymentFunction.java" +=== "@Idempotent annotation" - ```java hl_lines="5-7 16 29-31" + ```java hl_lines="7 16" public class PaymentFunction implements RequestHandler { public PaymentFunction() { @@ -344,6 +415,50 @@ Imagine the function executes successfully, but the client never receives the re } ``` +=== "Functional API" + + ```java hl_lines="7 17-18" + public class PaymentFunction implements RequestHandler { + + public PaymentFunction() { + Idempotency.config() + .withConfig( + IdempotencyConfig.builder() + .withEventKeyJMESPath("powertools_json(body)") + .build()) + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .build()) + .configure(); + } + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent event, final Context context) { + Idempotency.registerLambdaContext(context); + return Idempotency.makeIdempotent(this::processPayment, event, APIGatewayProxyResponseEvent.class); + } + + private APIGatewayProxyResponseEvent processPayment(APIGatewayProxyRequestEvent event) { + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent(); + + try { + Subscription subscription = JsonConfig.get().getObjectMapper().readValue(event.getBody(), Subscription.class); + + SubscriptionPayment payment = createSubscriptionPayment( + subscription.getUsername(), + subscription.getProductId() + ); + + return response + .withStatusCode(200) + .withBody(String.format("{\"paymentId\":\"%s\"}", payment.getId())); + + } catch (JsonProcessingException e) { + return response.withStatusCode(500); + } + } + ``` + === "Example event" ```json hl_lines="3" @@ -417,46 +532,82 @@ The client was successful in receiving the result after the retry. Since the Lam #### Lambda timeouts -This is automatically done when you annotate your Lambda handler with [@Idempotent annotation](#idempotent-annotation). - To prevent against extended failed retries when a [Lambda function times out](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-verify-invocation-timeouts/), Powertools for AWS Lambda (Java) calculates and includes the remaining invocation available time as part of the idempotency record. !!! example If a second invocation happens **after** this timestamp, and the record is marked as `INPROGRESS`, we will execute the invocation again as if it was in the `EXPIRED` state. This means that if an invocation expired during execution, it will be quickly executed again on the next retry. -!!! important - If you are using the [@Idempotent annotation on another method](#idempotent-annotation-on-another-method) to guard isolated parts of your code, you must use `registerLambdaContext` method available in the `Idempotency` object to benefit from this protection. +**With the `@Idempotent` annotation**, this is automatically done when you annotate your Lambda handler. + +**With the functional API** or when using the `@Idempotent` annotation on methods other than the handler, you must call `Idempotency.registerLambdaContext(context)` to benefit from this protection. +!!! important Here is an example on how you register the Lambda context in your handler: - ```java hl_lines="13-19" title="Registering the Lambda context" - public class PaymentHandler implements RequestHandler> { - - public PaymentHandler() { - Idempotency.config() - .withPersistenceStore( - DynamoDBPersistenceStore.builder() - .withTableName(System.getenv("TABLE_NAME")) - .build()) - .configure(); - } + === "@Idempotent annotation" + + ```java hl_lines="14" title="Registering the Lambda context" + public class PaymentHandler implements RequestHandler> { + + public PaymentHandler() { + Idempotency.config() + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .build()) + .configure(); + } + + @Override + public List handleRequest(SQSEvent sqsEvent, Context context) { + Idempotency.registerLambdaContext(context); + return sqsEvent.getRecords().stream().map(record -> process(record.getMessageId(), record.getBody())).collect(Collectors.toList()); + } + + @Idempotent + private String process(String messageId, @IdempotencyKey String messageBody) { + logger.info("Processing messageId: {}", messageId); + PaymentRequest request = extractDataFrom(messageBody).as(PaymentRequest.class); + return paymentService.process(request); + } - @Override - public List handleRequest(SQSEvent sqsEvent, Context context) { - Idempotency.registerLambdaContext(context); - return sqsEvent.getRecords().stream().map(record -> process(record.getMessageId(), record.getBody())).collect(Collectors.toList()); } - - @Idempotent - private String process(String messageId, @IdempotencyKey String messageBody) { - logger.info("Processing messageId: {}", messageId); - PaymentRequest request = extractDataFrom(messageBody).as(PaymentRequest.class); - return paymentService.process(request); + ``` + + === "Functional API" + + ```java hl_lines="14" title="Registering the Lambda context" + public class PaymentHandler implements RequestHandler> { + + public PaymentHandler() { + Idempotency.config() + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .build()) + .configure(); + } + + @Override + public List handleRequest(SQSEvent sqsEvent, Context context) { + Idempotency.registerLambdaContext(context); + return sqsEvent.getRecords().stream() + .map(record -> Idempotency.makeIdempotent( + record.getBody(), + () -> process(record.getMessageId(), record.getBody()), + String.class)) + .collect(Collectors.toList()); + } + + private String process(String messageId, String messageBody) { + logger.info("Processing messageId: {}", messageId); + PaymentRequest request = extractDataFrom(messageBody).as(PaymentRequest.class); + return paymentService.process(request); + } + } - - } - ``` + ``` #### Lambda timeout sequence diagram @@ -499,9 +650,11 @@ sequenceDiagram ### Handling exceptions -If you are using the `@Idempotent` annotation on your Lambda handler or any other method, any unhandled exceptions that are thrown during the code execution will cause **the record in the persistence layer to be deleted**. +**With the `@Idempotent` annotation**, any unhandled exceptions that are thrown during the code execution will cause **the record in the persistence layer to be deleted**. This means that new invocations will execute your code again despite having the same payload. If you don't want the record to be deleted, you need to catch exceptions within the idempotent function and return a successful response. +**With the functional API**, exceptions are handled the same way - unhandled exceptions will cause the record to be deleted. You should catch and handle exceptions within your idempotent function if you want to preserve the record. +
```mermaid sequenceDiagram @@ -553,7 +706,7 @@ If an Exception is raised _outside_ the scope of a decorated method and after yo This persistence store is built-in, and you can either use an existing DynamoDB table or create a new one dedicated for idempotency state (recommended). Use the builder to customize the table structure: -```java hl_lines="3-7" title="Customizing DynamoDBPersistenceStore to suit your table structure" +```java hl_lines="2-7" title="Customizing DynamoDBPersistenceStore to suit your table structure" DynamoDBPersistenceStore.builder() .withTableName(System.getenv("TABLE_NAME")) .withKeyAttr("idempotency_key") @@ -579,11 +732,68 @@ When using DynamoDB as a persistence layer, you can alter the attribute names by ## Advanced +### Using explicit function names + +When using the functional API, if you need to call different methods with the same payload as the idempotency key, you must provide explicit function names to differentiate between them. This ensures each function has its own idempotency scope. + +=== "Functional API with explicit names" + + ```java hl_lines="5-9 11-15" + public Response handleRequest(Order order, Context context) { + Idempotency.registerLambdaContext(context); + + // Same orderId, different operations - need explicit function names + Idempotency.makeIdempotent( + "processPayment", + order.getId(), + () -> processPayment(order), + PaymentResult.class); + + Idempotency.makeIdempotent( + "sendConfirmation", + order.getId(), + () -> sendEmail(order), + EmailResult.class); + + return new Response("success"); + } + ``` + +!!! note + When using the `@Idempotent` annotation, the function name is automatically inferred from the method name, so this is not needed. + +### Generic return types support + +The functional API supports making methods with generic return types idempotent using Jackson's `TypeReference`. This is not possible with the `@Idempotent` annotation due to type erasure. + +=== "Functional API with TypeReference" + + ```java hl_lines="1 6-10" + import com.fasterxml.jackson.core.type.TypeReference; + + public Map handleRequest(Product input, Context context) { + Idempotency.registerLambdaContext(context); + + return Idempotency.makeIdempotent( + this::processProduct, + input, + new TypeReference>() {} + ); + } + + private Map processProduct(Product product) { + // business logic returning generic type + Map result = new HashMap<>(); + // ... + return result; + } + ``` + ### Customizing the default behavior Idempotency behavior can be further configured with **`IdempotencyConfig`** using a builder: -```java hl_lines="2-8" title="Customizing IdempotencyConfig" +```java hl_lines="2-9" title="Customizing IdempotencyConfig" IdempotencyConfig.builder() .withEventKeyJMESPath("id") .withPayloadValidationJMESPath("paymentId") @@ -667,7 +877,7 @@ By default, we will return the same result as it returned before, however in thi With **`PayloadValidationJMESPath`**, you can provide an additional JMESPath expression to specify which part of the event body should be validated against previous idempotent invocations -=== "App.java" +=== "@Idempotent annotation" ```java hl_lines="8 13 20 26" public App() { @@ -700,6 +910,43 @@ With **`PayloadValidationJMESPath`**, you can provide an additional JMESPath exp } ``` +=== "Functional API" + + ```java hl_lines="8 14-15 24 30" + public App() { + Idempotency.config() + .withPersistenceStore(DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .build()) + .withConfig(IdempotencyConfig.builder() + .withEventKeyJMESPath("[userDetail, productId]") + .withPayloadValidationJMESPath("amount") + .build()) + .configure(); + } + + public SubscriptionResult handleRequest(final Subscription input, final Context context) { + Idempotency.registerLambdaContext(context); + return Idempotency.makeIdempotent(this::processSubscription, input, SubscriptionResult.class); + } + + private SubscriptionResult processSubscription(Subscription input) { + // Creating a subscription payment is a side + // effect of calling this function! + SubscriptionPayment payment = createSubscriptionPayment( + input.getUserDetail().getUsername(), + input.getProductId(), + input.getAmount() + ) + // ... + return new SubscriptionResult( + "success", 200, + payment.getId(), + payment.getAmount() + ); + } + ``` + === "Example Event 1" ```json hl_lines="8" @@ -745,9 +992,9 @@ This means that we will throw **`IdempotencyKeyException`** if the evaluation of When set to `false` (the default), if the idempotency key is null, then the data is not persisted in the store. -=== "App.java" +=== "@Idempotent annotation" - ```java hl_lines="9-10 13" + ```java hl_lines="9" public App() { Idempotency.config() .withPersistenceStore(DynamoDBPersistenceStore.builder() @@ -767,6 +1014,32 @@ When set to `false` (the default), if the idempotency key is null, then the data } ``` +=== "Functional API" + + ```java hl_lines="9" + public App() { + Idempotency.config() + .withPersistenceStore(DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("TABLE_NAME")) + .build()) + .withConfig(IdempotencyConfig.builder() + // Requires "user"."uid" and "orderId" to be present + .withEventKeyJMESPath("[user.uid, orderId]") + .withThrowOnNoIdempotencyKey(true) + .build()) + .configure(); + } + + public OrderResult handleRequest(final Order input, final Context context) { + Idempotency.registerLambdaContext(context); + return Idempotency.makeIdempotent(this::processOrder, input, OrderResult.class); + } + + private OrderResult processOrder(Order input) { + // ... + } + ``` + === "Success Event" ```json hl_lines="3 6" @@ -977,7 +1250,7 @@ To unit test your function with DynamoDB Local, you can refer to this guide to [ === "pom.xml" - ```xml hl_lines="4-6 24-26 28-31 42 45-47" + ```xml @@ -1046,7 +1319,7 @@ To unit test your function with DynamoDB Local, you can refer to this guide to [ === "AppTest.java" - ```java hl_lines="13-18 24-30 34" + ```java public class AppTest { @Mock private Context context; @@ -1143,7 +1416,7 @@ To unit test your function with DynamoDB Local, you can refer to this guide to [ === "App.java" - ```java hl_lines="8 9 16" + ```java public class App implements RequestHandler { public App() { @@ -1174,7 +1447,7 @@ To unit test your function with DynamoDB Local, you can refer to this guide to [ === "shell" - ```shell hl_lines="2 6 7 12 16 21 22" + ```shell # use or create a docker network docker network inspect sam-local || docker network create sam-local @@ -1201,7 +1474,7 @@ To unit test your function with DynamoDB Local, you can refer to this guide to [ === "env.json" - ```json hl_lines="3" + ```json { "IdempotentFunction": { "TABLE_NAME": "idempotency" diff --git a/docs/utilities/large_messages.md b/docs/utilities/large_messages.md index 38228afe9..9d14c8228 100644 --- a/docs/utilities/large_messages.md +++ b/docs/utilities/large_messages.md @@ -4,7 +4,7 @@ description: Utility --- The large message utility handles SQS and SNS messages which have had their payloads -offloaded to S3 if they are larger than the maximum allowed size (256 KB). +offloaded to S3 if they are larger than the maximum allowed size (1 MB). ## Features @@ -27,12 +27,12 @@ stateDiagram-v2 sendMsg --> extendLib state extendLib { state if_big <> - bigMsg: MessageBody > 256KB ? + bigMsg: MessageBody > 1MB ? putObject: putObject(S3Bucket, S3Key, Body) updateMsg: Update MessageBody
with a pointer to S3
and add a message attribute bigMsg --> if_big - if_big --> [*]: size(body) <= 256kb - if_big --> putObject: size(body) > 256kb + if_big --> [*]: size(body) <= 1MB + if_big --> putObject: size(body) > 1MB putObject --> updateMsg updateMsg --> [*] } @@ -72,7 +72,7 @@ stateDiagram-v2 ``` -SQS and SNS message payload is limited to 256KB. If you wish to send messages with a larger payload, you can leverage the +SQS and SNS message payload is limited to 1MB. If you wish to send messages with a larger payload, you can leverage the [amazon-sqs-java-extended-client-lib](https://github.com/awslabs/amazon-sqs-java-extended-client-lib) or [amazon-sns-java-extended-client-lib](https://github.com/awslabs/amazon-sns-java-extended-client-lib) which offload the message to Amazon S3. See documentation @@ -87,16 +87,14 @@ extended client libraries. Once a message's payload has been processed successfu utility deletes the payload from S3. This utility is compatible with -versions *[1.1.0+](https://github.com/awslabs/amazon-sqs-java-extended-client-lib/releases/tag/1.1.0)* -of amazon-sqs-java-extended-client-lib -and *[1.0.0+](https://github.com/awslabs/amazon-sns-java-extended-client-lib/releases/tag/1.0.0)* -of amazon-sns-java-extended-client-lib. +versions *[1.1.0+](https://github.com/awslabs/amazon-sqs-java-extended-client-lib/releases/tag/1.1.0)* and *[2.0.0+](https://github.com/awslabs/amazon-sqs-java-extended-client-lib/releases/tag/2.0.0)* +of [amazon-sqs-java-extended-client-lib](https://github.com/awslabs/amazon-sqs-java-extended-client-lib) / [amazon-sns-java-extended-client-lib](https://github.com/awslabs/amazon-sns-java-extended-client-lib). ## Install === "Maven" - ```xml hl_lines="3-7 16 18 24-27" + ```xml hl_lines="3-7 25-28" ... @@ -108,6 +106,7 @@ of amazon-sns-java-extended-client-lib. ... + ... @@ -152,7 +151,7 @@ of amazon-sns-java-extended-client-lib. ```groovy hl_lines="3 11" plugins { id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' // Not needed when using the functional approach } repositories { @@ -160,7 +159,7 @@ of amazon-sns-java-extended-client-lib. } dependencies { - aspect 'software.amazon.lambda:powertools-large-messages:{{ powertools.version }}' + aspect 'software.amazon.lambda:powertools-large-messages:{{ powertools.version }}' // Use 'implementation' instead of 'aspect' when using the functional approach } sourceCompatibility = 11 // or higher @@ -175,14 +174,20 @@ on the S3 bucket used for the large messages offloading: - `s3:GetObject` - `s3:DeleteObject` -## Annotation +## Usage -The annotation `@LargeMessage` can be used on any method where the *first* parameter is one of: +You can use the Large Messages utility with either the `@LargeMessage` annotation or the functional API. + +The `@LargeMessage` annotation can be used on any method where the *first* parameter is one of: - `SQSEvent.SQSMessage` - `SNSEvent.SNSRecord` -=== "SQS Example" +The functional API `LargeMessages.processLargeMessage()` accepts the same message types. + +### Basic usage + +=== "@LargeMessage annotation - SQS" ```java hl_lines="8 13 15" import software.amazon.lambda.powertools.largemessages.LargeMessage; @@ -204,7 +209,28 @@ The annotation `@LargeMessage` can be used on any method where the *first* param } ``` -=== "SNS Example" +=== "Functional API - SQS" + + ```java hl_lines="1 8" + import software.amazon.lambda.powertools.largemessages.LargeMessages; + + public class SqsMessageHandler implements RequestHandler { + + @Override + public SQSBatchResponse handleRequest(SQSEvent event, Context context) { + for (SQSMessage message: event.getRecords()) { + LargeMessages.processLargeMessage(message, this::processRawMessage); + } + return SQSBatchResponse.builder().build(); + } + + private void processRawMessage(SQSEvent.SQSMessage sqsMessage) { + // sqsMessage.getBody() will contain the content of the S3 object + } + } + ``` + +=== "@LargeMessage annotation - SNS" ```java hl_lines="7 11 13" import software.amazon.lambda.powertools.largemessages.LargeMessage; @@ -224,6 +250,25 @@ The annotation `@LargeMessage` can be used on any method where the *first* param } ``` +=== "Functional API - SNS" + + ```java hl_lines="1 7" + import software.amazon.lambda.powertools.largemessages.LargeMessages; + + public class SnsRecordHandler implements RequestHandler { + + @Override + public String handleRequest(SNSEvent event, Context context) { + return LargeMessages.processLargeMessage(event.records.get(0), this::processSNSRecord); + } + + private String processSNSRecord(SNSEvent.SNSRecord snsRecord) { + // snsRecord.getSNS().getMessage() will contain the content of the S3 object + return "Hello World"; + } + } + ``` + When the Lambda function is invoked with a SQS or SNS event, the utility first checks if the content was offloaded to S3. In the case of a large message, there is a message attribute specifying the size of the offloaded message and the message contains a pointer to the S3 object. @@ -233,9 +278,9 @@ and place the content of the object in the message payload. You can then directl If there was an error during the S3 download, the function will fail with a `LargeMessageProcessingException`. After your code is invoked and returns without error, the object is deleted from S3 -using the `deleteObject(bucket, key)` API. You can disable the deletion of S3 objects with the following configuration: +using the `deleteObject(bucket, key)` API. You can disable the deletion of S3 objects: -=== "Don't delete S3 Objects" +=== "@LargeMessage annotation" ```java @LargeMessage(deleteS3Object = false) private void processRawMessage(SQSEvent.SQSMessage sqsMessage) { @@ -243,71 +288,143 @@ using the `deleteObject(bucket, key)` API. You can disable the deletion of S3 ob } ``` +=== "Functional API" + ```java + LargeMessages.processLargeMessage(message, this::processRawMessage, false); + ``` + !!! tip "Use together with batch module" This utility works perfectly together with the batch module (`powertools-batch`), especially for SQS: - ```java hl_lines="2 5-7 12 15 16" title="Combining batch and large message modules" - public class SqsBatchHandler implements RequestHandler { - private final BatchMessageHandler handler; - - public SqsBatchHandler() { - handler = new BatchMessageHandlerBuilder() - .withSqsBatchHandler() - .buildWithRawMessageHandler(this::processMessage); - } + === "@LargeMessage annotation" + ```java hl_lines="2 5-7 12 15 16" title="Combining batch and large message modules" + public class SqsBatchHandler implements RequestHandler { + private final BatchMessageHandler handler; + + public SqsBatchHandler() { + handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithRawMessageHandler(this::processMessage); + } - @Override - public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { - return handler.processBatch(sqsEvent, context); + @Override + public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + return handler.processBatch(sqsEvent, context); + } + + @LargeMessage + private void processMessage(SQSEvent.SQSMessage sqsMessage) { + // do something with the message + } } + ``` - @LargeMessage - private void processMessage(SQSEvent.SQSMessage sqsMessage) { - // do something with the message + === "Functional API" + ```java hl_lines="7-9 14 18" title="Combining batch and large message modules" + import software.amazon.lambda.powertools.largemessages.LargeMessages; + + public class SqsBatchHandler implements RequestHandler { + private final BatchMessageHandler handler; + + public SqsBatchHandler() { + handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithRawMessageHandler(this::processMessage); + } + + @Override + public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + return handler.processBatch(sqsEvent, context); + } + + private void processMessage(SQSEvent.SQSMessage sqsMessage) { + LargeMessages.processLargeMessage(sqsMessage, this::handleProcessedMessage); + } + + private void handleProcessedMessage(SQSEvent.SQSMessage processedMessage) { + // do something with the message + } } - } - ``` + ``` !!! tip "Use together with idempotency module" - This utility also works together with the idempotency module (`powertools-idempotency`). - You can add both the `@LargeMessage` and `@Idempotent` annotations, in any order, to the same method. - The `@Idempotent` takes precedence over the `@LargeMessage` annotation. - It means Idempotency module will use the initial raw message (containing the S3 pointer) and not the large message. + When using the `@LargeMessage` annotation, you can combine it with the `@Idempotent` annotation on the same method. + The `@Idempotent` takes precedence over the `@LargeMessage` annotation, meaning the Idempotency module will use the initial raw message (containing the S3 pointer) and not the large message. + + When using the functional API, call `LargeMessages.processLargeMessage()` from within the `@Idempotent` method to ensure idempotency is based on the S3 pointer, not the unwrapped large blob. - ```java hl_lines="6 23-25" title="Combining idempotency and large message modules" - public class SqsBatchHandler implements RequestHandler { + === "@LargeMessage annotation" + ```java hl_lines="6 23-25" title="Combining idempotency and large message modules" + public class SqsBatchHandler implements RequestHandler { - public SqsBatchHandler() { - Idempotency.config().withConfig( - IdempotencyConfig.builder() - .withEventKeyJMESPath("body") // get the body of the message for the idempotency key - .build()) - .withPersistenceStore( - DynamoDBPersistenceStore.builder() - .withTableName(System.getenv("IDEMPOTENCY_TABLE")) - .build() - ).configure(); - } + public SqsBatchHandler() { + Idempotency.config().withConfig( + IdempotencyConfig.builder() + .withEventKeyJMESPath("body") // get the body of the message for the idempotency key + .build()) + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("IDEMPOTENCY_TABLE")) + .build() + ).configure(); + } - @Override - public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { - for (SQSMessage message: event.getRecords()) { - processRawMessage(message, context); + @Override + public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + for (SQSMessage message: event.getRecords()) { + processRawMessage(message, context); + } + return SQSBatchResponse.builder().build(); + } + + @Idempotent + @LargeMessage + private String processRawMessage(@IdempotencyKey SQSEvent.SQSMessage sqsMessage, Context context) { + // do something with the message } - return SQSBatchResponse.builder().build(); } + ``` - @Idempotent - @LargeMessage - private String processRawMessage(@IdempotencyKey SQSEvent.SQSMessage sqsMessage, Context context) { - // do something with the message + === "Functional API" + ```java hl_lines="8 25 27" title="Combining idempotency and large message modules" + import software.amazon.lambda.powertools.largemessages.LargeMessages; + + public class SqsBatchHandler implements RequestHandler { + + public SqsBatchHandler() { + Idempotency.config().withConfig( + IdempotencyConfig.builder() + .withEventKeyJMESPath("body") // get the body of the message for the idempotency key + .build()) + .withPersistenceStore( + DynamoDBPersistenceStore.builder() + .withTableName(System.getenv("IDEMPOTENCY_TABLE")) + .build() + ).configure(); + } + + @Override + public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + for (SQSMessage message: event.getRecords()) { + processRawMessage(message, context); + } + return SQSBatchResponse.builder().build(); + } + + @Idempotent + private String processRawMessage(@IdempotencyKey SQSEvent.SQSMessage sqsMessage, Context context) { + return LargeMessages.processLargeMessage(sqsMessage, this::handleProcessedMessage); + } + + private String handleProcessedMessage(SQSEvent.SQSMessage processedMessage) { + // do something with the message + } } - } - ``` + ``` ## Customizing S3 client configuration -To interact with S3, the utility creates a default S3 Client : +To interact with S3, the utility creates a default S3 Client: === "Default S3 Client" ```java @@ -317,9 +434,9 @@ To interact with S3, the utility creates a default S3 Client : .build(); ``` -If you need to customize this `S3Client`, you can leverage the `LargeMessageConfig` singleton: +If you need to customize this `S3Client`, you can leverage the `LargeMessageConfig` singleton. This works with both the annotation and functional API: -=== "Custom S3 Client" +=== "@LargeMessage annotation" ```java hl_lines="6" import software.amazon.lambda.powertools.largemessages.LargeMessage; @@ -342,6 +459,28 @@ If you need to customize this `S3Client`, you can leverage the `LargeMessageConf } ``` +=== "Functional API" + ```java hl_lines="1 6" + import software.amazon.lambda.powertools.largemessages.LargeMessages; + + public class SnsRecordHandler implements RequestHandler { + + public SnsRecordHandler() { + LargeMessageConfig.init().withS3Client(/* put your custom S3Client here */); + } + + @Override + public String handleRequest(SNSEvent event, Context context) { + return LargeMessages.processLargeMessage(event.records.get(0), this::processSNSRecord); + } + + private String processSNSRecord(SNSEvent.SNSRecord snsRecord) { + // snsRecord.getSNS().getMessage() will contain the content of the S3 object + return "Hello World"; + } + } + ``` + ## Migration from the SQS Large Message utility - Replace the dependency in maven / gradle: `powertools-sqs` ==> `powertools-large-messages` diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index beb460aa6..6de47df68 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -49,6 +49,7 @@ Note that you must provide the concrete parameters module you want to use below
... + ... @@ -91,10 +92,10 @@ Note that you must provide the concrete parameters module you want to use below === "Gradle" - ```groovy hl_lines="3 11 12" + ```groovy hl_lines="3 11-13" plugins { id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' // Not needed when using provider classes directly (without annotations) } repositories { @@ -103,7 +104,8 @@ Note that you must provide the concrete parameters module you want to use below dependencies { // TODO! Provide the parameters module you want to use here - aspect 'software.amazon.lambda:powertools-parameters-secrets:{{ powertools.version }}' + aspect 'software.amazon.lambda:powertools-parameters-secrets:{{ powertools.version }}' // Not needed when using provider classes directly (without annotations) + implementation 'software.amazon.lambda:powertools-parameters-secrets:{{ powertools.version }}' // Use this instead of 'aspect' when using provider classes directly } sourceCompatibility = 11 // or higher @@ -124,19 +126,18 @@ This utility requires additional permissions to work as expected. See the table | AppConfig | `AppConfigProvider.get(String)` `AppConfigProvider.getMultiple(string)` | `appconfig:StartConfigurationSession`, `appConfig:GetLatestConfiguration` | ## Retrieving Parameters -You can retrieve parameters either using annotations or by using the `xParamProvider` class for each parameter -provider directly. The latter is useful if you need to configure the underlying SDK client, for example to use -a different region or credentials, the former is simpler to use. +You can retrieve parameters using either annotations or provider classes directly: + +- **Annotations** (e.g., `@SecretsParam`, `@SSMParam`) - Simpler syntax with field injection, but requires AspectJ configuration +- **Provider classes** (e.g., `SecretsProvider`, `SSMProvider`) - No AspectJ required, useful when you need to configure the underlying SDK client (e.g., different region or credentials), or prefer avoiding AspectJ setup ## Built-in provider classes -This section describes the built-in provider classes for each parameter store, providing -examples showing how to inject parameters using annotations, and how to use the provider -interface. In cases where a provider supports extra features, these will also be described. +This section describes the built-in provider classes for each parameter store. For each provider, examples are shown for both the annotation-based approach and the provider class approach. In cases where a provider supports extra features, these will also be described. ### Secrets Manager -=== "Secrets Manager: @SecretsParam" +=== "@SecretsParam annotation" ```java hl_lines="8 9" import com.amazonaws.services.lambda.runtime.Context; @@ -156,7 +157,7 @@ interface. In cases where a provider supports extra features, these will also be } ``` -=== "Secrets Manager: SecretsProvider" +=== "SecretsProvider class" ```java hl_lines="12-15 19" import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; @@ -195,7 +196,7 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen | **recursive()** | `False` | For `getMultiple()` only, will fetch all parameter values recursively based on a path prefix. | -=== "SSM Parameter Store: @SSMParam" +=== "@SSMParam annotation" ```java hl_lines="8 9" import com.amazonaws.services.lambda.runtime.Context; @@ -214,7 +215,7 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen } ``` -=== "SSM Parameter Store: SSMProvider" +=== "SSMProvider class" ```java hl_lines="12-15 19-20 22" import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; @@ -246,15 +247,14 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen } ``` -=== "SSM Parameter Store: Additional Options" +=== "Additional Options" - ```java hl_lines="9 12" - import software.amazon.lambda.powertools.parameters.SSMProvider; - import software.amazon.lambda.powertools.parameters.ParamManager; + ```java hl_lines="5 9 12" + import software.amazon.lambda.powertools.parameters.ssm.SSMProvider; public class AppWithSSM implements RequestHandler { // Get an instance of the SSM Provider - SSMProvider ssmProvider = ParamManager.getSsmProvider(); + SSMProvider ssmProvider = SSMProvider.builder().build(); // Retrieve a single parameter and decrypt it String value = ssmProvider.withDecryption().get("/my/parameter"); @@ -267,7 +267,7 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen ### DynamoDB -=== "DynamoDB: @DyanmoDbParam" +=== "@DynamoDbParam annotation" ```java hl_lines="8 9" import com.amazonaws.services.lambda.runtime.Context; @@ -286,7 +286,7 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen } ``` -=== "DynamoDB: DynamoDbProvider" +=== "DynamoDbProvider class" ```java hl_lines="12-15 19-20 22" import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; @@ -320,7 +320,7 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen ### AppConfig -=== "AppConfig: @AppConfigParam" +=== "@AppConfigParam annotation" ```java hl_lines="8 9" import com.amazonaws.services.lambda.runtime.Context; @@ -339,7 +339,7 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen } ``` -=== "AppConfig: AppConfigProvider" +=== "AppConfigProvider class" ```java hl_lines="12-15 19-20" import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; diff --git a/docs/utilities/validation.md b/docs/utilities/validation.md index ec35b7034..8e0d2c631 100644 --- a/docs/utilities/validation.md +++ b/docs/utilities/validation.md @@ -26,6 +26,7 @@ This utility provides JSON Schema validation for payloads held within events and ... + ... @@ -67,10 +68,10 @@ This utility provides JSON Schema validation for payloads held within events and === "Gradle" - ```groovy hl_lines="3 11" + ```groovy hl_lines="3 11 12" plugins { id 'java' - id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' // Not needed when using the functional approach with ValidationUtils.validate() } repositories { @@ -78,7 +79,8 @@ This utility provides JSON Schema validation for payloads held within events and } dependencies { - aspect 'software.amazon.lambda:powertools-validation:{{ powertools.version }}' + aspect 'software.amazon.lambda:powertools-validation:{{ powertools.version }}' // Not needed when using the functional approach with ValidationUtils.validate() + implementation 'software.amazon.lambda:powertools-validation:{{ powertools.version }}' // Use this instead of 'aspect' when using the functional approach } sourceCompatibility = 11 // or higher @@ -87,17 +89,18 @@ This utility provides JSON Schema validation for payloads held within events and ## Validating events -You can validate inbound and outbound events using `@Validation` annotation. +You can validate inbound and outbound events using either the `@Validation` annotation or the functional approach with `ValidationUtils.validate()` methods: -You can also use the `Validator#validate()` methods, if you want more control over the validation process such as handling a validation error. +- **@Validation annotation** - Simpler syntax with automatic validation, but requires AspectJ configuration +- **ValidationUtils.validate()** - No AspectJ required, provides more control over the validation process such as handling validation errors -We support JSON schema version 4, 6, 7, 2019-09 and 2020-12 using the [NetworkNT JSON Schema Validator](https://github.com/networknt/json-schema-validator). ([Compatibility with JSON Schema versions](https://github.com/networknt/json-schema-validator/blob/master/doc/compatibility.md)). +We support JSON schema version 4, 6, 7, 2019-09 and 2020-12 using the [NetworkNT JSON Schema Validator](https://github.com/networknt/json-schema-validator) ([Compatibility with JSON Schema versions](https://github.com/networknt/json-schema-validator/blob/master/doc/compatibility.md)). The validator is configured to enable format assertions by default even for 2019-09 and 2020-12. ### Validation annotation -`@Validation` annotation is used to validate either inbound events or functions' response. +The `@Validation` annotation is used to validate either inbound events or functions' response. It will fail fast if an event or response doesn't conform with given JSON Schema. For most type of events a `ValidationException` will be thrown. @@ -129,11 +132,11 @@ While it is easier to specify a json schema file in the classpath (using the not **NOTE**: It's not a requirement to validate both inbound and outbound schemas - You can either use one, or both. -### Validate function +### Functional approach with ValidationUtils -Validate standalone function is used within the Lambda handler, or any other methods that perform data validation. +The `ValidationUtils.validate()` method provides a functional approach that can be used within the Lambda handler or any other methods that perform data validation. This approach does not require AspectJ configuration. -You can also gracefully handle schema validation errors by catching `ValidationException`. +With this approach, you can gracefully handle schema validation errors by catching `ValidationException`. === "MyFunctionHandler.java" diff --git a/mkdocs.yml b/mkdocs.yml index da4303c38..3914cfa1a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,9 +1,10 @@ site_name: Powertools for AWS Lambda (Java) site_description: Powertools for AWS Lambda (Java) site_author: Amazon Web Services -site_url: https://docs.powertools.aws.dev/lambda/java/latest/ +site_url: https://docs.aws.amazon.com/powertools/java/latest/ nav: - Homepage: index.md + - Usage patterns: usage-patterns.md - Changelog: changelog.md - Upgrade Guide: upgrade.md - FAQs: FAQs.md @@ -99,6 +100,7 @@ plugins: sections: Project Overview: - index.md + - usage-patterns.md - changelog.md - FAQs.md - roadmap.md From c2763c5a7db3ad63472d54b128d2a1dc74deb810 Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Thu, 13 Nov 2025 10:35:58 +0100 Subject: [PATCH 32/76] chore(ci): Enable Java 25 builds. (#2285) --- .github/workflows/check-build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index e1d949aad..88f1eabee 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -47,6 +47,7 @@ on: - 'powertools-large-messages/**' - 'powertools-logging/**' - 'powertools-metrics/**' + - 'powertools-kafka/**' - 'powertools-parameters/**' - 'powertools-serialization/**' - 'powertools-sqs/**' @@ -72,6 +73,7 @@ jobs: - 11 - 17 - 21 + - 25 steps: - id: checkout name: Checkout repository From 464490dd6e08c01b3e92507a05a34b555730fdaa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 13:42:28 +0100 Subject: [PATCH 33/76] chore(ci): bump version to 2.7.0 (#2286) * chore(ci): bump version to 2.7.0 * Restore CHANGELOG.md from main. --------- Co-authored-by: Powertools for AWS Lambda (Java) Bot <151832416+aws-powertools-bot@users.noreply.github.com> Co-authored-by: Philipp Page --- README.md | 6 +++--- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/README.md | 4 ++-- .../infra/sam-graalvm/README.md | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- .../powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- .../powertools-examples-core-utilities/gradle/build.gradle | 6 +++--- .../kotlin/build.gradle.kts | 6 +++--- .../powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- .../powertools-examples-core-utilities/serverless/pom.xml | 2 +- .../powertools-examples-core-utilities/terraform/pom.xml | 2 +- .../powertools-examples-idempotency/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-idempotency/sam/pom.xml | 2 +- examples/powertools-examples-kafka/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- .../powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-common/pom.xml | 2 +- powertools-e2e-tests/handlers/batch/pom.xml | 2 +- .../handlers/idempotency-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/idempotency-generics/pom.xml | 2 +- powertools-e2e-tests/handlers/idempotency/pom.xml | 2 +- .../handlers/largemessage-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/largemessage/pom.xml | 2 +- .../handlers/largemessage_idempotent/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-log4j/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-logback/pom.xml | 2 +- powertools-e2e-tests/handlers/metrics/pom.xml | 2 +- powertools-e2e-tests/handlers/parameters/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/handlers/tracing/pom.xml | 2 +- powertools-e2e-tests/handlers/validation-alb-event/pom.xml | 2 +- .../handlers/validation-apigw-event/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-idempotency/powertools-idempotency-core/pom.xml | 2 +- .../powertools-idempotency-dynamodb/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-logging/powertools-logging-log4j/pom.xml | 2 +- powertools-logging/powertools-logging-logback/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- .../powertools-parameters-appconfig/pom.xml | 2 +- .../powertools-parameters-dynamodb/pom.xml | 2 +- powertools-parameters/powertools-parameters-secrets/pom.xml | 2 +- powertools-parameters/powertools-parameters-ssm/pom.xml | 2 +- powertools-parameters/powertools-parameters-tests/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 62 files changed, 69 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index e771db05c..6d3689092 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your software.amazon.lambda powertools-tracing - 2.6.0 + 2.7.0 software.amazon.lambda powertools-logging-log4j - 2.6.0 + 2.7.0 software.amazon.lambda powertools-metrics - 2.6.0 + 2.7.0 ... diff --git a/examples/pom.xml b/examples/pom.xml index 9ce451a33..c02d318cc 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ software.amazon.lambda powertools-examples - 2.6.0 + 2.7.0 pom Powertools for AWS Lambda (Java) - Examples diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 518cdbdd4..5226166fb 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ 4.0.0 software.amazon.lambda.examples - 2.6.0 + 2.7.0 powertools-examples-batch jar Powertools for AWS Lambda (Java) - Examples - Batch diff --git a/examples/powertools-examples-cloudformation/README.md b/examples/powertools-examples-cloudformation/README.md index 35184b6d2..84d9d7fac 100644 --- a/examples/powertools-examples-cloudformation/README.md +++ b/examples/powertools-examples-cloudformation/README.md @@ -15,7 +15,7 @@ Run the following in your shell: ```bash cd infra/sam sam build -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.6.0718 +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.7.0718 ``` ### Deploy with CDK @@ -32,5 +32,5 @@ To build and deploy this application for the first time, run the following in yo cd infra/cdk mvn package cdk synth -cdk deploy -c BucketNameParam=my-unique-bucket-2.6.0718 +cdk deploy -c BucketNameParam=my-unique-bucket-2.7.0718 ``` \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md index 01365932e..85c8b386f 100644 --- a/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md +++ b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md @@ -40,7 +40,7 @@ sam build ## Deploy the sample application ```shell -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.6.0718 +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.7.0718 ``` This sample is based on Serverless Application Model (SAM). To deploy it, check out the instructions for getting started with SAM in [the examples directory](../../../README.md) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 392d08b4b..74ced3a4f 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ 4.0.0 software.amazon.lambda.examples - 2.6.0 + 2.7.0 powertools-examples-cloudformation jar diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 756c082b8..dbd6743a8 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda.examples - 2.6.0 + 2.7.0 powertools-examples-core-utilities-cdk jar diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 4ca38877d..87dc26169 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -4,7 +4,7 @@ 4.0.0 software.amazon.lambda.examples cdk - 2.6.0 + 2.7.0 UTF-8 2.223.0 diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle index 36524f0b1..0b7951f11 100644 --- a/examples/powertools-examples-core-utilities/gradle/build.gradle +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -29,8 +29,8 @@ dependencies { implementation 'com.amazonaws:aws-lambda-java-events:3.16.0' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2' implementation 'org.aspectj:aspectjrt:1.9.20.1' - aspect 'software.amazon.lambda:powertools-tracing:2.6.0' - aspect 'software.amazon.lambda:powertools-logging-log4j:2.6.0' - aspect 'software.amazon.lambda:powertools-metrics:2.6.0' + aspect 'software.amazon.lambda:powertools-tracing:2.7.0' + aspect 'software.amazon.lambda:powertools-logging-log4j:2.7.0' + aspect 'software.amazon.lambda:powertools-metrics:2.7.0' } diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index c3713c344..47e82c5b8 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -15,9 +15,9 @@ dependencies { implementation("com.amazonaws:aws-lambda-java-events:3.16.0") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.2") implementation("org.aspectj:aspectjrt:1.9.20.1") - aspect("software.amazon.lambda:powertools-tracing:2.6.0") - aspect("software.amazon.lambda:powertools-logging-log4j:2.6.0") - aspect("software.amazon.lambda:powertools-metrics:2.6.0") + aspect("software.amazon.lambda:powertools-tracing:2.7.0") + aspect("software.amazon.lambda:powertools-logging-log4j:2.7.0") + aspect("software.amazon.lambda:powertools-metrics:2.7.0") implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.24") } diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index cfd640aa1..682e1a10b 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -4,7 +4,7 @@ Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM GraalVM software.amazon.lambda.examples - 2.6.0 + 2.7.0 powertools-examples-core-utilities-sam-graalvm jar diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index a5e3881b2..d03cafcfb 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -4,7 +4,7 @@ Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM software.amazon.lambda.examples - 2.6.0 + 2.7.0 powertools-examples-core-utilities-sam jar diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index 8ef3b556d..996f77d4b 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -4,7 +4,7 @@ Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Serverless software.amazon.lambda.examples - 2.6.0 + 2.7.0 powertools-examples-core-utilities-serverless jar diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index d320a1d40..d28835a8a 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -4,7 +4,7 @@ Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Terraform software.amazon.lambda.examples - 2.6.0 + 2.7.0 powertools-examples-core-utilities-terraform jar diff --git a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml index dff0082f1..205958c9a 100644 --- a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml +++ b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.6.0 + 2.7.0 powertools-examples-idempotency-sam-graalvm jar Powertools for AWS Lambda (Java) - Examples - Idempotency GraalVM diff --git a/examples/powertools-examples-idempotency/sam/pom.xml b/examples/powertools-examples-idempotency/sam/pom.xml index 0654867f9..694b79342 100644 --- a/examples/powertools-examples-idempotency/sam/pom.xml +++ b/examples/powertools-examples-idempotency/sam/pom.xml @@ -17,7 +17,7 @@ 4.0.0 software.amazon.lambda.examples - 2.6.0 + 2.7.0 powertools-examples-idempotency jar Powertools for AWS Lambda (Java) - Examples - Idempotency diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 5428655bd..184025be8 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.6.0 + 2.7.0 powertools-examples-kafka jar Powertools for AWS Lambda (Java) - Examples - Kafka diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 9c62498d5..a83ddddb8 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.6.0 + 2.7.0 powertools-examples-parameters-sam-graalvm jar Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index 17a1efa79..83efef4d2 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.6.0 + 2.7.0 powertools-examples-parameters-sam jar Powertools for AWS Lambda (Java) - Examples - Parameters diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index 7df5805ce..b77ce975e 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.6.0 + 2.7.0 powertools-examples-serialization-sam-graalvm jar Powertools for AWS Lambda (Java) - Examples - Serialization GraalVM diff --git a/examples/powertools-examples-serialization/sam/pom.xml b/examples/powertools-examples-serialization/sam/pom.xml index 7120d3a91..789d9969f 100644 --- a/examples/powertools-examples-serialization/sam/pom.xml +++ b/examples/powertools-examples-serialization/sam/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.6.0 + 2.7.0 powertools-examples-serialization-sam jar Powertools for AWS Lambda (Java) - Examples - Serialization diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 5826e7456..abcdd4e3a 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.6.0 + 2.7.0 powertools-examples-validation jar Powertools for AWS Lambda (Java) - Examples - Validation diff --git a/mkdocs.yml b/mkdocs.yml index 3914cfa1a..98f6945d7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -129,7 +129,7 @@ extra_javascript: extra: powertools: - version: 2.6.0 + version: 2.7.0 version: provider: mike default: latest diff --git a/pom.xml b/pom.xml index 4d5ff7248..48be39d47 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ software.amazon.lambda powertools-parent - 2.6.0 + 2.7.0 pom Powertools for AWS Lambda (Java) - Parent diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index ccf926d39..bce58ab58 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda powertools-parent - 2.6.0 + 2.7.0 A suite of utilities that makes batch message processing using AWS Lambda easier. diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 015179833..a3270563a 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 2.6.0 + 2.7.0 Powertools for AWS Lambda (Java) - Cloudformation diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 2610a8c4f..d4e9f6213 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 2.6.0 + 2.7.0 Powertools for AWS Lambda (Java) - Common Internal Utilities diff --git a/powertools-e2e-tests/handlers/batch/pom.xml b/powertools-e2e-tests/handlers/batch/pom.xml index a630ba09b..2c726340a 100644 --- a/powertools-e2e-tests/handlers/batch/pom.xml +++ b/powertools-e2e-tests/handlers/batch/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.6.0 + 2.7.0 e2e-test-handler-batch diff --git a/powertools-e2e-tests/handlers/idempotency-functional/pom.xml b/powertools-e2e-tests/handlers/idempotency-functional/pom.xml index 73c8780d3..133a0ccff 100644 --- a/powertools-e2e-tests/handlers/idempotency-functional/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency-functional/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.6.0 + 2.7.0 e2e-test-handler-idempotency-functional diff --git a/powertools-e2e-tests/handlers/idempotency-generics/pom.xml b/powertools-e2e-tests/handlers/idempotency-generics/pom.xml index d89aa33e2..a69babd0d 100644 --- a/powertools-e2e-tests/handlers/idempotency-generics/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency-generics/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.6.0 + 2.7.0 e2e-test-handler-idempotency-generics diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml index ea84e7b26..cbe7e0cac 100644 --- a/powertools-e2e-tests/handlers/idempotency/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.6.0 + 2.7.0 e2e-test-handler-idempotency diff --git a/powertools-e2e-tests/handlers/largemessage-functional/pom.xml b/powertools-e2e-tests/handlers/largemessage-functional/pom.xml index 094c54841..e2e67b2da 100644 --- a/powertools-e2e-tests/handlers/largemessage-functional/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage-functional/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.6.0 + 2.7.0 e2e-test-handler-largemessage-functional diff --git a/powertools-e2e-tests/handlers/largemessage/pom.xml b/powertools-e2e-tests/handlers/largemessage/pom.xml index 3004a884c..56d179c3b 100644 --- a/powertools-e2e-tests/handlers/largemessage/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.6.0 + 2.7.0 e2e-test-handler-largemessage diff --git a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml index 74a7b2999..9896db217 100644 --- a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.6.0 + 2.7.0 e2e-test-handler-large-msg-idempotent diff --git a/powertools-e2e-tests/handlers/logging-functional/pom.xml b/powertools-e2e-tests/handlers/logging-functional/pom.xml index a8f79df30..ba532c3db 100644 --- a/powertools-e2e-tests/handlers/logging-functional/pom.xml +++ b/powertools-e2e-tests/handlers/logging-functional/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.6.0 + 2.7.0 e2e-test-handler-logging-functional diff --git a/powertools-e2e-tests/handlers/logging-log4j/pom.xml b/powertools-e2e-tests/handlers/logging-log4j/pom.xml index 0ed761274..445da94e2 100644 --- a/powertools-e2e-tests/handlers/logging-log4j/pom.xml +++ b/powertools-e2e-tests/handlers/logging-log4j/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.6.0 + 2.7.0 e2e-test-handler-logging-log4j diff --git a/powertools-e2e-tests/handlers/logging-logback/pom.xml b/powertools-e2e-tests/handlers/logging-logback/pom.xml index d8523f721..9f5035722 100644 --- a/powertools-e2e-tests/handlers/logging-logback/pom.xml +++ b/powertools-e2e-tests/handlers/logging-logback/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.6.0 + 2.7.0 e2e-test-handler-logging-logback diff --git a/powertools-e2e-tests/handlers/metrics/pom.xml b/powertools-e2e-tests/handlers/metrics/pom.xml index 3b60d2aba..5bf3bd5ef 100644 --- a/powertools-e2e-tests/handlers/metrics/pom.xml +++ b/powertools-e2e-tests/handlers/metrics/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.6.0 + 2.7.0 e2e-test-handler-metrics diff --git a/powertools-e2e-tests/handlers/parameters/pom.xml b/powertools-e2e-tests/handlers/parameters/pom.xml index 495b51311..e30a51150 100644 --- a/powertools-e2e-tests/handlers/parameters/pom.xml +++ b/powertools-e2e-tests/handlers/parameters/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.6.0 + 2.7.0 e2e-test-handler-parameters diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index bf2e78f8c..afc21131f 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -4,7 +4,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.6.0 + 2.7.0 pom Handlers for End-to-End tests Fake handlers that use Powertools for AWS Lambda (Java). diff --git a/powertools-e2e-tests/handlers/tracing/pom.xml b/powertools-e2e-tests/handlers/tracing/pom.xml index 2aaae55f5..1a3b56a77 100644 --- a/powertools-e2e-tests/handlers/tracing/pom.xml +++ b/powertools-e2e-tests/handlers/tracing/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.6.0 + 2.7.0 e2e-test-handler-tracing diff --git a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml index c966194b1..d2e1266fc 100644 --- a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.6.0 + 2.7.0 e2e-test-handler-validation-alb-event diff --git a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml index 85b8f47dc..6832280a6 100644 --- a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.6.0 + 2.7.0 e2e-test-handler-validation-apigw-event diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 860593819..998c6a803 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ powertools-parent software.amazon.lambda - 2.6.0 + 2.7.0 powertools-e2e-tests diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index 7bfe38ef4..f119ca445 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ software.amazon.lambda powertools-parent - 2.6.0 + 2.7.0 powertools-idempotency diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index 0e784c0f5..5e57ee136 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -21,7 +21,7 @@ software.amazon.lambda powertools-idempotency - 2.6.0 + 2.7.0 powertools-idempotency-core diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index ea562ea89..25f6f77c7 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -21,7 +21,7 @@ software.amazon.lambda powertools-idempotency - 2.6.0 + 2.7.0 powertools-idempotency-dynamodb diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index ac8c95f05..a07d4e0d6 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -21,7 +21,7 @@ powertools-parent software.amazon.lambda - 2.6.0 + 2.7.0 powertools-kafka diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index e29327c7d..6353cb089 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ software.amazon.lambda powertools-parent - 2.6.0 + 2.7.0 powertools-large-messages diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index bcc28c6c2..9fd7b1e62 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -21,7 +21,7 @@ powertools-parent software.amazon.lambda - 2.6.0 + 2.7.0 Powertools for AWS Lambda (Java) - Logging diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index fc47ac84d..8006baa7d 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 2.6.0 + 2.7.0 ../../pom.xml diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 2269303f5..70bbbdfc4 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -6,7 +6,7 @@ powertools-parent software.amazon.lambda - 2.6.0 + 2.7.0 ../../pom.xml diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index ae2871b41..1ea59493c 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 2.6.0 + 2.7.0 Powertools for AWS Lambda (Java) - Metrics diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 72d3133d3..158fdc978 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ powertools-parent software.amazon.lambda - 2.6.0 + 2.7.0 powertools-parameters diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index 454102ede..9d9adc16b 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 2.6.0 + 2.7.0 ../../pom.xml diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index 594761bee..d66153da6 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 2.6.0 + 2.7.0 ../../pom.xml diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index 4b4616cb2..f126716d6 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 2.6.0 + 2.7.0 ../../pom.xml diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index 0e31ed729..e6bf1ea27 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 2.6.0 + 2.7.0 ../../pom.xml diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index b1b655fe8..b40046bde 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda powertools-parent - 2.6.0 + 2.7.0 ../../pom.xml diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 79b6653fa..d7f33df28 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ powertools-parent software.amazon.lambda - 2.6.0 + 2.7.0 powertools-serialization diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 3b1e58ccd..68231dbe1 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 2.6.0 + 2.7.0 Powertools for AWS Lambda (Java) - Tracing diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 2c119972d..74051989e 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 2.6.0 + 2.7.0 Powertools for AWS Lambda (Java) - Validation From 286a2d06d510e0799511b1c20340071b86979295 Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Thu, 13 Nov 2025 17:22:33 +0100 Subject: [PATCH 34/76] =?UTF-8?q?improv(parameters):=20Make=20parameters?= =?UTF-8?q?=20top-level=20provider=20thread-safe=20fo=E2=80=A6=20(#2284)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * improv(parameters): Make parameters top-level provider thread-safe for cache and transformer management. * Use countdown latch instead of Thread.sleep for transformation manager tests. * Make SSMProvider and AppConfigProvider thread-safe. * Make all tests package-private. * Fix sonar finding AZp41z3q7Kei0U644EOS. * Fix sonar finding AZp41z3q7Kei0U644EOS for real now. * Fix PMD findings. * Fix PMD findings. * Fix PMD findings. --- .../appconfig/AppConfigProvider.java | 4 +- .../appconfig/AppConfigParamAspectTest.java | 4 +- .../dynamodb/DynamoDbParamAspectTest.java | 4 +- .../dynamodb/DynamoDbProviderE2ETest.java | 26 ++-- .../dynamodb/DynamoDbProviderTest.java | 44 +++--- .../secrets/SecretsParamAspectTest.java | 4 +- .../secrets/SecretsProviderTest.java | 14 +- .../parameters/ssm/SSMProvider.java | 30 ++-- .../parameters/ssm/SSMParamAspectTest.java | 4 +- .../parameters/ssm/SSMProviderTest.java | 132 ++++++++++++++--- .../parameters/BaseProviderTest.java | 62 ++++---- .../ParamProvidersIntegrationTest.java | 8 +- .../parameters/cache/CacheManagerTest.java | 110 ++++++++++++-- .../parameters/cache/DataStoreTest.java | 14 +- .../transform/Base64TransformerTest.java | 6 +- .../transform/JsonTransformerTest.java | 10 +- .../transform/TransformationManagerTest.java | 140 ++++++++++++++++-- .../powertools/parameters/BaseProvider.java | 31 ++-- .../parameters/cache/CacheManager.java | 23 ++- .../parameters/cache/DataStore.java | 13 +- .../transform/TransformationManager.java | 43 ++++-- 21 files changed, 535 insertions(+), 191 deletions(-) diff --git a/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProvider.java b/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProvider.java index 37f07ae7a..06d00ffbe 100644 --- a/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProvider.java +++ b/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProvider.java @@ -14,8 +14,8 @@ package software.amazon.lambda.powertools.parameters.appconfig; -import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import software.amazon.awssdk.core.SdkBytes; import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient; @@ -46,7 +46,7 @@ public class AppConfigProvider extends BaseProvider { private final AppConfigDataClient client; private final String application; private final String environment; - private final Map establishedSessions = new HashMap<>(); + private final Map establishedSessions = new ConcurrentHashMap<>(); AppConfigProvider(CacheManager cacheManager, TransformationManager transformationManager, AppConfigDataClient client, String environment, String application) { diff --git a/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParamAspectTest.java b/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParamAspectTest.java index df3191632..a32cc20a5 100644 --- a/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParamAspectTest.java +++ b/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParamAspectTest.java @@ -22,10 +22,10 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; -public class AppConfigParamAspectTest { +class AppConfigParamAspectTest { @Test - public void parameterInjectedByProvider() throws Exception { + void parameterInjectedByProvider() throws Exception { // Setup our aspect to return a mocked AppConfigProvider String environment = "myEnvironment"; String appName = "myApp"; diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspectTest.java b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspectTest.java index 07e93a7c1..4294eca48 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspectTest.java +++ b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspectTest.java @@ -21,10 +21,10 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; -public class DynamoDbParamAspectTest { +class DynamoDbParamAspectTest { @Test - public void parameterInjectedByProvider() throws Exception { + void parameterInjectedByProvider() throws Exception { // Setup our aspect to return a mocked DynamoDbProvider String tableName = "my-test-tablename"; String key = "myKey"; diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderE2ETest.java b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderE2ETest.java index 2695938d8..af2617edf 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderE2ETest.java +++ b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderE2ETest.java @@ -36,10 +36,10 @@ * will move this across. */ @Disabled -public class DynamoDbProviderE2ETest { +class DynamoDbProviderE2ETest { - final String ParamsTestTable = "ddb-params-test"; - final String MultiparamsTestTable = "ddb-multiparams-test"; + private static final String PARAMS_TEST_TABLE = "ddb-params-test"; + private static final String MULTI_PARAMS_TEST_TABLE = "ddb-multiparams-test"; private final DynamoDbClient ddbClient; public DynamoDbProviderE2ETest() { @@ -52,19 +52,19 @@ public DynamoDbProviderE2ETest() { } @Test - public void TestGetValue() { + void TestGetValue() { // Arrange - HashMap testItem = new HashMap(); + Map testItem = new HashMap<>(); testItem.put("id", AttributeValue.fromS("test_param")); testItem.put("value", AttributeValue.fromS("the_value_is_hello!")); ddbClient.putItem(PutItemRequest.builder() - .tableName(ParamsTestTable) + .tableName(PARAMS_TEST_TABLE) .item(testItem) .build()); // Act - DynamoDbProvider provider = makeProvider(ParamsTestTable); + DynamoDbProvider provider = makeProvider(PARAMS_TEST_TABLE); String value = provider.getValue("test_param"); // Assert @@ -72,29 +72,29 @@ public void TestGetValue() { } @Test - public void TestGetValues() { + void TestGetValues() { // Arrange - HashMap testItem = new HashMap(); + Map testItem = new HashMap<>(); testItem.put("id", AttributeValue.fromS("test_param")); testItem.put("sk", AttributeValue.fromS("test_param_part_1")); testItem.put("value", AttributeValue.fromS("the_value_is_hello!")); ddbClient.putItem(PutItemRequest.builder() - .tableName(MultiparamsTestTable) + .tableName(MULTI_PARAMS_TEST_TABLE) .item(testItem) .build()); - HashMap testItem2 = new HashMap(); + Map testItem2 = new HashMap<>(); testItem2.put("id", AttributeValue.fromS("test_param")); testItem2.put("sk", AttributeValue.fromS("test_param_part_2")); testItem2.put("value", AttributeValue.fromS("the_value_is_still_hello!")); ddbClient.putItem(PutItemRequest.builder() - .tableName(MultiparamsTestTable) + .tableName(MULTI_PARAMS_TEST_TABLE) .item(testItem2) .build()); // Act - DynamoDbProvider provider = makeProvider(MultiparamsTestTable); + DynamoDbProvider provider = makeProvider(MULTI_PARAMS_TEST_TABLE); Map values = provider.getMultipleValues("test_param"); // Assert diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java index 68d48b01c..64f29db79 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java +++ b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java @@ -45,9 +45,9 @@ import software.amazon.lambda.powertools.parameters.transform.TransformationManager; @ExtendWith(MockitoExtension.class) -public class DynamoDbProviderTest { +class DynamoDbProviderTest { - private final String tableName = "ddb-test-table"; + private static final String TABLE_NAME = "ddb-test-table"; @Mock DynamoDbClient client; @@ -64,19 +64,19 @@ public class DynamoDbProviderTest { private DynamoDbProvider provider; @BeforeEach - public void init() { + void init() { openMocks(this); CacheManager cacheManager = new CacheManager(); - provider = new DynamoDbProvider(cacheManager, transformationManager, client, tableName); + provider = new DynamoDbProvider(cacheManager, transformationManager, client, TABLE_NAME); } @Test - public void getValue() { + void getValue() { // Arrange String key = "Key1"; String expectedValue = "Value1"; - HashMap responseData = new HashMap(); + Map responseData = new HashMap<>(); responseData.put("id", AttributeValue.fromS(key)); responseData.put("value", AttributeValue.fromS(expectedValue)); GetItemResponse response = GetItemResponse.builder() @@ -89,12 +89,12 @@ public void getValue() { // Assert assertThat(value).isEqualTo(expectedValue); - assertThat(getItemValueCaptor.getValue().tableName()).isEqualTo(tableName); + assertThat(getItemValueCaptor.getValue().tableName()).isEqualTo(TABLE_NAME); assertThat(getItemValueCaptor.getValue().key().get("id").s()).isEqualTo(key); } @Test - public void getValueWithNullResultsReturnsNull() { + void getValueWithNullResultsReturnsNull() { // Arrange Mockito.when(client.getItem(getItemValueCaptor.capture())).thenReturn(GetItemResponse.builder() .item(null) @@ -108,7 +108,7 @@ public void getValueWithNullResultsReturnsNull() { } @Test - public void getValueWithoutResultsReturnsNull() { + void getValueWithoutResultsReturnsNull() { // Arrange Mockito.when(client.getItem(getItemValueCaptor.capture())).thenReturn(GetItemResponse.builder() .item(new HashMap<>()) @@ -122,10 +122,10 @@ public void getValueWithoutResultsReturnsNull() { } @Test - public void getValueWithMalformedRowThrows() { + void getValueWithMalformedRowThrows() { // Arrange String key = "Key1"; - HashMap responseData = new HashMap(); + Map responseData = new HashMap<>(); responseData.put("id", AttributeValue.fromS(key)); responseData.put("not-value", AttributeValue.fromS("something")); Mockito.when(client.getItem(getItemValueCaptor.capture())).thenReturn(GetItemResponse.builder() @@ -138,7 +138,7 @@ public void getValueWithMalformedRowThrows() { } @Test - public void getValues() { + void getValues() { // Arrange String key = "Key1"; @@ -146,11 +146,11 @@ public void getValues() { String val1 = "Val1"; String subkey2 = "Subkey2"; String val2 = "Val2"; - HashMap item1 = new HashMap(); + Map item1 = new HashMap<>(); item1.put("id", AttributeValue.fromS(key)); item1.put("sk", AttributeValue.fromS(subkey1)); item1.put("value", AttributeValue.fromS(val1)); - HashMap item2 = new HashMap(); + Map item2 = new HashMap<>(); item2.put("id", AttributeValue.fromS(key)); item2.put("sk", AttributeValue.fromS(subkey2)); item2.put("value", AttributeValue.fromS(val2)); @@ -166,13 +166,13 @@ public void getValues() { assertThat(values.size()).isEqualTo(2); assertThat(values.get(subkey1)).isEqualTo(val1); assertThat(values.get(subkey2)).isEqualTo(val2); - assertThat(queryRequestCaptor.getValue().tableName()).isEqualTo(tableName); + assertThat(queryRequestCaptor.getValue().tableName()).isEqualTo(TABLE_NAME); assertThat(queryRequestCaptor.getValue().keyConditionExpression()).isEqualTo("id = :v_id"); assertThat(queryRequestCaptor.getValue().expressionAttributeValues().get(":v_id").s()).isEqualTo(key); } @Test - public void getValuesWithoutResultsReturnsNull() { + void getValuesWithoutResultsReturnsNull() { // Arrange Mockito.when(client.query(queryRequestCaptor.capture())).thenReturn( QueryResponse.builder().items().build()); @@ -185,10 +185,10 @@ public void getValuesWithoutResultsReturnsNull() { } @Test - public void getMultipleValuesMissingSortKey_throwsException() { + void getMultipleValuesMissingSortKey_throwsException() { // Arrange String key = "Key1"; - HashMap item = new HashMap(); + Map item = new HashMap<>(); item.put("id", AttributeValue.fromS(key)); item.put("value", AttributeValue.fromS("somevalue")); QueryResponse response = QueryResponse.builder() @@ -204,10 +204,10 @@ public void getMultipleValuesMissingSortKey_throwsException() { } @Test - public void getValuesWithMalformedRowThrows() { + void getValuesWithMalformedRowThrows() { // Arrange String key = "Key1"; - HashMap item1 = new HashMap(); + Map item1 = new HashMap<>(); item1.put("id", AttributeValue.fromS(key)); item1.put("sk", AttributeValue.fromS("some-subkey")); item1.put("not-value", AttributeValue.fromS("somevalue")); @@ -224,7 +224,7 @@ public void getValuesWithMalformedRowThrows() { } @Test - public void testDynamoDBBuilderMissingTable_throwsException() { + void testDynamoDBBuilderMissingTable_throwsException() { // Act & Assert assertThatIllegalStateException().isThrownBy(() -> DynamoDbProvider.builder() @@ -233,7 +233,7 @@ public void testDynamoDBBuilderMissingTable_throwsException() { } @Test - public void testDynamoDBBuilder_withoutParameter_shouldHaveDefaultTransformationManager() { + void testDynamoDBBuilder_withoutParameter_shouldHaveDefaultTransformationManager() { // Act DynamoDbProvider dynamoDbProvider = DynamoDbProvider.builder().withTable("test-table") diff --git a/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspectTest.java b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspectTest.java index 7aa0f0872..5523cbb0e 100644 --- a/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspectTest.java +++ b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspectTest.java @@ -21,10 +21,10 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; -public class SecretsParamAspectTest { +class SecretsParamAspectTest { @Test - public void parameterInjectedByProvider() throws Exception { + void parameterInjectedByProvider() throws Exception { // Setup our aspect to return a mocked SecretsProvider String key = "myKey"; String value = "mySecretValue"; diff --git a/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java index d0b32874a..a6fbe1c8e 100644 --- a/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java +++ b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java @@ -40,7 +40,7 @@ import software.amazon.lambda.powertools.parameters.transform.TransformationManager; @ExtendWith(MockitoExtension.class) -public class SecretsProviderTest { +class SecretsProviderTest { @Mock SecretsManagerClient client; @@ -56,13 +56,13 @@ public class SecretsProviderTest { SecretsProvider provider; @BeforeEach - public void init() { + void init() { cacheManager = new CacheManager(); provider = new SecretsProvider(cacheManager, transformationManager, client); } @Test - public void getValue() { + void getValue() { String key = "Key1"; String expectedValue = "Value1"; GetSecretValueResponse response = GetSecretValueResponse.builder().secretString(expectedValue).build(); @@ -76,7 +76,7 @@ public void getValue() { } @Test - public void getValueBase64() { + void getValueBase64() { String key = "Key2"; String expectedValue = "Value2"; byte[] valueb64 = Base64.getEncoder().encode(expectedValue.getBytes()); @@ -91,14 +91,14 @@ public void getValueBase64() { } @Test - public void getMultipleValuesThrowsException() { + void getMultipleValuesThrowsException() { // Act & Assert assertThatRuntimeException().isThrownBy(() -> provider.getMultipleValues("path")) .withMessage("Impossible to get multiple values from AWS Secrets Manager"); } @Test - public void testGetSecretsProvider_withoutParameter_shouldCreateDefaultClient() { + void testGetSecretsProvider_withoutParameter_shouldCreateDefaultClient() { // Act SecretsProvider secretsProvider = SecretsProvider.builder() .build(); @@ -109,7 +109,7 @@ public void testGetSecretsProvider_withoutParameter_shouldCreateDefaultClient() } @Test - public void testGetSecretsProvider_withoutParameter_shouldHaveDefaultTransformationManager() { + void testGetSecretsProvider_withoutParameter_shouldHaveDefaultTransformationManager() { // Act SecretsProvider secretsProvider = SecretsProvider.builder() .build(); diff --git a/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProvider.java b/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProvider.java index c24b08b84..3cf728219 100644 --- a/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProvider.java +++ b/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProvider.java @@ -66,8 +66,8 @@ public class SSMProvider extends BaseProvider { private final SsmClient client; - private boolean decrypt = false; - private boolean recursive = false; + private final ThreadLocal decrypt = ThreadLocal.withInitial(() -> false); + private final ThreadLocal recursive = ThreadLocal.withInitial(() -> false); /** * Constructor with custom {@link SsmClient}.
@@ -109,7 +109,7 @@ public static SSMProvider create() { public String getValue(String key) { GetParameterRequest request = GetParameterRequest.builder() .name(key) - .withDecryption(decrypt) + .withDecryption(decrypt.get()) .build(); return client.getParameter(request).parameter().value(); } @@ -122,7 +122,7 @@ public String getValue(String key) { * @return the provider itself in order to chain calls (eg.
provider.withDecryption().get("key")
). */ public SSMProvider withDecryption() { - this.decrypt = true; + this.decrypt.set(true); return this; } @@ -133,7 +133,7 @@ public SSMProvider withDecryption() { * @return the provider itself in order to chain calls (eg.
provider.recursive().getMultiple("key")
). */ public SSMProvider recursive() { - this.recursive = true; + this.recursive.set(true); return this; } @@ -160,8 +160,8 @@ protected Map getMultipleValues(String path) { private Map getMultipleBis(String path, String nextToken) { GetParametersByPathRequest request = GetParametersByPathRequest.builder() .path(path) - .withDecryption(decrypt) - .recursive(recursive) + .withDecryption(decrypt.get()) + .recursive(recursive.get()) .nextToken(nextToken) .build(); @@ -170,12 +170,12 @@ private Map getMultipleBis(String path, String nextToken) { // not using the client.getParametersByPathPaginator() as hardly testable GetParametersByPathResponse res = client.getParametersByPath(request); if (res.hasParameters()) { - res.parameters().forEach(parameter -> - { - /* Standardize the parameter name - The parameter name returned by SSM will contain the full path. - However, for readability, we should return only the part after - the path. + res.parameters().forEach(parameter -> { + /* + * Standardize the parameter name + * The parameter name returned by SSM will contain the full path. + * However, for readability, we should return only the part after + * the path. */ String name = parameter.name(); if (name.startsWith(path)) { @@ -196,8 +196,8 @@ private Map getMultipleBis(String path, String nextToken) { @Override protected void resetToDefaults() { super.resetToDefaults(); - recursive = false; - decrypt = false; + decrypt.remove(); + recursive.remove(); } // For tests purpose only diff --git a/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspectTest.java b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspectTest.java index e56d20ffa..abfc1d7fa 100644 --- a/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspectTest.java +++ b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspectTest.java @@ -21,13 +21,13 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; -public class SSMParamAspectTest { +class SSMParamAspectTest { // This class tests the SSM Param aspect in the same fashion // as the tests for the aspects for the other providers. @Test - public void parameterInjectedByProvider() throws Exception { + void parameterInjectedByProvider() throws Exception { String key = "myKey"; String value = "mySecretValue"; diff --git a/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java index db45dc21c..fb475a737 100644 --- a/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java +++ b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java @@ -23,6 +23,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.CountDownLatch; + import org.assertj.core.data.MapEntry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -32,6 +34,7 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; + import software.amazon.awssdk.services.ssm.SsmClient; import software.amazon.awssdk.services.ssm.model.GetParameterRequest; import software.amazon.awssdk.services.ssm.model.GetParameterResponse; @@ -41,7 +44,7 @@ import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -public class SSMProviderTest { +class SSMProviderTest { @Mock SsmClient client; @@ -60,14 +63,14 @@ public class SSMProviderTest { SSMProvider provider; @BeforeEach - public void init() { + void init() { MockitoAnnotations.openMocks(this); cacheManager = new CacheManager(); provider = new SSMProvider(cacheManager, null, client); } @Test - public void getValue() { + void getValue() { String key = "Key1"; String expectedValue = "Value1"; initMock(expectedValue); @@ -80,7 +83,7 @@ public void getValue() { } @Test - public void getValueDecrypted() { + void getValueDecrypted() { String key = "Key2"; String expectedValue = "Value2"; initMock(expectedValue); @@ -93,7 +96,7 @@ public void getValueDecrypted() { } @Test - public void getMultiple() { + void getMultiple() { List parameters = new ArrayList<>(); parameters.add(Parameter.builder().name("/prod/app1/key1").value("foo1").build()); parameters.add(Parameter.builder().name("/prod/app1/key2").value("foo2").build()); @@ -116,7 +119,7 @@ public void getMultiple() { } @Test - public void getMultipleWithTrailingSlash() { + void getMultipleWithTrailingSlash() { List parameters = new ArrayList<>(); parameters.add(Parameter.builder().name("/prod/app1/key1").value("foo1").build()); parameters.add(Parameter.builder().name("/prod/app1/key2").value("foo2").build()); @@ -139,7 +142,7 @@ public void getMultipleWithTrailingSlash() { } @Test - public void getMultiple_cached_shouldNotCallSSM() { + void getMultiple_cached_shouldNotCallSSM() { List parameters = new ArrayList<>(); parameters.add(Parameter.builder().name("/prod/app1/key1").value("foo1").build()); parameters.add(Parameter.builder().name("/prod/app1/key2").value("foo2").build()); @@ -161,12 +164,12 @@ public void getMultiple_cached_shouldNotCallSSM() { } @Test - public void getMultipleWithNextToken() { + void getMultipleWithNextToken() { List parameters1 = new ArrayList<>(); parameters1.add(Parameter.builder().name("/prod/app1/key1").value("foo1").build()); parameters1.add(Parameter.builder().name("/prod/app1/key2").value("foo2").build()); - GetParametersByPathResponse response1 = - GetParametersByPathResponse.builder().parameters(parameters1).nextToken("123abc").build(); + GetParametersByPathResponse response1 = GetParametersByPathResponse.builder().parameters(parameters1) + .nextToken("123abc").build(); List parameters2 = new ArrayList<>(); parameters2.add(Parameter.builder().name("/prod/app1/key3").value("foo3").build()); @@ -185,25 +188,118 @@ public void getMultipleWithNextToken() { GetParametersByPathRequest request1 = requestParams.get(0); GetParametersByPathRequest request2 = requestParams.get(1); - assertThat(asList(request1, request2)).allSatisfy(req -> - { - assertThat(req.path()).isEqualTo("/prod/app1"); - assertThat(req.withDecryption()).isFalse(); - assertThat(req.recursive()).isFalse(); - }); + assertThat(asList(request1, request2)) + .isNotEmpty() + .allSatisfy(req -> { + assertThat(req.path()).isEqualTo("/prod/app1"); + assertThat(req.withDecryption()).isFalse(); + assertThat(req.recursive()).isFalse(); + }); assertThat(request1.nextToken()).isNull(); assertThat(request2.nextToken()).isEqualTo("123abc"); } @Test - public void testSSMProvider_withoutParameter_shouldHaveDefaultTransformationManager() { + void testSSMProvider_withoutParameter_shouldHaveDefaultTransformationManager() { // Act SSMProvider ssmProvider = SSMProvider.builder() .build(); // Assert - assertDoesNotThrow(()->ssmProvider.withTransformation(json)); + assertDoesNotThrow(() -> ssmProvider.withTransformation(json)); + } + + @Test + void withDecryption_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { + // GIVEN + Parameter param1 = Parameter.builder().value("value1").build(); + Parameter param2 = Parameter.builder().value("value2").build(); + GetParameterResponse response1 = GetParameterResponse.builder().parameter(param1).build(); + GetParameterResponse response2 = GetParameterResponse.builder().parameter(param2).build(); + CountDownLatch latch = new CountDownLatch(2); + Mockito.when(client.getParameter(paramCaptor.capture())) + .thenReturn(response1, response2); + + // WHEN + Thread thread1 = new Thread(() -> { + try { + latch.countDown(); + latch.await(); + provider.withDecryption().getValue("key1"); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }); + + Thread thread2 = new Thread(() -> { + try { + latch.countDown(); + latch.await(); + provider.getValue("key2"); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }); + + thread1.start(); + thread2.start(); + thread1.join(); + thread2.join(); + + // THEN + List requests = paramCaptor.getAllValues(); + assertThat(requests) + .hasSize(2) + .anyMatch(GetParameterRequest::withDecryption) + .anyMatch(r -> !r.withDecryption()); + } + + @Test + void recursive_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { + // GIVEN + List params1 = new ArrayList<>(); + params1.add(Parameter.builder().name("/path1/key1").value("value1").build()); + List params2 = new ArrayList<>(); + params2.add(Parameter.builder().name("/path2/key2").value("value2").build()); + GetParametersByPathResponse response1 = GetParametersByPathResponse.builder().parameters(params1).build(); + GetParametersByPathResponse response2 = GetParametersByPathResponse.builder().parameters(params2).build(); + CountDownLatch latch = new CountDownLatch(2); + Mockito.when(client.getParametersByPath(paramByPathCaptor.capture())) + .thenReturn(response1, response2); + + // WHEN + Thread thread1 = new Thread(() -> { + try { + latch.countDown(); + latch.await(); + provider.recursive().getMultiple("/path1"); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }); + + Thread thread2 = new Thread(() -> { + try { + latch.countDown(); + latch.await(); + provider.getMultiple("/path2"); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }); + + thread1.start(); + thread2.start(); + thread1.join(); + thread2.join(); + + // THEN + List requests = paramByPathCaptor.getAllValues(); + assertThat(requests) + .hasSize(2) + .anyMatch(GetParametersByPathRequest::recursive) + .anyMatch(r -> !r.recursive()); } private void initMock(String expectedValue) { diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java index cbc8f5b30..dd31ce016 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java @@ -38,7 +38,7 @@ import software.amazon.lambda.powertools.parameters.transform.TransformationManager; import software.amazon.lambda.powertools.parameters.transform.Transformer; -public class BaseProviderTest { +class BaseProviderTest { Clock clock; CacheManager cacheManager; @@ -47,7 +47,7 @@ public class BaseProviderTest { boolean getFromStore = false; @BeforeEach - public void setup() { + void setup() { clock = Clock.systemDefaultZone(); cacheManager = new CacheManager(); transformationManager = new TransformationManager(); @@ -55,7 +55,7 @@ public void setup() { } @Test - public void get_notCached_shouldGetValue() { + void get_notCached_shouldGetValue() { String foo = provider.get("toto"); assertThat(foo).isEqualTo("valueFromStore"); @@ -63,7 +63,7 @@ public void get_notCached_shouldGetValue() { } @Test - public void get_cached_shouldGetFromCache() { + void get_cached_shouldGetFromCache() { provider.get("foo"); getFromStore = false; @@ -73,7 +73,7 @@ public void get_cached_shouldGetFromCache() { } @Test - public void get_expired_shouldGetValue() { + void get_expired_shouldGetValue() { provider.get("bar"); getFromStore = false; @@ -84,7 +84,7 @@ public void get_expired_shouldGetValue() { } @Test - public void getMultiple_notCached_shouldGetValue() { + void getMultiple_notCached_shouldGetValue() { Map foo = provider.getMultiple("toto"); assertThat(foo.get("toto")).isEqualTo("valueFromStore"); @@ -92,7 +92,7 @@ public void getMultiple_notCached_shouldGetValue() { } @Test - public void getMultiple_cached_shouldGetFromCache() { + void getMultiple_cached_shouldGetFromCache() { provider.getMultiple("foo"); getFromStore = false; @@ -102,7 +102,7 @@ public void getMultiple_cached_shouldGetFromCache() { } @Test - public void getMultiple_expired_shouldGetValue() { + void getMultiple_expired_shouldGetValue() { provider.getMultiple("bar"); getFromStore = false; @@ -113,7 +113,7 @@ public void getMultiple_expired_shouldGetValue() { } @Test - public void get_customTTL_cached_shouldGetFromCache() { + void get_customTTL_cached_shouldGetFromCache() { provider.withMaxAge(12, ChronoUnit.MINUTES).get("key"); getFromStore = false; @@ -124,7 +124,7 @@ public void get_customTTL_cached_shouldGetFromCache() { } @Test - public void get_customTTL_expired_shouldGetValue() { + void get_customTTL_expired_shouldGetValue() { provider.withMaxAge(2, ChronoUnit.MINUTES).get("mykey"); getFromStore = false; @@ -135,7 +135,7 @@ public void get_customTTL_expired_shouldGetValue() { } @Test - public void get_customDefaultTTL_cached_shouldGetFromCache() { + void get_customDefaultTTL_cached_shouldGetFromCache() { provider.cacheManager.setDefaultExpirationTime(Duration.of(12, MINUTES)); provider.get("foobar"); getFromStore = false; @@ -147,7 +147,7 @@ public void get_customDefaultTTL_cached_shouldGetFromCache() { } @Test - public void get_customDefaultTTL_expired_shouldGetValue() { + void get_customDefaultTTL_expired_shouldGetValue() { provider.cacheManager.setDefaultExpirationTime(Duration.of(2, MINUTES)); getFromStore = false; @@ -158,7 +158,7 @@ public void get_customDefaultTTL_expired_shouldGetValue() { } @Test - public void get_customDefaultTTLAndTTL_cached_shouldGetFromCache() { + void get_customDefaultTTLAndTTL_cached_shouldGetFromCache() { provider.get("foobaz"); getFromStore = false; @@ -169,7 +169,7 @@ public void get_customDefaultTTLAndTTL_cached_shouldGetFromCache() { } @Test - public void get_customDefaultTTLAndTTL_expired_shouldGetValue() { + void get_customDefaultTTLAndTTL_expired_shouldGetValue() { provider.cacheManager.setDefaultExpirationTime(Duration.ofMinutes(2)); @@ -183,7 +183,7 @@ public void get_customDefaultTTLAndTTL_expired_shouldGetValue() { } @Test - public void get_basicTransformation_shouldTransformInString() { + void get_basicTransformation_shouldTransformInString() { provider.setValue(Base64.getEncoder().encodeToString("bar".getBytes())); String value = provider.withTransformation(Transformer.base64).get("base64"); @@ -192,20 +192,20 @@ public void get_basicTransformation_shouldTransformInString() { } @Test - public void get_complexTransformation_shouldTransformInObject() { + void get_complexTransformation_shouldTransformInObject() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); ObjectToDeserialize objectToDeserialize = provider.withTransformation(json).get("foo", ObjectToDeserialize.class); assertThat(objectToDeserialize).matches( - o -> o.getFoo().equals("Foo") + o -> "Foo".equals(o.getFoo()) && o.getBar() == 42 && o.getBaz() == 123456789); } @Test - public void getObject_notCached_shouldGetValue() { + void getObject_notCached_shouldGetValue() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); ObjectToDeserialize foo = provider.withTransformation(json).get("foo", ObjectToDeserialize.class); @@ -215,7 +215,7 @@ public void getObject_notCached_shouldGetValue() { } @Test - public void getObject_cached_shouldGetFromCache() { + void getObject_cached_shouldGetFromCache() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); provider.withTransformation(json).get("foo", ObjectToDeserialize.class); @@ -227,7 +227,7 @@ public void getObject_cached_shouldGetFromCache() { } @Test - public void getObject_expired_shouldGetValue() { + void getObject_expired_shouldGetValue() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); provider.withTransformation(json).get("foo", ObjectToDeserialize.class); @@ -240,7 +240,7 @@ public void getObject_expired_shouldGetValue() { } @Test - public void getObject_customTTL_cached_shouldGetFromCache() { + void getObject_customTTL_cached_shouldGetFromCache() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); provider.withMaxAge(12, ChronoUnit.MINUTES) @@ -255,7 +255,7 @@ public void getObject_customTTL_cached_shouldGetFromCache() { } @Test - public void getObject_customTTL_expired_shouldGetValue() { + void getObject_customTTL_expired_shouldGetValue() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); provider.withMaxAge(2, ChronoUnit.MINUTES) @@ -270,7 +270,7 @@ public void getObject_customTTL_expired_shouldGetValue() { } @Test - public void getObject_customDefaultTTL_cached_shouldGetFromCache() { + void getObject_customDefaultTTL_cached_shouldGetFromCache() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); provider.cacheManager.setDefaultExpirationTime(Duration.of(12, MINUTES)); @@ -286,7 +286,7 @@ public void getObject_customDefaultTTL_cached_shouldGetFromCache() { } @Test - public void getObject_customDefaultTTL_expired_shouldGetValue() { + void getObject_customDefaultTTL_expired_shouldGetValue() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); provider.cacheManager.setDefaultExpirationTime(Duration.of(2, MINUTES)); @@ -302,7 +302,7 @@ public void getObject_customDefaultTTL_expired_shouldGetValue() { } @Test - public void getObject_customDefaultTTLAndTTL_cached_shouldGetFromCache() { + void getObject_customDefaultTTLAndTTL_cached_shouldGetFromCache() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); provider.cacheManager.setDefaultExpirationTime(Duration.ofSeconds(5)); @@ -319,7 +319,7 @@ public void getObject_customDefaultTTLAndTTL_cached_shouldGetFromCache() { } @Test - public void getObject_customDefaultTTLAndTTL_expired_shouldGetValue() { + void getObject_customDefaultTTLAndTTL_expired_shouldGetValue() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); provider.cacheManager.setDefaultExpirationTime(Duration.ofMinutes(2)); @@ -336,7 +336,7 @@ public void getObject_customDefaultTTLAndTTL_expired_shouldGetValue() { } @Test - public void get_noTransformationManager_shouldThrowException() { + void get_noTransformationManager_shouldThrowException() { provider = new BasicProvider(new CacheManager(), null); assertThatIllegalStateException() @@ -344,14 +344,14 @@ public void get_noTransformationManager_shouldThrowException() { } @Test - public void getObject_noTransformationManager_shouldThrowException() { + void getObject_noTransformationManager_shouldThrowException() { assertThatIllegalStateException() .isThrownBy(() -> provider.get("foo", ObjectToDeserialize.class)); } @Test - public void getTwoParams_shouldResetTTLOptionsInBetween() { + void getTwoParams_shouldResetTTLOptionsInBetween() { provider.withMaxAge(50, SECONDS).get("foo50"); provider.get("foo5"); @@ -365,7 +365,7 @@ public void getTwoParams_shouldResetTTLOptionsInBetween() { } @Test - public void getTwoParams_shouldResetTransformationOptionsInBetween() { + void getTwoParams_shouldResetTransformationOptionsInBetween() { provider.setValue(Base64.getEncoder().encodeToString("base64encoded".getBytes())); String foob64 = provider.withTransformation(base64).get("foob64"); @@ -384,7 +384,7 @@ public BasicProvider(CacheManager cacheManager, TransformationManager transforma super(cacheManager, transformationManager); } - public void setValue(String value) { + void setValue(String value) { this.value = value; } diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java index 6b3cf7641..5bc609777 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java @@ -46,7 +46,7 @@ import software.amazon.lambda.powertools.parameters.ssm.SSMProvider; @ExtendWith(MockitoExtension.class) -public class ParamProvidersIntegrationTest { +class ParamProvidersIntegrationTest { @Mock SsmClient ssmClient; @@ -66,7 +66,7 @@ public class ParamProvidersIntegrationTest { ArgumentCaptor secretsCaptor; @Test - public void ssmProvider_get() { + void ssmProvider_get() { SSMProvider ssmProvider = SSMProvider.builder() .withClient(ssmClient) .build(); @@ -84,7 +84,7 @@ public void ssmProvider_get() { } @Test - public void ssmProvider_getMultiple() { + void ssmProvider_getMultiple() { SSMProvider ssmProvider = SSMProvider.builder() .withClient(ssmClient) .build(); @@ -111,7 +111,7 @@ public void ssmProvider_getMultiple() { } @Test - public void secretsProvider_get() { + void secretsProvider_get() { SecretsProvider secretsProvider = SecretsProvider.builder() .withClient(secretsManagerClient) .build(); diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java index 2bcfcc566..d22572fad 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java @@ -21,23 +21,24 @@ import java.time.Clock; import java.util.Optional; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -public class CacheManagerTest { +class CacheManagerTest { CacheManager manager; Clock clock; @BeforeEach - public void setup() { + void setup() { clock = Clock.systemDefaultZone(); manager = new CacheManager(); } @Test - public void getIfNotExpired_notExpired_shouldReturnValue() { + void getIfNotExpired_notExpired_shouldReturnValue() { manager.putInCache("key", "value"); Optional value = manager.getIfNotExpired("key", clock.instant()); @@ -46,7 +47,7 @@ public void getIfNotExpired_notExpired_shouldReturnValue() { } @Test - public void getIfNotExpired_expired_shouldReturnNothing() { + void getIfNotExpired_expired_shouldReturnNothing() { manager.putInCache("key", "value"); Optional value = manager.getIfNotExpired("key", offset(clock, of(6, SECONDS)).instant()); @@ -55,7 +56,7 @@ public void getIfNotExpired_expired_shouldReturnNothing() { } @Test - public void getIfNotExpired_withCustomExpirationTime_notExpired_shouldReturnValue() { + void getIfNotExpired_withCustomExpirationTime_notExpired_shouldReturnValue() { manager.setExpirationTime(of(42, SECONDS)); manager.putInCache("key", "value"); @@ -65,18 +66,17 @@ public void getIfNotExpired_withCustomExpirationTime_notExpired_shouldReturnValu } @Test - public void getIfNotExpired_withCustomDefaultExpirationTime_notExpired_shouldReturnValue() { + void getIfNotExpired_withCustomDefaultExpirationTime_notExpired_shouldReturnValue() { manager.setDefaultExpirationTime(of(42, SECONDS)); manager.putInCache("key", "value"); - Optional value = manager.getIfNotExpired("key", offset(clock, of(40, SECONDS)).instant()); assertThat(value).isPresent().contains("value"); } @Test - public void getIfNotExpired_customDefaultExpirationTime_customExpirationTime_shouldUseExpirationTime() { + void getIfNotExpired_customDefaultExpirationTime_customExpirationTime_shouldUseExpirationTime() { manager.setDefaultExpirationTime(of(42, SECONDS)); manager.setExpirationTime(of(2, SECONDS)); manager.putInCache("key", "value"); @@ -87,7 +87,7 @@ public void getIfNotExpired_customDefaultExpirationTime_customExpirationTime_sho } @Test - public void getIfNotExpired_resetExpirationTime_shouldUseDefaultExpirationTime() { + void getIfNotExpired_resetExpirationTime_shouldUseDefaultExpirationTime() { manager.setDefaultExpirationTime(of(42, SECONDS)); manager.setExpirationTime(of(2, SECONDS)); manager.putInCache("key", "value"); @@ -101,4 +101,96 @@ public void getIfNotExpired_resetExpirationTime_shouldUseDefaultExpirationTime() assertThat(value2).isPresent().contains("value2"); } + @Test + void putInCache_sharedCache_shouldBeAccessibleAcrossThreads() throws InterruptedException { + // GIVEN + Thread thread1 = new Thread(() -> { + manager.setExpirationTime(of(60, SECONDS)); + manager.putInCache("sharedKey", "valueFromThread1"); + manager.resetExpirationTime(); + }); + + Thread thread2 = new Thread(() -> { + manager.setExpirationTime(of(10, SECONDS)); + // Thread 2 should be able to read the value cached by Thread 1 + Optional value = manager.getIfNotExpired("sharedKey", clock.instant()); + assertThat(value).isPresent().contains("valueFromThread1"); + manager.resetExpirationTime(); + }); + + // WHEN + thread1.start(); + thread1.join(); + thread2.start(); + thread2.join(); + + // THEN - Both threads should be able to access the same cached value + Optional value = manager.getIfNotExpired("sharedKey", clock.instant()); + assertThat(value).isPresent().contains("valueFromThread1"); + } + + @Test + void putInCache_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { + // GIVEN + int threadCount = 10; + Thread[] threads = new Thread[threadCount]; + boolean[] success = new boolean[threadCount]; + Clock testClock = Clock.systemDefaultZone(); + + // WHEN - Multiple threads set different expiration times and cache values concurrently + for (int i = 0; i < threadCount; i++) { + final int threadIndex = i; + final int expirationSeconds = (i % 2 == 0) ? 60 : 10; // Alternate between 60s and 10s + + threads[i] = new Thread(() -> { + try { + manager.setExpirationTime(of(expirationSeconds, SECONDS)); + manager.putInCache("key" + threadIndex, "value" + threadIndex); + manager.resetExpirationTime(); + success[threadIndex] = true; + } catch (Exception e) { + success[threadIndex] = false; + } + }); + } + + // Start all threads + for (Thread thread : threads) { + thread.start(); + } + + // Wait for all threads to complete + for (Thread thread : threads) { + thread.join(); + } + + // THEN - All threads should complete successfully + for (boolean result : success) { + assertThat(result).isTrue(); + } + + // THEN - Each cached value should have the correct expiration time + // Values with 60s TTL should still be present after 9s, values with 10s should expire after 11s + for (int i = 0; i < threadCount; i++) { + final int expirationSeconds = (i % 2 == 0) ? 60 : 10; + + // Check that value is still present just before expiration + Optional valueBeforeExpiry = manager.getIfNotExpired("key" + i, + offset(testClock, of(expirationSeconds - 1, SECONDS)).instant()); + assertThat(valueBeforeExpiry) + .as("Thread %d with %ds expiration should still have value after %ds", i, expirationSeconds, + expirationSeconds - 1) + .isPresent() + .contains("value" + i); + + // Check that value expires after the TTL + Optional valueAfterExpiry = manager.getIfNotExpired("key" + i, + offset(testClock, of(expirationSeconds + 1, SECONDS)).instant()); + assertThat(valueAfterExpiry) + .as("Thread %d with %ds expiration should not have value after %ds", i, expirationSeconds, + expirationSeconds + 1) + .isNotPresent(); + } + } + } diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java index e86ded9be..0de31e63d 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java @@ -24,35 +24,35 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -public class DataStoreTest { +class DataStoreTest { Clock clock; DataStore store; @BeforeEach - public void setup() { + void setup() { clock = Clock.systemDefaultZone(); store = new DataStore(); } @Test - public void put_shouldInsertInStore() { + void put_shouldInsertInStore() { store.put("key", "value", Instant.now()); assertThat(store.get("key")).isEqualTo("value"); } @Test - public void get_invalidKey_shouldReturnNull() { + void get_invalidKey_shouldReturnNull() { assertThat(store.get("key")).isNull(); } @Test - public void hasExpired_invalidKey_shouldReturnTrue() { + void hasExpired_invalidKey_shouldReturnTrue() { assertThat(store.hasExpired("key", clock.instant())).isTrue(); } @Test - public void hasExpired_notExpired_shouldReturnFalse() { + void hasExpired_notExpired_shouldReturnFalse() { Instant now = Instant.now(); store.put("key", "value", now.plus(10, SECONDS)); @@ -61,7 +61,7 @@ public void hasExpired_notExpired_shouldReturnFalse() { } @Test - public void hasExpired_expired_shouldReturnTrueAndRemoveElement() { + void hasExpired_expired_shouldReturnTrueAndRemoveElement() { Instant now = Instant.now(); store.put("key", "value", now.plus(10, SECONDS)); diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java index ea713b552..8dbddacf6 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java @@ -21,10 +21,10 @@ import org.junit.jupiter.api.Test; import software.amazon.lambda.powertools.parameters.exception.TransformationException; -public class Base64TransformerTest { +class Base64TransformerTest { @Test - public void transform_base64_shouldTransformInString() { + void transform_base64_shouldTransformInString() { Base64Transformer transformer = new Base64Transformer(); String s = transformer.applyTransformation(Base64.getEncoder().encodeToString("foobar".getBytes())); @@ -33,7 +33,7 @@ public void transform_base64_shouldTransformInString() { } @Test - public void transform_base64WrongFormat_shouldThrowException() { + void transform_base64WrongFormat_shouldThrowException() { Base64Transformer transformer = new Base64Transformer(); assertThatExceptionOfType(TransformationException.class) diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java index 5cb980cc7..48cebb6b0 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java @@ -22,23 +22,23 @@ import org.junit.jupiter.api.Test; import software.amazon.lambda.powertools.parameters.exception.TransformationException; -public class JsonTransformerTest { +class JsonTransformerTest { @Test - public void transform_json_shouldTransformInObject() throws TransformationException { + void transform_json_shouldTransformInObject() throws TransformationException { JsonTransformer transformation = new JsonTransformer<>(); ObjectToDeserialize objectToDeserialize = transformation.applyTransformation("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", ObjectToDeserialize.class); assertThat(objectToDeserialize).matches( - o -> o.getFoo().equals("Foo") + o -> "Foo".equals(o.getFoo()) && o.getBar() == 42 && o.getBaz() == 123456789); } @Test - public void transform_json_shouldTransformInHashMap() throws TransformationException { + void transform_json_shouldTransformInHashMap() throws TransformationException { JsonTransformer transformation = new JsonTransformer<>(); Map map = @@ -50,7 +50,7 @@ public void transform_json_shouldTransformInHashMap() throws TransformationExcep } @Test - public void transform_badJson_shouldThrowException() { + void transform_badJson_shouldThrowException() { JsonTransformer transformation = new JsonTransformer<>(); assertThatExceptionOfType(TransformationException.class) diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java index 39e69f9e0..fad6f5391 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java @@ -21,41 +21,44 @@ import static software.amazon.lambda.powertools.parameters.transform.Transformer.json; import java.util.Base64; +import java.util.concurrent.CountDownLatch; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; + import software.amazon.lambda.powertools.parameters.exception.TransformationException; -public class TransformationManagerTest { +class TransformationManagerTest { TransformationManager manager; Class basicTransformer = BasicTransformer.class; @BeforeEach - public void setup() { + void setup() { manager = new TransformationManager(); } @Test - public void setTransformer_shouldTransform() { + void setTransformer_shouldTransform() { manager.setTransformer(json); assertThat(manager.shouldTransform()).isTrue(); } @Test - public void notSetTransformer_shouldNotTransform() { + void notSetTransformer_shouldNotTransform() { assertThat(manager.shouldTransform()).isFalse(); } @Test - public void performBasicTransformation_noTransformer_shouldThrowException() { + void performBasicTransformation_noTransformer_shouldThrowException() { assertThatIllegalStateException() .isThrownBy(() -> manager.performBasicTransformation("value")); } @Test - public void performBasicTransformation_notBasicTransformer_shouldThrowException() { + void performBasicTransformation_notBasicTransformer_shouldThrowException() { manager.setTransformer(json); assertThatIllegalStateException() @@ -63,7 +66,7 @@ public void performBasicTransformation_notBasicTransformer_shouldThrowException( } @Test - public void performBasicTransformation_abstractTransformer_throwsTransformationException() { + void performBasicTransformation_abstractTransformer_throwsTransformationException() { manager.setTransformer(basicTransformer); assertThatExceptionOfType(TransformationException.class) @@ -71,7 +74,7 @@ public void performBasicTransformation_abstractTransformer_throwsTransformationE } @Test - public void performBasicTransformation_shouldPerformTransformation() { + void performBasicTransformation_shouldPerformTransformation() { manager.setTransformer(base64); String expectedValue = "bar"; @@ -81,27 +84,136 @@ public void performBasicTransformation_shouldPerformTransformation() { } @Test - public void performComplexTransformation_noTransformer_shouldThrowException() { + void performComplexTransformation_noTransformer_shouldThrowException() { assertThatIllegalStateException() .isThrownBy(() -> manager.performComplexTransformation("value", ObjectToDeserialize.class)); } @Test - public void performComplexTransformation_shouldPerformTransformation() { + void performComplexTransformation_shouldPerformTransformation() { manager.setTransformer(json); - ObjectToDeserialize object = - manager.performComplexTransformation("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", - ObjectToDeserialize.class); + ObjectToDeserialize object = manager.performComplexTransformation( + "{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", + ObjectToDeserialize.class); assertThat(object).isNotNull(); } @Test - public void performComplexTransformation_throwsTransformationException() { + void performComplexTransformation_throwsTransformationException() { manager.setTransformer(basicTransformer); assertThatExceptionOfType(TransformationException.class) .isThrownBy(() -> manager.performComplexTransformation("value", ObjectToDeserialize.class)); } + + @Test + void unsetTransformer_shouldCleanUpThreadLocal() { + // GIVEN + manager.setTransformer(json); + assertThat(manager.shouldTransform()).isTrue(); + + // WHEN + manager.unsetTransformer(); + + // THEN + assertThat(manager.shouldTransform()).isFalse(); + } + + @Test + void setTransformer_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { + // GIVEN + boolean[] success = new boolean[2]; + CountDownLatch latch = new CountDownLatch(2); + + Thread thread1 = new Thread(() -> { + try { + latch.countDown(); + latch.await(); + manager.setTransformer(json); + // Thread 1 expects json transformer + String result = manager.performComplexTransformation( + "{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", + ObjectToDeserialize.class).getFoo(); + success[0] = "Foo".equals(result); + } catch (Exception e) { + e.printStackTrace(); + success[0] = false; + } + }); + + Thread thread2 = new Thread(() -> { + try { + latch.countDown(); + latch.await(); + manager.setTransformer(base64); + // Thread 2 expects base64 transformer + String result = manager.performBasicTransformation( + Base64.getEncoder().encodeToString("bar".getBytes())); + success[1] = "bar".equals(result); + } catch (Exception e) { + e.printStackTrace(); + success[1] = false; + } + }); + + // WHEN - Start both threads concurrently + thread1.start(); + thread2.start(); + + // THEN - Both threads should complete without errors + thread1.join(); + thread2.join(); + + assertThat(success[0]).as("Thread 1 with JSON transformer should succeed").isTrue(); + assertThat(success[1]).as("Thread 2 with Base64 transformer should succeed").isTrue(); + } + + @Test + void unsetTransformer_concurrentCalls_shouldNotAffectOtherThreads() throws InterruptedException { + // GIVEN + boolean[] success = new boolean[2]; + CountDownLatch latch = new CountDownLatch(2); + + Thread thread1 = new Thread(() -> { + try { + latch.countDown(); + latch.await(); + manager.setTransformer(json); + // Thread 1 should still have json transformer even if thread 2 unsets + assertThat(manager.shouldTransform()).isTrue(); + success[0] = true; + } catch (Exception e) { + e.printStackTrace(); + success[0] = false; + } + }); + + Thread thread2 = new Thread(() -> { + try { + latch.countDown(); + latch.await(); + manager.setTransformer(base64); + manager.unsetTransformer(); + // Thread 2 should have no transformer after unset + assertThat(manager.shouldTransform()).isFalse(); + success[1] = true; + } catch (Exception e) { + e.printStackTrace(); + success[1] = false; + } + }); + + // WHEN + thread1.start(); + thread2.start(); + + // THEN + thread1.join(); + thread2.join(); + + assertThat(success[0]).as("Thread 1 should still have transformer").isTrue(); + assertThat(success[1]).as("Thread 2 should have unset transformer").isTrue(); + } } diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseProvider.java index bedace28c..d83ff0298 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseProvider.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseProvider.java @@ -19,7 +19,8 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Map; -import software.amazon.awssdk.annotations.NotThreadSafe; + +import software.amazon.awssdk.annotations.ThreadSafe; import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.exception.TransformationException; import software.amazon.lambda.powertools.parameters.transform.BasicTransformer; @@ -28,8 +29,20 @@ /** * Base class for all parameter providers. + *

+ * This class is thread-safe when used as a singleton in multi-threaded environments. + * Configuration methods ({@link #withMaxAge(int, ChronoUnit)}, {@link #withTransformation(Class)}) + * use thread-local storage to support concurrent requests with different requirements. + *

+ * The cache and transformation managers are thread-safe with zero synchronization overhead, + * using lock-free data structures (ThreadLocal, AtomicReference, ConcurrentHashMap) for optimal performance. + * The cache storage is shared across all threads, allowing cached values to be reused across requests. + *

+ * Implementation Requirements: Subclasses must ensure that implementations of + * {@link #getValue(String)} and {@link #getMultipleValues(String)} are thread-safe to + * guarantee overall thread-safety of the provider. */ -@NotThreadSafe +@ThreadSafe public abstract class BaseProvider implements ParamProvider { public static final String PARAMETERS = "parameters"; @@ -91,6 +104,7 @@ public BaseProvider withMaxAge(int maxAge, ChronoUnit unit) { * @param transformerClass Class of the transformer to apply. For convenience, you can use {@link Transformer#json} or {@link Transformer#base64} shortcuts. * @return the provider itself in order to chain calls (eg.

provider.withTransformation(json).get("key", MyObject.class)
). */ + @SuppressWarnings("rawtypes") // Transformer type parameter determined at runtime public BaseProvider withTransformation(Class transformerClass) { if (transformationManager == null) { throw new IllegalStateException( @@ -110,12 +124,12 @@ public BaseProvider withTransformation(Class transformerC * eg. getMultiple("/foo/bar") will retrieve [key="baz", value="valuebaz"] for parameter "/foo/bar/baz" */ @Override + @SuppressWarnings("unchecked") // Cache stores Object, safe cast as we control what's stored public Map getMultiple(String path) { // remove trailing whitespace String pathWithoutTrailingSlash = path.replaceAll("\\/+$", ""); try { - return (Map) cacheManager.getIfNotExpired(pathWithoutTrailingSlash, now()).orElseGet(() -> - { + return (Map) cacheManager.getIfNotExpired(pathWithoutTrailingSlash, now()).orElseGet(() -> { Map params = getMultipleValues(pathWithoutTrailingSlash); cacheManager.putInCache(pathWithoutTrailingSlash, params); @@ -143,8 +157,7 @@ public Map getMultiple(String path) { @Override public String get(final String key) { try { - return (String) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> - { + return (String) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> { String value = getValue(key); String transformedValue = value; @@ -175,10 +188,10 @@ public String get(final String key) { * @throws TransformationException if the transformation could not be done, because of a wrong format or an error during transformation. */ @Override + @SuppressWarnings("unchecked") // Cache stores Object, safe cast as we control what's stored public T get(final String key, final Class targetClass) { try { - return (T) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> - { + return (T) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> { String value = getValue(key); if (transformationManager == null) { @@ -207,7 +220,7 @@ protected Instant now() { protected void resetToDefaults() { cacheManager.resetExpirationTime(); if (transformationManager != null) { - transformationManager.setTransformer(null); + transformationManager.unsetTransformer(); } } diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/CacheManager.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/CacheManager.java index b868cb642..99c281b3d 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/CacheManager.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/CacheManager.java @@ -20,18 +20,27 @@ import java.time.Duration; import java.time.Instant; import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; +/** + * Manages caching of parameter values with configurable expiration times. + *

+ * This class is thread-safe. The cache storage is shared across all threads, + * while expiration time configuration is thread-local to support concurrent + * requests with different cache TTL requirements. + */ public class CacheManager { static final Duration DEFAULT_MAX_AGE_SECS = Duration.of(5, SECONDS); private final DataStore store; - private Duration defaultMaxAge = DEFAULT_MAX_AGE_SECS; - private Duration maxAge = defaultMaxAge; + private final AtomicReference defaultMaxAge = new AtomicReference<>(DEFAULT_MAX_AGE_SECS); + private final ThreadLocal maxAge = ThreadLocal.withInitial(() -> null); public CacheManager() { store = new DataStore(); } + @SuppressWarnings("unchecked") // DataStore stores Object, safe cast as we control what's stored public Optional getIfNotExpired(String key, Instant now) { if (store.hasExpired(key, now)) { return Optional.empty(); @@ -40,19 +49,19 @@ public Optional getIfNotExpired(String key, Instant now) { } public void setExpirationTime(Duration duration) { - this.maxAge = duration; + this.maxAge.set(duration); } public void setDefaultExpirationTime(Duration duration) { - this.defaultMaxAge = duration; - this.maxAge = duration; + this.defaultMaxAge.set(duration); } public void putInCache(String key, T value) { - store.put(key, value, Clock.systemDefaultZone().instant().plus(maxAge)); + Duration effectiveMaxAge = maxAge.get() != null ? maxAge.get() : defaultMaxAge.get(); + store.put(key, value, Clock.systemDefaultZone().instant().plus(effectiveMaxAge)); } public void resetExpirationTime() { - maxAge = defaultMaxAge; + maxAge.remove(); } } diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/DataStore.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/DataStore.java index 737faa353..4b6350cb5 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/DataStore.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/DataStore.java @@ -15,6 +15,7 @@ package software.amazon.lambda.powertools.parameters.cache; import java.time.Instant; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** @@ -22,7 +23,7 @@ */ public class DataStore { - private final ConcurrentHashMap store; + private final Map store; public DataStore() { this.store = new ConcurrentHashMap<>(); @@ -32,8 +33,8 @@ public void put(String key, Object value, Instant time) { store.put(key, new ValueNode(value, time)); } - public void remove(String Key) { - store.remove(Key); + public void remove(String key) { + store.remove(key); } public Object get(String key) { @@ -42,7 +43,11 @@ public Object get(String key) { } public boolean hasExpired(String key, Instant now) { - boolean hasExpired = !store.containsKey(key) || now.isAfter(store.get(key).time); + ValueNode node = store.get(key); + if (node == null) { + return true; + } + boolean hasExpired = now.isAfter(node.time); // Auto-clean if the parameter has expired if (hasExpired) { remove(key); diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/TransformationManager.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/TransformationManager.java index d3fbce14f..bddbf81d9 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/TransformationManager.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/TransformationManager.java @@ -15,31 +15,44 @@ package software.amazon.lambda.powertools.parameters.transform; import java.lang.reflect.InvocationTargetException; + import software.amazon.lambda.powertools.parameters.exception.TransformationException; /** * Manager in charge of transforming parameter values in another format.
* Leverages a {@link Transformer} in order to perform the transformation.
* The transformer must be passed with {@link #setTransformer(Class)} before performing any transform operation. + *

+ * This class is thread-safe. Transformer configuration is thread-local to support concurrent + * requests with different transformation requirements. */ public class TransformationManager { - private Class transformer = null; + private final ThreadLocal> transformer = ThreadLocal.withInitial(() -> null); /** * Set the {@link Transformer} to use for transformation. Must be called before any transformation. * * @param transformerClass class of the {@link Transformer} */ + @SuppressWarnings("rawtypes") // Transformer type parameter determined at runtime public void setTransformer(Class transformerClass) { - this.transformer = transformerClass; + this.transformer.set(transformerClass); + } + + /** + * Unset the {@link Transformer} and clean up thread-local storage. + * Should be called after transformation is complete to prevent memory leaks. + */ + public void unsetTransformer() { + this.transformer.remove(); } /** * @return true if a {@link Transformer} has been passed to the Manager */ public boolean shouldTransform() { - return transformer != null; + return transformer.get() != null; } /** @@ -48,20 +61,22 @@ public boolean shouldTransform() { * @param value the value to transform * @return the value transformed */ + @SuppressWarnings("rawtypes") // Transformer type parameter determined at runtime public String performBasicTransformation(String value) { - if (transformer == null) { + Class transformerClass = transformer.get(); + if (transformerClass == null) { throw new IllegalStateException( "You cannot perform a transformation without Transformer, use the provider.withTransformation() method to specify it."); } - if (!BasicTransformer.class.isAssignableFrom(transformer)) { + if (!BasicTransformer.class.isAssignableFrom(transformerClass)) { throw new IllegalStateException("Wrong Transformer for a String, choose a BasicTransformer."); } try { - BasicTransformer basicTransformer = - (BasicTransformer) transformer.getDeclaredConstructor().newInstance(null); + BasicTransformer basicTransformer = (BasicTransformer) transformerClass.getDeclaredConstructor() + .newInstance(null); return basicTransformer.applyTransformation(value); - } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | - InvocationTargetException e) { + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException + | InvocationTargetException e) { throw new TransformationException(e); } } @@ -73,17 +88,19 @@ public String performBasicTransformation(String value) { * @param targetClass the type of the target object. * @return the value transformed in an object ot type T. */ + @SuppressWarnings("rawtypes") // Transformer type parameter determined at runtime public T performComplexTransformation(String value, Class targetClass) { - if (transformer == null) { + Class transformerClass = transformer.get(); + if (transformerClass == null) { throw new IllegalStateException( "You cannot perform a transformation without Transformer, use the provider.withTransformation() method to specify it."); } try { - Transformer complexTransformer = transformer.getDeclaredConstructor().newInstance(null); + Transformer complexTransformer = transformerClass.getDeclaredConstructor().newInstance(null); return complexTransformer.applyTransformation(value, targetClass); - } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | - InvocationTargetException e) { + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException + | InvocationTargetException e) { throw new TransformationException(e); } } From 03ca72b27463e5848f582c689b474cdb92643f61 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Nov 2025 09:27:22 +0100 Subject: [PATCH 35/76] chore: bump aws.sdk.version from 2.38.3 to 2.38.5 (#2287) Bumps `aws.sdk.version` from 2.38.3 to 2.38.5. Updates `software.amazon.awssdk:bom` from 2.38.3 to 2.38.5 Updates `software.amazon.awssdk:http-client-spi` from 2.38.3 to 2.38.5 Updates `software.amazon.awssdk:url-connection-client` from 2.38.2 to 2.38.5 Updates `software.amazon.awssdk:s3` from 2.38.2 to 2.38.5 Updates `software.amazon.awssdk:dynamodb` from 2.38.3 to 2.38.5 Updates `software.amazon.awssdk:lambda` from 2.38.3 to 2.38.5 Updates `software.amazon.awssdk:kinesis` from 2.38.2 to 2.38.5 Updates `software.amazon.awssdk:cloudwatch` from 2.38.3 to 2.38.5 Updates `software.amazon.awssdk:xray` from 2.38.3 to 2.38.5 Updates `software.amazon.awssdk:sqs` from 2.38.2 to 2.38.5 Updates `software.amazon.awssdk:cloudformation` from 2.38.3 to 2.38.5 Updates `software.amazon.awssdk:sts` from 2.38.3 to 2.38.5 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.38.5 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.38.5 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.38.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.38.5 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.38.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.38.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.38.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.38.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.38.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.38.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.38.5 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.38.5 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 74ced3a4f..824b98da9 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ 11 1.4.0 3.16.1 - 2.38.3 + 2.38.5 1.9.20.1 diff --git a/pom.xml b/pom.xml index 48be39d47..536ba6002 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ 2.25.2 2.0.17 2.20.1 - 2.38.3 + 2.38.5 2.20.0 2.2.0 UTF-8 diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index afc21131f..7838dfc03 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ 3.6.1 1.14.1 3.14.1 - 2.38.3 + 2.38.5 1.9.20.1 true From 4e83d8a3b5cfbdb26d6ae5db81393d9c47c78df5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Nov 2025 09:27:30 +0100 Subject: [PATCH 36/76] chore: bump commons-io:commons-io from 2.20.0 to 2.21.0 (#2288) Bumps [commons-io:commons-io](https://github.com/apache/commons-io) from 2.20.0 to 2.21.0. - [Changelog](https://github.com/apache/commons-io/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-io/compare/rel/commons-io-2.20.0...rel/commons-io-2.21.0) --- updated-dependencies: - dependency-name: commons-io:commons-io dependency-version: 2.21.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 998c6a803..6dd94910a 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -95,7 +95,7 @@ commons-io commons-io - 2.20.0 + 2.21.0 org.junit.jupiter From 3ed0e0dea3f5c5988dbc8e34ccfb7ffb207f6133 Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Mon, 17 Nov 2025 10:56:22 +0100 Subject: [PATCH 37/76] improv(metrics): Add thread-safety to Metrics utility through request-scoped Metrics instance management (#2294) * Add proxy to manage thread local metrics provider instances to achieve zero-lock thread-safety. * Refactor from inheritable thread local to tracking by trace id. Similar to log buffer. * Do not hard code JVM system property for trace id. * Fix PMD findings. --- .../powertools/metrics/MetricsFactory.java | 18 +- .../internal/RequestScopedMetricsProxy.java | 156 ++++++++++ .../metrics/ConfigurationPrecedenceTest.java | 13 +- .../metrics/MetricsBuilderTest.java | 18 +- .../metrics/MetricsFactoryTest.java | 70 ++++- .../metrics/RequestHandlerTest.java | 2 +- .../internal/EmfMetricsLoggerTest.java | 14 +- .../internal/LambdaMetricsAspectTest.java | 8 +- .../RequestScopedMetricsProxyTest.java | 277 ++++++++++++++++++ 9 files changed, 536 insertions(+), 40 deletions(-) create mode 100644 powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/RequestScopedMetricsProxy.java create mode 100644 powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/RequestScopedMetricsProxyTest.java diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsFactory.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsFactory.java index 67ab17b7b..0644329c9 100644 --- a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsFactory.java +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsFactory.java @@ -16,9 +16,11 @@ import org.crac.Core; import org.crac.Resource; + import software.amazon.lambda.powertools.common.internal.ClassPreLoader; import software.amazon.lambda.powertools.common.internal.LambdaConstants; import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.metrics.internal.RequestScopedMetricsProxy; import software.amazon.lambda.powertools.metrics.model.DimensionSet; import software.amazon.lambda.powertools.metrics.provider.EmfMetricsProvider; import software.amazon.lambda.powertools.metrics.provider.MetricsProvider; @@ -28,7 +30,7 @@ */ public final class MetricsFactory implements Resource { private static MetricsProvider provider = new EmfMetricsProvider(); - private static Metrics metrics; + private static RequestScopedMetricsProxy metricsProxy; // Dummy instance to register MetricsFactory with CRaC private static final MetricsFactory INSTANCE = new MetricsFactory(); @@ -44,23 +46,23 @@ public final class MetricsFactory implements Resource { * @return the singleton Metrics instance */ public static synchronized Metrics getMetricsInstance() { - if (metrics == null) { - metrics = provider.getMetricsInstance(); + if (metricsProxy == null) { + metricsProxy = new RequestScopedMetricsProxy(provider); // Apply default configuration from environment variables String envNamespace = System.getenv("POWERTOOLS_METRICS_NAMESPACE"); if (envNamespace != null) { - metrics.setNamespace(envNamespace); + metricsProxy.setNamespace(envNamespace); } // Only set Service dimension if it's not the default undefined value String serviceName = LambdaHandlerProcessor.serviceName(); if (!LambdaConstants.SERVICE_UNDEFINED.equals(serviceName)) { - metrics.setDefaultDimensions(DimensionSet.of("Service", serviceName)); + metricsProxy.setDefaultDimensions(DimensionSet.of("Service", serviceName)); } } - return metrics; + return metricsProxy; } /** @@ -73,8 +75,8 @@ public static synchronized void setMetricsProvider(MetricsProvider metricsProvid throw new IllegalArgumentException("Metrics provider cannot be null"); } provider = metricsProvider; - // Reset the metrics instance so it will be recreated with the new provider - metrics = null; + // Reset the metrics proxy so it will be recreated with the new provider + metricsProxy = null; } @Override diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/RequestScopedMetricsProxy.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/RequestScopedMetricsProxy.java new file mode 100644 index 000000000..61c83be17 --- /dev/null +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/RequestScopedMetricsProxy.java @@ -0,0 +1,156 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.metrics.internal; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +import com.amazonaws.services.lambda.runtime.Context; + +import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.metrics.model.DimensionSet; +import software.amazon.lambda.powertools.metrics.model.MetricResolution; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; +import software.amazon.lambda.powertools.metrics.provider.MetricsProvider; + +public class RequestScopedMetricsProxy implements Metrics { + private static final String DEFAULT_TRACE_ID = "DEFAULT"; + private final ConcurrentMap metricsMap = new ConcurrentHashMap<>(); + private final MetricsProvider provider; + private final AtomicReference initialNamespace = new AtomicReference<>(); + private final AtomicReference initialDefaultDimensions = new AtomicReference<>(); + private final AtomicBoolean initialRaiseOnEmptyMetrics = new AtomicBoolean(false); + + public RequestScopedMetricsProxy(MetricsProvider provider) { + this.provider = provider; + } + + private String getTraceId() { + return LambdaHandlerProcessor.getXrayTraceId().orElse(DEFAULT_TRACE_ID); + } + + private Metrics getOrCreateMetrics() { + String traceId = getTraceId(); + return metricsMap.computeIfAbsent(traceId, key -> { + Metrics metrics = provider.getMetricsInstance(); + String namespace = initialNamespace.get(); + if (namespace != null) { + metrics.setNamespace(namespace); + } + DimensionSet dimensions = initialDefaultDimensions.get(); + if (dimensions != null) { + metrics.setDefaultDimensions(dimensions); + } + metrics.setRaiseOnEmptyMetrics(initialRaiseOnEmptyMetrics.get()); + return metrics; + }); + } + + // Configuration methods - called by MetricsFactory and MetricsBuilder + // These methods DO NOT eagerly create instances because they are typically called + // outside the Lambda handler (e.g., during class initialization) potentially on a different thread. + // We delay instance creation until the first operation that needs the metrics backend (e.g., addMetric). + // See {@link software.amazon.lambda.powertools.metrics.MetricsFactory#getMetricsInstance()} + // and {@link software.amazon.lambda.powertools.metrics.MetricsBuilder#build()} + + @Override + public void setNamespace(String namespace) { + this.initialNamespace.set(namespace); + Optional.ofNullable(metricsMap.get(getTraceId())).ifPresent(m -> m.setNamespace(namespace)); + } + + @Override + public void setDefaultDimensions(DimensionSet dimensionSet) { + if (dimensionSet == null) { + throw new IllegalArgumentException("DimensionSet cannot be null"); + } + this.initialDefaultDimensions.set(dimensionSet); + Optional.ofNullable(metricsMap.get(getTraceId())).ifPresent(m -> m.setDefaultDimensions(dimensionSet)); + } + + @Override + public void setRaiseOnEmptyMetrics(boolean raiseOnEmptyMetrics) { + this.initialRaiseOnEmptyMetrics.set(raiseOnEmptyMetrics); + Optional.ofNullable(metricsMap.get(getTraceId())).ifPresent(m -> m.setRaiseOnEmptyMetrics(raiseOnEmptyMetrics)); + } + + @Override + public DimensionSet getDefaultDimensions() { + Metrics metrics = metricsMap.get(getTraceId()); + if (metrics != null) { + return metrics.getDefaultDimensions(); + } + DimensionSet dimensions = initialDefaultDimensions.get(); + return dimensions != null ? dimensions : DimensionSet.of(new HashMap<>()); + } + + // Metrics operations - these eagerly create instances + + @Override + public void addMetric(String key, double value, MetricUnit unit, MetricResolution resolution) { + getOrCreateMetrics().addMetric(key, value, unit, resolution); + } + + @Override + public void addDimension(DimensionSet dimensionSet) { + getOrCreateMetrics().addDimension(dimensionSet); + } + + @Override + public void setTimestamp(Instant timestamp) { + getOrCreateMetrics().setTimestamp(timestamp); + } + + @Override + public void addMetadata(String key, Object value) { + getOrCreateMetrics().addMetadata(key, value); + } + + @Override + public void clearDefaultDimensions() { + getOrCreateMetrics().clearDefaultDimensions(); + } + + @Override + public void flush() { + // Always create instance to ensure validation and warnings are triggered. E.g. when raiseOnEmptyMetrics + // is enabled. + Metrics metrics = getOrCreateMetrics(); + metrics.flush(); + metricsMap.remove(getTraceId()); + } + + @Override + public void captureColdStartMetric(Context context, DimensionSet dimensions) { + getOrCreateMetrics().captureColdStartMetric(context, dimensions); + } + + @Override + public void captureColdStartMetric(DimensionSet dimensions) { + getOrCreateMetrics().captureColdStartMetric(dimensions); + } + + @Override + public void flushMetrics(Consumer metricsConsumer) { + getOrCreateMetrics().flushMetrics(metricsConsumer); + } +} diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/ConfigurationPrecedenceTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/ConfigurationPrecedenceTest.java index c9c772313..492ecfc1e 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/ConfigurationPrecedenceTest.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/ConfigurationPrecedenceTest.java @@ -33,8 +33,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; -import software.amazon.lambda.powertools.metrics.model.MetricUnit; import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; /** * Tests to verify the hierarchy of precedence for configuration: @@ -44,7 +44,7 @@ */ class ConfigurationPrecedenceTest { - private final PrintStream standardOut = System.out; + private static final PrintStream STANDARD_OUT = System.out; private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); private final ObjectMapper objectMapper = new ObjectMapper(); @@ -65,10 +65,10 @@ void setUp() throws Exception { @AfterEach void tearDown() throws Exception { - System.setOut(standardOut); + System.setOut(STANDARD_OUT); // Reset the singleton state between tests - java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metrics"); + java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metricsProxy"); field.setAccessible(true); field.set(null, null); @@ -183,7 +183,7 @@ void shouldUseDefaultsWhenNoConfiguration() throws Exception { assertThat(rootNode.has("Service")).isFalse(); } - private static class HandlerWithMetricsAnnotation implements RequestHandler, String> { + private static final class HandlerWithMetricsAnnotation implements RequestHandler, String> { @Override @FlushMetrics(namespace = "AnnotationNamespace", service = "AnnotationService") public String handleRequest(Map input, Context context) { @@ -193,7 +193,8 @@ public String handleRequest(Map input, Context context) { } } - private static class HandlerWithDefaultMetricsAnnotation implements RequestHandler, String> { + private static final class HandlerWithDefaultMetricsAnnotation + implements RequestHandler, String> { @Override @FlushMetrics public String handleRequest(Map input, Context context) { diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsBuilderTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsBuilderTest.java index bd300fb6b..12ac46e43 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsBuilderTest.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsBuilderTest.java @@ -27,15 +27,15 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import software.amazon.lambda.powertools.metrics.internal.RequestScopedMetricsProxy; import software.amazon.lambda.powertools.metrics.model.DimensionSet; import software.amazon.lambda.powertools.metrics.model.MetricUnit; import software.amazon.lambda.powertools.metrics.provider.MetricsProvider; -import software.amazon.lambda.powertools.metrics.testutils.TestMetrics; import software.amazon.lambda.powertools.metrics.testutils.TestMetricsProvider; class MetricsBuilderTest { - private final PrintStream standardOut = System.out; + private static final PrintStream STANDARD_OUT = System.out; private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); private final ObjectMapper objectMapper = new ObjectMapper(); @@ -46,10 +46,10 @@ void setUp() { @AfterEach void tearDown() throws Exception { - System.setOut(standardOut); + System.setOut(STANDARD_OUT); // Reset the singleton state between tests - java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metrics"); + java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metricsProxy"); field.setAccessible(true); field.set(null, null); @@ -151,7 +151,7 @@ void shouldBuildWithMultipleDefaultDimensions() throws Exception { } @Test - void shouldBuildWithCustomMetricsProvider() { + void shouldBuildWithCustomMetricsProvider() throws Exception { // Given MetricsProvider testProvider = new TestMetricsProvider(); @@ -161,7 +161,13 @@ void shouldBuildWithCustomMetricsProvider() { .build(); // Then - assertThat(metrics).isInstanceOf(TestMetrics.class); + assertThat(metrics) + .isInstanceOf(RequestScopedMetricsProxy.class); + + java.lang.reflect.Field providerField = metrics.getClass().getDeclaredField("provider"); + providerField.setAccessible(true); + MetricsProvider actualProvider = (MetricsProvider) providerField.get(metrics); + assertThat(actualProvider).isSameAs(testProvider); } @Test diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsFactoryTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsFactoryTest.java index 4fc98d2a5..c9b183a1a 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsFactoryTest.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsFactoryTest.java @@ -29,10 +29,11 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import software.amazon.lambda.powertools.common.internal.LambdaConstants; import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.metrics.internal.RequestScopedMetricsProxy; import software.amazon.lambda.powertools.metrics.model.MetricUnit; import software.amazon.lambda.powertools.metrics.provider.MetricsProvider; -import software.amazon.lambda.powertools.metrics.testutils.TestMetrics; import software.amazon.lambda.powertools.metrics.testutils.TestMetricsProvider; class MetricsFactoryTest { @@ -40,7 +41,7 @@ class MetricsFactoryTest { private static final String TEST_NAMESPACE = "TestNamespace"; private static final String TEST_SERVICE = "TestService"; - private final PrintStream standardOut = System.out; + private static final PrintStream STANDARD_OUT = System.out; private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); private final ObjectMapper objectMapper = new ObjectMapper(); @@ -61,10 +62,11 @@ void setUp() throws Exception { @AfterEach void tearDown() throws Exception { - System.setOut(standardOut); + System.setOut(STANDARD_OUT); + System.clearProperty(LambdaConstants.XRAY_TRACE_HEADER); // Reset the singleton state between tests - java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metrics"); + java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metricsProxy"); field.setAccessible(true); field.set(null, null); @@ -126,7 +128,7 @@ void shouldUseServiceNameFromEnvironmentVariable() throws Exception { } @Test - void shouldSetCustomMetricsProvider() { + void shouldSetCustomMetricsProvider() throws Exception { // Given MetricsProvider testProvider = new TestMetricsProvider(); @@ -135,7 +137,13 @@ void shouldSetCustomMetricsProvider() { Metrics metrics = MetricsFactory.getMetricsInstance(); // Then - assertThat(metrics).isInstanceOf(TestMetrics.class); + assertThat(metrics) + .isInstanceOf(RequestScopedMetricsProxy.class); + + java.lang.reflect.Field providerField = metrics.getClass().getDeclaredField("provider"); + providerField.setAccessible(true); + MetricsProvider actualProvider = (MetricsProvider) providerField.get(metrics); + assertThat(actualProvider).isSameAs(testProvider); } @Test @@ -163,4 +171,54 @@ void shouldNotSetServiceDimensionWhenServiceUndefined() throws Exception { // Service dimension should not be present assertThat(rootNode.has("Service")).isFalse(); } + + @Test + void shouldIsolateMetricsByTraceId() throws Exception { + // GIVEN + Metrics metrics = MetricsFactory.getMetricsInstance(); + + // WHEN - Simulate Lambda invocation 1 with trace ID 1 + System.setProperty(LambdaConstants.XRAY_TRACE_HEADER, "Root=1-trace-id-1"); + metrics.setNamespace("TestNamespace"); + metrics.addDimension("userId", "user123"); + metrics.addMetric("ProcessedOrder", 1, MetricUnit.COUNT); + metrics.flush(); + + // WHEN - Simulate Lambda invocation 2 with trace ID 2 + System.setProperty(LambdaConstants.XRAY_TRACE_HEADER, "Root=1-trace-id-2"); + metrics.setNamespace("TestNamespace"); + metrics.addDimension("userId", "user456"); + metrics.addMetric("ProcessedOrder", 1, MetricUnit.COUNT); + metrics.flush(); + + // THEN - Verify each invocation has isolated metrics + String emfOutput = outputStreamCaptor.toString().trim(); + String[] jsonLines = emfOutput.split("\n"); + assertThat(jsonLines).hasSize(2); + + JsonNode output1 = objectMapper.readTree(jsonLines[0]); + JsonNode output2 = objectMapper.readTree(jsonLines[1]); + + assertThat(output1.get("userId").asText()).isEqualTo("user123"); + assertThat(output2.get("userId").asText()).isEqualTo("user456"); + } + + @Test + void shouldUseDefaultKeyWhenNoTraceId() throws Exception { + // GIVEN - No trace ID set + System.clearProperty(LambdaConstants.XRAY_TRACE_HEADER); + + // WHEN + Metrics metrics = MetricsFactory.getMetricsInstance(); + metrics.setNamespace("TestNamespace"); + metrics.addMetric("TestMetric", 1, MetricUnit.COUNT); + metrics.flush(); + + // THEN - Should work without X-Ray trace ID + String emfOutput = outputStreamCaptor.toString().trim(); + assertThat(emfOutput).isNotEmpty(); + + JsonNode rootNode = objectMapper.readTree(emfOutput); + assertThat(rootNode.get("TestMetric").asDouble()).isEqualTo(1.0); + } } diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/RequestHandlerTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/RequestHandlerTest.java index d3ed64fe3..fcd677c02 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/RequestHandlerTest.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/RequestHandlerTest.java @@ -56,7 +56,7 @@ void tearDown() throws Exception { System.setOut(STDOUT); // Reset the singleton state between tests - java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metrics"); + java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metricsProxy"); field.setAccessible(true); field.set(null, null); diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLoggerTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLoggerTest.java index bab039640..c2238711a 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLoggerTest.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLoggerTest.java @@ -38,14 +38,15 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import software.amazon.cloudwatchlogs.emf.environment.EnvironmentProvider; +import software.amazon.cloudwatchlogs.emf.model.MetricsContext; import software.amazon.cloudwatchlogs.emf.model.Unit; import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; import software.amazon.lambda.powertools.metrics.Metrics; -import software.amazon.lambda.powertools.metrics.MetricsFactory; import software.amazon.lambda.powertools.metrics.model.DimensionSet; import software.amazon.lambda.powertools.metrics.model.MetricResolution; import software.amazon.lambda.powertools.metrics.model.MetricUnit; -import software.amazon.lambda.powertools.common.stubs.TestLambdaContext; class EmfMetricsLoggerTest { @@ -66,19 +67,14 @@ void setUp() throws Exception { coldStartField.setAccessible(true); coldStartField.set(null, null); - metrics = MetricsFactory.getMetricsInstance(); + metrics = new EmfMetricsLogger(new EnvironmentProvider(), new MetricsContext()); metrics.setNamespace("TestNamespace"); System.setOut(new PrintStream(outputStreamCaptor)); } @AfterEach - void tearDown() throws Exception { + void tearDown() { System.setOut(standardOut); - - // Reset the singleton state between tests - java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metrics"); - field.setAccessible(true); - field.set(null, null); } @ParameterizedTest diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspectTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspectTest.java index 031fe4553..119e094a9 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspectTest.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspectTest.java @@ -41,7 +41,7 @@ class LambdaMetricsAspectTest { - private final PrintStream standardOut = System.out; + private static final PrintStream STANDARD_OUT = System.out; private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); private final ObjectMapper objectMapper = new ObjectMapper(); @@ -62,10 +62,10 @@ void setUp() throws Exception { @AfterEach void tearDown() throws Exception { - System.setOut(standardOut); + System.setOut(STANDARD_OUT); // Reset the singleton state between tests - java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metrics"); + java.lang.reflect.Field field = MetricsFactory.class.getDeclaredField("metricsProxy"); field.setAccessible(true); field.set(null, null); } @@ -216,7 +216,7 @@ void shouldUseCustomFunctionNameWhenProvidedForColdStartMetric() throws Exceptio JsonNode dimensions = coldStartNode.get("_aws").get("CloudWatchMetrics").get(0).get("Dimensions").get(0); boolean hasFunctionName = false; for (JsonNode dimension : dimensions) { - if (dimension.asText().equals("FunctionName")) { + if ("FunctionName".equals(dimension.asText())) { hasFunctionName = true; break; } diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/RequestScopedMetricsProxyTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/RequestScopedMetricsProxyTest.java new file mode 100644 index 000000000..848222bae --- /dev/null +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/RequestScopedMetricsProxyTest.java @@ -0,0 +1,277 @@ +package software.amazon.lambda.powertools.metrics.internal; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.time.Instant; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mock.Strictness; +import org.mockito.junit.jupiter.MockitoExtension; + +import software.amazon.lambda.powertools.common.internal.LambdaConstants; +import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.metrics.model.DimensionSet; +import software.amazon.lambda.powertools.metrics.model.MetricResolution; +import software.amazon.lambda.powertools.metrics.model.MetricUnit; +import software.amazon.lambda.powertools.metrics.provider.MetricsProvider; + +/** + * Tests for RequestScopedMetricsProxy focusing on lazy vs eager initialization behavior. + * + * CRITICAL: These tests ensure configuration methods (setNamespace, setDefaultDimensions, + * setRaiseOnEmptyMetrics) do NOT eagerly create instances, while metrics operations + * (addMetric, addDimension, flush) DO eagerly create instances. + */ +@ExtendWith(MockitoExtension.class) +class RequestScopedMetricsProxyTest { + + @Mock(strictness = Strictness.LENIENT) + private MetricsProvider mockProvider; + + @Mock(strictness = Strictness.LENIENT) + private Metrics mockMetrics; + + private RequestScopedMetricsProxy proxy; + + @BeforeEach + void setUp() { + when(mockProvider.getMetricsInstance()).thenReturn(mockMetrics); + proxy = new RequestScopedMetricsProxy(mockProvider); + } + + @AfterEach + void tearDown() { + System.clearProperty(LambdaConstants.XRAY_TRACE_HEADER); + } + + // ========== LAZY INITIALIZATION TESTS (Configuration Methods) ========== + + @Test + void setNamespace_shouldNotEagerlyCreateInstance() { + // WHEN + proxy.setNamespace("TestNamespace"); + + // THEN - Provider should NOT be called (lazy initialization) + verify(mockProvider, never()).getMetricsInstance(); + } + + @Test + void setDefaultDimensions_shouldNotEagerlyCreateInstance() { + // WHEN + proxy.setDefaultDimensions(DimensionSet.of("key", "value")); + + // THEN - Provider should NOT be called (lazy initialization) + verify(mockProvider, never()).getMetricsInstance(); + } + + @Test + void setDefaultDimensions_shouldThrowExceptionWhenNull() { + // When/Then + assertThatThrownBy(() -> proxy.setDefaultDimensions(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("DimensionSet cannot be null"); + } + + @Test + void setRaiseOnEmptyMetrics_shouldNotEagerlyCreateInstance() { + // WHEN + proxy.setRaiseOnEmptyMetrics(true); + + // THEN - Provider should NOT be called (lazy initialization) + verify(mockProvider, never()).getMetricsInstance(); + } + + // ========== EAGER INITIALIZATION TESTS (Metrics Operations) ========== + + @Test + void addMetric_shouldEagerlyCreateInstance() { + // WHEN + proxy.addMetric("test", 1, MetricUnit.COUNT, MetricResolution.HIGH); + + // THEN - Provider SHOULD be called (eager initialization) + verify(mockProvider, times(1)).getMetricsInstance(); + verify(mockMetrics).addMetric("test", 1, MetricUnit.COUNT, MetricResolution.HIGH); + } + + @Test + void addDimension_shouldEagerlyCreateInstance() { + // WHEN + proxy.addDimension(DimensionSet.of("key", "value")); + + // THEN - Provider SHOULD be called (eager initialization) + verify(mockProvider, times(1)).getMetricsInstance(); + verify(mockMetrics).addDimension(any(DimensionSet.class)); + } + + @Test + void addMetadata_shouldEagerlyCreateInstance() { + // WHEN + proxy.addMetadata("key", "value"); + + // THEN - Provider SHOULD be called (eager initialization) + verify(mockProvider, times(1)).getMetricsInstance(); + verify(mockMetrics).addMetadata("key", "value"); + } + + @Test + void flush_shouldAlwaysCreateInstance() { + // WHEN + proxy.flush(); + + // THEN - Provider SHOULD be called even if no metrics added + verify(mockProvider, times(1)).getMetricsInstance(); + verify(mockMetrics).flush(); + } + + // ========== CONFIGURATION APPLIED ON FIRST METRICS OPERATION ========== + + @Test + void firstMetricsOperation_shouldApplyStoredConfiguration() { + // GIVEN - Set configuration without creating instance + proxy.setNamespace("TestNamespace"); + proxy.setDefaultDimensions(DimensionSet.of("Service", "TestService")); + proxy.setRaiseOnEmptyMetrics(true); + verify(mockProvider, never()).getMetricsInstance(); + + // WHEN - First metrics operation + proxy.addMetric("test", 1, MetricUnit.COUNT); + + // THEN - Instance created and configuration applied + verify(mockProvider, times(1)).getMetricsInstance(); + verify(mockMetrics).setNamespace("TestNamespace"); + verify(mockMetrics).setDefaultDimensions(any(DimensionSet.class)); + verify(mockMetrics).setRaiseOnEmptyMetrics(true); + verify(mockMetrics).addMetric("test", 1, MetricUnit.COUNT, MetricResolution.STANDARD); + } + + // ========== THREAD ISOLATION TESTS ========== + + @Test + void shouldShareInstanceAcrossThreadsWithSameTraceId() throws Exception { + // GIVEN - Set trace ID + System.setProperty(LambdaConstants.XRAY_TRACE_HEADER, "Root=1-test-trace-id"); + + // WHEN - Parent thread adds metric + proxy.addMetric("metric1", 1, MetricUnit.COUNT); + verify(mockProvider, times(1)).getMetricsInstance(); + + // WHEN - Child thread adds metric (same trace ID) + Thread thread2 = new Thread(() -> { + proxy.addMetric("metric2", 2, MetricUnit.COUNT); + }); + thread2.start(); + thread2.join(); + + // THEN - Only one instance created (same trace ID = shared instance) + verify(mockProvider, times(1)).getMetricsInstance(); + } + + @Test + void flush_shouldRemoveThreadLocalInstance() { + // GIVEN - Create instance + proxy.addMetric("test", 1, MetricUnit.COUNT); + verify(mockProvider, times(1)).getMetricsInstance(); + + // WHEN - Flush + proxy.flush(); + + // WHEN - Add another metric (should create new instance) + proxy.addMetric("test2", 2, MetricUnit.COUNT); + + // THEN - Provider called twice (instance was removed after flush) + verify(mockProvider, times(2)).getMetricsInstance(); + } + + // ========== EDGE CASES ========== + + @Test + void multipleConfigurationCalls_shouldUpdateAtomicReferences() { + // WHEN + proxy.setNamespace("Namespace1"); + proxy.setNamespace("Namespace2"); + proxy.setNamespace("Namespace3"); + + // THEN - No instance created + verify(mockProvider, never()).getMetricsInstance(); + + // WHEN - First metrics operation + proxy.addMetric("test", 1, MetricUnit.COUNT); + + // THEN - Only last namespace applied + verify(mockMetrics).setNamespace("Namespace3"); + verify(mockMetrics, never()).setNamespace("Namespace1"); + verify(mockMetrics, never()).setNamespace("Namespace2"); + } + + @Test + void configurationAfterInstanceCreation_shouldApplyImmediately() { + // GIVEN - Instance already created + proxy.addMetric("test", 1, MetricUnit.COUNT); + + // WHEN - Set configuration + proxy.setNamespace("NewNamespace"); + + // THEN - Applied immediately to existing instance + verify(mockMetrics).setNamespace("NewNamespace"); + } + + @Test + void setTimestamp_shouldEagerlyCreateInstance() { + // When + proxy.setTimestamp(Instant.now()); + + // Then + verify(mockProvider, times(1)).getMetricsInstance(); + verify(mockMetrics).setTimestamp(any()); + } + + @Test + void getDefaultDimensions_shouldNotEagerlyCreateInstance() { + // WHEN + DimensionSet result = proxy.getDefaultDimensions(); + + // THEN - Provider should NOT be called (returns stored config or empty) + verify(mockProvider, never()).getMetricsInstance(); + assertThat(result).isNotNull(); + } + + @Test + void captureColdStartMetric_shouldEagerlyCreateInstance() { + // WHEN + proxy.captureColdStartMetric(DimensionSet.of("key", "value")); + + // THEN - Provider SHOULD be called (eager initialization) + verify(mockProvider, times(1)).getMetricsInstance(); + verify(mockMetrics).captureColdStartMetric(any(DimensionSet.class)); + } + + @Test + void flushMetrics_shouldEagerlyCreateInstance() { + // WHEN + proxy.flushMetrics(m -> m.addMetric("test", 1, MetricUnit.COUNT)); + + // THEN - Provider SHOULD be called (eager initialization) + verify(mockProvider, times(1)).getMetricsInstance(); + verify(mockMetrics).flushMetrics(any()); + } + + @Test + void clearDefaultDimensions_shouldEagerlyCreateInstance() { + // WHEN + proxy.clearDefaultDimensions(); + + // THEN - Provider SHOULD be called (eager initialization) + verify(mockProvider, times(1)).getMetricsInstance(); + verify(mockMetrics).clearDefaultDimensions(); + } +} From 0af0ba445821cdd810da4eef2fb3de8ef0770c77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 10:58:59 +0100 Subject: [PATCH 38/76] chore: bump aws.sdk.version from 2.38.5 to 2.38.6 (#2290) Bumps `aws.sdk.version` from 2.38.5 to 2.38.6. Updates `software.amazon.awssdk:bom` from 2.38.5 to 2.38.6 Updates `software.amazon.awssdk:http-client-spi` from 2.38.5 to 2.38.6 Updates `software.amazon.awssdk:url-connection-client` from 2.38.2 to 2.38.6 Updates `software.amazon.awssdk:s3` from 2.38.2 to 2.38.6 Updates `software.amazon.awssdk:dynamodb` from 2.38.5 to 2.38.6 Updates `software.amazon.awssdk:lambda` from 2.38.5 to 2.38.6 Updates `software.amazon.awssdk:kinesis` from 2.38.2 to 2.38.6 Updates `software.amazon.awssdk:cloudwatch` from 2.38.5 to 2.38.6 Updates `software.amazon.awssdk:xray` from 2.38.5 to 2.38.6 Updates `software.amazon.awssdk:sqs` from 2.38.2 to 2.38.6 Updates `software.amazon.awssdk:cloudformation` from 2.38.5 to 2.38.6 Updates `software.amazon.awssdk:sts` from 2.38.5 to 2.38.6 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.38.6 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.38.6 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.38.6 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.38.6 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.38.6 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.38.6 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.38.6 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.38.6 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:xray dependency-version: 2.38.6 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.38.6 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.38.6 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sts dependency-version: 2.38.6 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 824b98da9..9ca4e8dc2 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ 11 1.4.0 3.16.1 - 2.38.5 + 2.38.6 1.9.20.1 diff --git a/pom.xml b/pom.xml index 536ba6002..ba206feae 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ 2.25.2 2.0.17 2.20.1 - 2.38.5 + 2.38.6 2.20.0 2.2.0 UTF-8 diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 7838dfc03..11eedca85 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ 3.6.1 1.14.1 3.14.1 - 2.38.5 + 2.38.6 1.9.20.1 true From f7e3b1bddf79e9031cb8e258a05d4272255638e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 10:59:16 +0100 Subject: [PATCH 39/76] chore: bump org.apache.kafka:kafka-clients from 4.1.0 to 4.1.1 (#2291) Bumps org.apache.kafka:kafka-clients from 4.1.0 to 4.1.1. --- updated-dependencies: - dependency-name: org.apache.kafka:kafka-clients dependency-version: 4.1.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-kafka/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 184025be8..24856f80d 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -24,7 +24,7 @@ org.apache.kafka kafka-clients - 4.1.0 + 4.1.1 org.apache.avro diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index a07d4e0d6..0a76f0d2e 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -34,7 +34,7 @@ - 4.1.0 + 4.1.1 1.12.1 4.33.0 1.1.6 From 4966668537b189e7eb2f10bace1c63516807ffab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 10:59:39 +0100 Subject: [PATCH 40/76] chore: bump sam/build-java21 (#2292) Bumps sam/build-java21 from `51709ae` to `ced5388`. --- updated-dependencies: - dependency-name: sam/build-java21 dependency-version: ced538830c78a0f45f91abe5f7963cb73005ad933aee7dd1160bd77d9fd3c4f3 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/src/test/resources/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/src/test/resources/docker/Dockerfile b/powertools-e2e-tests/src/test/resources/docker/Dockerfile index 1abb53643..7d47b5331 100644 --- a/powertools-e2e-tests/src/test/resources/docker/Dockerfile +++ b/powertools-e2e-tests/src/test/resources/docker/Dockerfile @@ -1,5 +1,5 @@ # Use the official AWS SAM base image for Java 21 -FROM public.ecr.aws/sam/build-java21@sha256:51709ae612478654f833998a3455519d0524157230757cf6327e402213811e38 +FROM public.ecr.aws/sam/build-java21@sha256:ced538830c78a0f45f91abe5f7963cb73005ad933aee7dd1160bd77d9fd3c4f3 # Install GraalVM dependencies RUN curl -4 -L https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_linux-x64_bin.tar.gz | tar -xvz From 14572398c67e2b7e4405aa51ea4bf547cc54b42d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 10:59:57 +0100 Subject: [PATCH 41/76] chore: bump github/codeql-action from 4.31.2 to 4.31.3 (#2293) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.2 to 4.31.3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/0499de31b99561a6d14a36a5f662c2a54f91beee...014f16e7ab1402f30e7c3329d33797e7948572db) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.31.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 996135fa9..bbf9490c4 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5 + uses: github/codeql-action/upload-sarif@014f16e7ab1402f30e7c3329d33797e7948572db # v3.29.5 with: sarif_file: results.sarif From f449a570236ed51ddb7f35f7a35835c5a66b26bf Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Tue, 18 Nov 2025 18:00:51 +0100 Subject: [PATCH 42/76] chore(ci): Enable Java 25 E2E tests on Java 25 Lambda runtime and upgrade to GraalVM 25. (#2298) * chore(ci): Enable Java 25 E2E tests on Java 25 Lambda runtime. * Only run for GraalVM 25. * Retain log groups according to retention policy of E2E tests to allow for easier debugging. * Add unsafeAllocated: true to allow native calls from Lambda runtime. --- .github/workflows/check-e2e.yml | 9 ++++----- .../reflect-config.json | 18 ++++++++++++++---- .../reflect-config.json | 18 ++++++++++++++---- .../reflect-config.json | 18 ++++++++++++++---- .../reflect-config.json | 18 ++++++++++++++---- .../reflect-config.json | 16 +++++++++++++--- .../reflect-config.json | 3 ++- .../reflect-config.json | 3 ++- .../reflect-config.json | 3 ++- .../reflect-config.json | 3 ++- .../reflect-config.json | 3 ++- .../reflect-config.json | 3 ++- .../reflect-config.json | 3 ++- .../reflect-config.json | 3 ++- powertools-e2e-tests/handlers/pom.xml | 9 +++++++++ .../reflect-config.json | 3 ++- .../powertools/testutils/Infrastructure.java | 4 +++- .../powertools/testutils/JavaRuntime.java | 3 ++- .../src/test/resources/docker/Dockerfile | 8 ++++---- 19 files changed, 109 insertions(+), 39 deletions(-) diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml index a3c4a7542..54e7cd023 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -48,12 +48,13 @@ jobs: environment: E2E strategy: fail-fast: false - max-parallel: 3 + max-parallel: 4 matrix: java: - 11 - 17 - 21 + - 25 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -81,12 +82,10 @@ jobs: environment: E2E strategy: fail-fast: false - max-parallel: 3 + max-parallel: 1 matrix: java: - - 11 - - 17 - - 21 + - 25 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 diff --git a/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json index 10152cc64..e97baa294 100644 --- a/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json +++ b/examples/powertools-examples-cloudformation/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -27,8 +27,18 @@ "fields":[{"name":"theUnsafe"}] }, { - "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", - "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}], - "allPublicMethods":true + "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields": [ + { "name": "id" }, + { "name": "invokedFunctionArn" }, + { "name": "deadlineTimeInMs" }, + { "name": "xrayTraceId" }, + { "name": "clientContext" }, + { "name": "cognitoIdentity" }, + { "name": "tenantId" }, + { "name": "content" } + ], + "allPublicMethods": true, + "unsafeAllocated": true } -] \ No newline at end of file +] diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json index 10152cc64..e97baa294 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json +++ b/examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -27,8 +27,18 @@ "fields":[{"name":"theUnsafe"}] }, { - "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", - "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}], - "allPublicMethods":true + "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields": [ + { "name": "id" }, + { "name": "invokedFunctionArn" }, + { "name": "deadlineTimeInMs" }, + { "name": "xrayTraceId" }, + { "name": "clientContext" }, + { "name": "cognitoIdentity" }, + { "name": "tenantId" }, + { "name": "content" } + ], + "allPublicMethods": true, + "unsafeAllocated": true } -] \ No newline at end of file +] diff --git a/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json index 10152cc64..e97baa294 100644 --- a/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json +++ b/examples/powertools-examples-idempotency/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -27,8 +27,18 @@ "fields":[{"name":"theUnsafe"}] }, { - "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", - "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}], - "allPublicMethods":true + "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields": [ + { "name": "id" }, + { "name": "invokedFunctionArn" }, + { "name": "deadlineTimeInMs" }, + { "name": "xrayTraceId" }, + { "name": "clientContext" }, + { "name": "cognitoIdentity" }, + { "name": "tenantId" }, + { "name": "content" } + ], + "allPublicMethods": true, + "unsafeAllocated": true } -] \ No newline at end of file +] diff --git a/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json index 10152cc64..e97baa294 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json +++ b/examples/powertools-examples-parameters/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -27,8 +27,18 @@ "fields":[{"name":"theUnsafe"}] }, { - "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", - "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}], - "allPublicMethods":true + "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields": [ + { "name": "id" }, + { "name": "invokedFunctionArn" }, + { "name": "deadlineTimeInMs" }, + { "name": "xrayTraceId" }, + { "name": "clientContext" }, + { "name": "cognitoIdentity" }, + { "name": "tenantId" }, + { "name": "content" } + ], + "allPublicMethods": true, + "unsafeAllocated": true } -] \ No newline at end of file +] diff --git a/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json index 8d3f375f2..7d38fc57d 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json +++ b/examples/powertools-examples-serialization/sam-graalvm/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -27,9 +27,19 @@ "fields":[{"name":"theUnsafe"}] }, { - "name":"com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", - "fields":[{"name":"id"}, {"name":"invokedFunctionArn"}, {"name":"deadlineTimeInMs"}, {"name":"xrayTraceId"}, {"name":"clientContext"}, {"name":"cognitoIdentity"}, {"name": "tenantId"}, {"name":"content"}], - "allPublicMethods":true + "name": "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest", + "fields": [ + { "name": "id" }, + { "name": "invokedFunctionArn" }, + { "name": "deadlineTimeInMs" }, + { "name": "xrayTraceId" }, + { "name": "clientContext" }, + { "name": "cognitoIdentity" }, + { "name": "tenantId" }, + { "name": "content" } + ], + "allPublicMethods": true, + "unsafeAllocated": true }, { "name":"software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor", diff --git a/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json index e69fa735c..467af67a0 100644 --- a/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json +++ b/powertools-e2e-tests/handlers/idempotency-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -56,6 +56,7 @@ { "name": "tenantId" }, { "name": "content" } ], - "allPublicMethods": true + "allPublicMethods": true, + "unsafeAllocated": true } ] diff --git a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json index e69fa735c..467af67a0 100644 --- a/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json +++ b/powertools-e2e-tests/handlers/idempotency-generics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -56,6 +56,7 @@ { "name": "tenantId" }, { "name": "content" } ], - "allPublicMethods": true + "allPublicMethods": true, + "unsafeAllocated": true } ] diff --git a/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json index e69fa735c..467af67a0 100644 --- a/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json +++ b/powertools-e2e-tests/handlers/idempotency/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -56,6 +56,7 @@ { "name": "tenantId" }, { "name": "content" } ], - "allPublicMethods": true + "allPublicMethods": true, + "unsafeAllocated": true } ] diff --git a/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json index e69fa735c..467af67a0 100644 --- a/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json +++ b/powertools-e2e-tests/handlers/logging-functional/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -56,6 +56,7 @@ { "name": "tenantId" }, { "name": "content" } ], - "allPublicMethods": true + "allPublicMethods": true, + "unsafeAllocated": true } ] diff --git a/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json index e69fa735c..467af67a0 100644 --- a/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json +++ b/powertools-e2e-tests/handlers/logging-log4j/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -56,6 +56,7 @@ { "name": "tenantId" }, { "name": "content" } ], - "allPublicMethods": true + "allPublicMethods": true, + "unsafeAllocated": true } ] diff --git a/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json index e69fa735c..467af67a0 100644 --- a/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json +++ b/powertools-e2e-tests/handlers/logging-logback/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -56,6 +56,7 @@ { "name": "tenantId" }, { "name": "content" } ], - "allPublicMethods": true + "allPublicMethods": true, + "unsafeAllocated": true } ] diff --git a/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json index e69fa735c..467af67a0 100644 --- a/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json +++ b/powertools-e2e-tests/handlers/metrics/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -56,6 +56,7 @@ { "name": "tenantId" }, { "name": "content" } ], - "allPublicMethods": true + "allPublicMethods": true, + "unsafeAllocated": true } ] diff --git a/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json index e69fa735c..467af67a0 100644 --- a/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json +++ b/powertools-e2e-tests/handlers/parameters/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -56,6 +56,7 @@ { "name": "tenantId" }, { "name": "content" } ], - "allPublicMethods": true + "allPublicMethods": true, + "unsafeAllocated": true } ] diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 11eedca85..ef1d3e65f 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -261,6 +261,15 @@ 1.9.21 + + jdk25 + + [25,) + + + 1.9.25 + + diff --git a/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json b/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json index e69fa735c..467af67a0 100644 --- a/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json +++ b/powertools-e2e-tests/handlers/tracing/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json @@ -56,6 +56,7 @@ { "name": "tenantId" }, { "name": "content" } ], - "allPublicMethods": true + "allPublicMethods": true, + "unsafeAllocated": true } ] diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/Infrastructure.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/Infrastructure.java index ea5ac3342..ae96943c2 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/Infrastructure.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/Infrastructure.java @@ -247,7 +247,7 @@ private Stack createStackWithLambda() { .create(e2eStack, functionName + "-logs") .logGroupName("/aws/lambda/" + functionName) .retention(RetentionDays.ONE_DAY) - .removalPolicy(RemovalPolicy.DESTROY) + .removalPolicy(RemovalPolicy.RETAIN) .build(); if (!StringUtils.isEmpty(idempotencyTable)) { @@ -522,6 +522,8 @@ private JavaRuntime mapRuntimeVersion(String environmentVariableName) { ret = JavaRuntime.JAVA17; } else if (javaVersion.startsWith("21")) { ret = JavaRuntime.JAVA21; + } else if (javaVersion.startsWith("25")) { + ret = JavaRuntime.JAVA25; } else { throw new IllegalArgumentException("Unsupported Java version " + javaVersion); } diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/JavaRuntime.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/JavaRuntime.java index 53d35e86d..625a222aa 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/JavaRuntime.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/JavaRuntime.java @@ -19,7 +19,8 @@ public enum JavaRuntime { JAVA11("java11", Runtime.JAVA_11, "11"), JAVA17("java17", Runtime.JAVA_17, "17"), - JAVA21("java21", Runtime.JAVA_21, "21"); + JAVA21("java21", Runtime.JAVA_21, "21"), + JAVA25("java25", Runtime.JAVA_25, "25"); private final String runtime; private final Runtime cdkRuntime; diff --git a/powertools-e2e-tests/src/test/resources/docker/Dockerfile b/powertools-e2e-tests/src/test/resources/docker/Dockerfile index 7d47b5331..5d374eaa8 100644 --- a/powertools-e2e-tests/src/test/resources/docker/Dockerfile +++ b/powertools-e2e-tests/src/test/resources/docker/Dockerfile @@ -1,9 +1,9 @@ -# Use the official AWS SAM base image for Java 21 -FROM public.ecr.aws/sam/build-java21@sha256:ced538830c78a0f45f91abe5f7963cb73005ad933aee7dd1160bd77d9fd3c4f3 +# Use the official AWS SAM base image for Java 25 +FROM public.ecr.aws/sam/build-java25@sha256:2ef9e5b950cc79489691be16c7edff904bf196955633dc7fbbc282a1ea421ba8 # Install GraalVM dependencies -RUN curl -4 -L 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 From 044b96d149816f52a247be42d929241380bfd7ba Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Fri, 21 Nov 2025 10:37:45 +0100 Subject: [PATCH 43/76] improv(batch): Propagate trace entity to worker threads during parallel batch processing. (#2300) --- docs/utilities/batch.md | 82 +++++++++++++++++- .../handler/DynamoDbBatchMessageHandler.java | 54 ++++++++---- .../KinesisStreamsBatchMessageHandler.java | 60 +++++++++----- .../batch/handler/SqsBatchMessageHandler.java | 70 ++++++++++------ .../internal/XRayTraceEntityPropagator.java | 83 +++++++++++++++++++ 5 files changed, 288 insertions(+), 61 deletions(-) create mode 100644 powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/internal/XRayTraceEntityPropagator.java diff --git a/docs/utilities/batch.md b/docs/utilities/batch.md index 3f9b6e53d..b535a90f6 100644 --- a/docs/utilities/batch.md +++ b/docs/utilities/batch.md @@ -484,7 +484,9 @@ used with SQS FIFO. In that case, an `UnsupportedOperationException` is thrown. in most cases the defaults work well, and changing them is more likely to decrease performance (see [here](https://www.baeldung.com/java-when-to-use-parallel-stream#fork-join-framework) and [here](https://dzone.com/articles/be-aware-of-forkjoinpoolcommonpool)). - In situations where this may be useful - such as performing IO-bound work in parallel - make sure to measure before and after! + In situations where this may be useful, such as performing IO-bound work in parallel, make sure to measure before and after! + +When using parallel processing with X-Ray tracing enabled, the Tracing utility automatically handles trace context propagation to worker threads. This ensures that subsegments created during parallel message processing appear under the correct parent segment in your X-Ray trace, maintaining proper trace hierarchy and visibility into your batch processing performance. === "Example with SQS" @@ -536,6 +538,84 @@ used with SQS FIFO. In that case, an `UnsupportedOperationException` is thrown. } ``` +=== "Example with X-Ray Tracing" + + ```java hl_lines="12 17" + public class SqsBatchHandler implements RequestHandler { + + private final BatchMessageHandler handler; + + public SqsBatchHandler() { + handler = new BatchMessageHandlerBuilder() + .withSqsBatchHandler() + .buildWithMessageHandler(this::processMessage, Product.class); + } + + @Override + @Tracing + public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) { + return handler.processBatchInParallel(sqsEvent, context); + } + + @Tracing // This will appear correctly under the handleRequest subsegment + private void processMessage(Product p, Context c) { + // Process the product - subsegments will appear under handleRequest + } + } + ``` + +### Choosing the right concurrency model + +The `processBatchInParallel` method has two overloads with different concurrency characteristics: + +#### Without custom executor (parallelStream) + +When you call `processBatchInParallel(event, context)` without providing an executor, the implementation uses Java's `parallelStream()` which leverages the common `ForkJoinPool`. + +**Best for: CPU-bound workloads** + +- Thread pool size matches available CPU cores +- Optimized for computational tasks (data transformation, calculations, parsing) +- Main thread participates in work-stealing +- Simple to use with no configuration needed + +```java +// Good for CPU-intensive processing +return handler.processBatchInParallel(sqsEvent, context); +``` + +#### With custom executor (CompletableFuture) + +When you call `processBatchInParallel(event, context, executor)` with a custom executor, the implementation uses `CompletableFuture` which gives you full control over the thread pool. + +**Best for: I/O-bound workloads** + +- You control thread pool size and characteristics +- Ideal for I/O operations (HTTP calls, database queries, S3 operations) +- Can use larger thread pools since threads spend time waiting, not computing +- Main thread only waits; worker threads do all processing + +```java +// Good for I/O-intensive processing (API calls, DB queries, etc.) +ExecutorService executor = Executors.newFixedThreadPool(50); +return handler.processBatchInParallel(sqsEvent, context, executor); +``` + +**For Java 21+: Virtual Threads** + +If you're using Java 21 or later, virtual threads are ideal for I/O-bound workloads: + +```java +ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); +return handler.processBatchInParallel(sqsEvent, context, executor); +``` + +Virtual threads are lightweight and can handle thousands of concurrent I/O operations efficiently without the overhead of platform threads. + +**Recommendation for typical Lambda SQS processing:** + +Most Lambda functions processing SQS messages perform I/O operations (calling APIs, querying databases, writing to S3). For these workloads, use the custom executor approach with a thread pool sized appropriately for your I/O operations or virtual threads for Java 21+. + ## Handling Messages diff --git a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/DynamoDbBatchMessageHandler.java b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/DynamoDbBatchMessageHandler.java index df7179a88..dbfdf63cd 100644 --- a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/DynamoDbBatchMessageHandler.java +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/DynamoDbBatchMessageHandler.java @@ -14,21 +14,25 @@ package software.amazon.lambda.powertools.batch.handler; -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.DynamodbEvent; -import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; - import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.stream.Collectors; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.DynamodbEvent; +import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; + import software.amazon.lambda.powertools.batch.internal.MultiThreadMDC; +import software.amazon.lambda.powertools.batch.internal.XRayTraceEntityPropagator; /** * A batch message processor for DynamoDB Streams batches. @@ -43,8 +47,8 @@ public class DynamoDbBatchMessageHandler implements BatchMessageHandler rawMessageHandler; public DynamoDbBatchMessageHandler(Consumer successHandler, - BiConsumer failureHandler, - BiConsumer rawMessageHandler) { + BiConsumer failureHandler, + BiConsumer rawMessageHandler) { this.successHandler = successHandler; this.failureHandler = failureHandler; this.rawMessageHandler = rawMessageHandler; @@ -65,14 +69,23 @@ public StreamsEventResponse processBatch(DynamodbEvent event, Context context) { @Override public StreamsEventResponse processBatchInParallel(DynamodbEvent event, Context context) { MultiThreadMDC multiThreadMDC = new MultiThreadMDC(); + Object capturedSubsegment = XRayTraceEntityPropagator.captureTraceEntity(); List batchItemFailures = event.getRecords() .parallelStream() // Parallel processing .map(eventRecord -> { - multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); - Optional failureOpt = processBatchItem(eventRecord, context); - multiThreadMDC.removeThread(Thread.currentThread().getName()); - return failureOpt; + AtomicReference> result = new AtomicReference<>(); + + XRayTraceEntityPropagator.runWithEntity(capturedSubsegment, () -> { + multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); + try { + result.set(processBatchItem(eventRecord, context)); + } finally { + multiThreadMDC.removeThread(Thread.currentThread().getName()); + } + }); + + return result.get(); }) .filter(Optional::isPresent) .map(Optional::get) @@ -84,21 +97,29 @@ public StreamsEventResponse processBatchInParallel(DynamodbEvent event, Context @Override public StreamsEventResponse processBatchInParallel(DynamodbEvent event, Context context, Executor executor) { MultiThreadMDC multiThreadMDC = new MultiThreadMDC(); + Object capturedSubsegment = XRayTraceEntityPropagator.captureTraceEntity(); List batchItemFailures = new ArrayList<>(); List> futures = event.getRecords().stream() .map(eventRecord -> CompletableFuture.runAsync(() -> { - multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); - Optional failureOpt = processBatchItem(eventRecord, context); - failureOpt.ifPresent(batchItemFailures::add); - multiThreadMDC.removeThread(Thread.currentThread().getName()); + XRayTraceEntityPropagator.runWithEntity(capturedSubsegment, () -> { + multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); + try { + Optional failureOpt = processBatchItem(eventRecord, + context); + failureOpt.ifPresent(batchItemFailures::add); + } finally { + multiThreadMDC.removeThread(Thread.currentThread().getName()); + } + }); }, executor)) .collect(Collectors.toList()); futures.forEach(CompletableFuture::join); return StreamsEventResponse.builder().withBatchItemFailures(batchItemFailures).build(); } - private Optional processBatchItem(DynamodbEvent.DynamodbStreamRecord streamRecord, Context context) { + private Optional processBatchItem( + DynamodbEvent.DynamodbStreamRecord streamRecord, Context context) { try { LOGGER.debug("Processing item {}", streamRecord.getEventID()); @@ -124,7 +145,8 @@ private Optional processBatchItem(Dynamod LOGGER.warn("failureHandler threw handling failure", e2); } } - return Optional.of(StreamsEventResponse.BatchItemFailure.builder().withItemIdentifier(sequenceNumber).build()); + return Optional + .of(StreamsEventResponse.BatchItemFailure.builder().withItemIdentifier(sequenceNumber).build()); } } } diff --git a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/KinesisStreamsBatchMessageHandler.java b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/KinesisStreamsBatchMessageHandler.java index 233830462..f147578d4 100644 --- a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/KinesisStreamsBatchMessageHandler.java +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/KinesisStreamsBatchMessageHandler.java @@ -14,22 +14,25 @@ package software.amazon.lambda.powertools.batch.handler; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.KinesisEvent; -import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; - import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.stream.Collectors; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.KinesisEvent; +import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; + import software.amazon.lambda.powertools.batch.internal.MultiThreadMDC; +import software.amazon.lambda.powertools.batch.internal.XRayTraceEntityPropagator; import software.amazon.lambda.powertools.utilities.EventDeserializer; /** @@ -49,10 +52,10 @@ public class KinesisStreamsBatchMessageHandler implements BatchMessageHandler private final BiConsumer failureHandler; public KinesisStreamsBatchMessageHandler(BiConsumer rawMessageHandler, - BiConsumer messageHandler, - Class messageClass, - Consumer successHandler, - BiConsumer failureHandler) { + BiConsumer messageHandler, + Class messageClass, + Consumer successHandler, + BiConsumer failureHandler) { this.rawMessageHandler = rawMessageHandler; this.messageHandler = messageHandler; @@ -76,14 +79,23 @@ public StreamsEventResponse processBatch(KinesisEvent event, Context context) { @Override public StreamsEventResponse processBatchInParallel(KinesisEvent event, Context context) { MultiThreadMDC multiThreadMDC = new MultiThreadMDC(); + Object capturedSubsegment = XRayTraceEntityPropagator.captureTraceEntity(); List batchItemFailures = event.getRecords() .parallelStream() // Parallel processing .map(eventRecord -> { - multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); - Optional failureOpt = processBatchItem(eventRecord, context); - multiThreadMDC.removeThread(Thread.currentThread().getName()); - return failureOpt; + AtomicReference> result = new AtomicReference<>(); + + XRayTraceEntityPropagator.runWithEntity(capturedSubsegment, () -> { + multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); + try { + result.set(processBatchItem(eventRecord, context)); + } finally { + multiThreadMDC.removeThread(Thread.currentThread().getName()); + } + }); + + return result.get(); }) .filter(Optional::isPresent) .map(Optional::get) @@ -95,21 +107,29 @@ public StreamsEventResponse processBatchInParallel(KinesisEvent event, Context c @Override public StreamsEventResponse processBatchInParallel(KinesisEvent event, Context context, Executor executor) { MultiThreadMDC multiThreadMDC = new MultiThreadMDC(); + Object capturedSubsegment = XRayTraceEntityPropagator.captureTraceEntity(); List batchItemFailures = new ArrayList<>(); List> futures = event.getRecords().stream() .map(eventRecord -> CompletableFuture.runAsync(() -> { - multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); - Optional failureOpt = processBatchItem(eventRecord, context); - failureOpt.ifPresent(batchItemFailures::add); - multiThreadMDC.removeThread(Thread.currentThread().getName()); + XRayTraceEntityPropagator.runWithEntity(capturedSubsegment, () -> { + multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); + try { + Optional failureOpt = processBatchItem(eventRecord, + context); + failureOpt.ifPresent(batchItemFailures::add); + } finally { + multiThreadMDC.removeThread(Thread.currentThread().getName()); + } + }); }, executor)) .collect(Collectors.toList()); futures.forEach(CompletableFuture::join); return StreamsEventResponse.builder().withBatchItemFailures(batchItemFailures).build(); } - private Optional processBatchItem(KinesisEvent.KinesisEventRecord eventRecord, Context context) { + private Optional processBatchItem( + KinesisEvent.KinesisEventRecord eventRecord, Context context) { try { LOGGER.debug("Processing item {}", eventRecord.getEventID()); @@ -141,8 +161,8 @@ private Optional processBatchItem(Kinesis } } - return Optional.of(StreamsEventResponse.BatchItemFailure.builder().withItemIdentifier(eventRecord.getKinesis().getSequenceNumber()).build()); + return Optional.of(StreamsEventResponse.BatchItemFailure.builder() + .withItemIdentifier(eventRecord.getKinesis().getSequenceNumber()).build()); } } } - diff --git a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/SqsBatchMessageHandler.java b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/SqsBatchMessageHandler.java index ccb6a6dd7..737c7cceb 100644 --- a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/SqsBatchMessageHandler.java +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/handler/SqsBatchMessageHandler.java @@ -14,24 +14,26 @@ package software.amazon.lambda.powertools.batch.handler; -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.KinesisEvent; -import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.stream.Collectors; -import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; + import software.amazon.lambda.powertools.batch.internal.MultiThreadMDC; +import software.amazon.lambda.powertools.batch.internal.XRayTraceEntityPropagator; import software.amazon.lambda.powertools.utilities.EventDeserializer; /** @@ -54,9 +56,9 @@ public class SqsBatchMessageHandler implements BatchMessageHandler failureHandler; public SqsBatchMessageHandler(BiConsumer messageHandler, Class messageClass, - BiConsumer rawMessageHandler, - Consumer successHandler, - BiConsumer failureHandler) { + BiConsumer rawMessageHandler, + Consumer successHandler, + BiConsumer failureHandler) { this.messageHandler = messageHandler; this.messageClass = messageClass; this.rawMessageHandler = rawMessageHandler; @@ -77,16 +79,16 @@ public SQSBatchResponse processBatch(SQSEvent event, Context context) { for (; messageCursor < event.getRecords().size() && !failWholeBatch.get(); messageCursor++) { SQSEvent.SQSMessage message = event.getRecords().get(messageCursor); - String messageGroupId = message.getAttributes() != null ? - message.getAttributes().get(MESSAGE_GROUP_ID_KEY) : null; + String messageGroupId = message.getAttributes() != null ? message.getAttributes().get(MESSAGE_GROUP_ID_KEY) + : null; processBatchItem(message, context).ifPresent(batchItemFailure -> { response.getBatchItemFailures().add(batchItemFailure); if (messageGroupId != null) { failWholeBatch.set(true); LOGGER.info( - "A message in a batch with messageGroupId {} and messageId {} failed; failing the rest of the batch too" - , messageGroupId, message.getMessageId()); + "A message in a batch with messageGroupId {} and messageId {} failed; failing the rest of the batch too", + messageGroupId, message.getMessageId()); } }); } @@ -105,18 +107,28 @@ public SQSBatchResponse processBatch(SQSEvent event, Context context) { @Override public SQSBatchResponse processBatchInParallel(SQSEvent event, Context context) { if (isFIFOEnabled(event)) { - throw new UnsupportedOperationException("FIFO queues are not supported in parallel mode, use the processBatch method instead"); + throw new UnsupportedOperationException( + "FIFO queues are not supported in parallel mode, use the processBatch method instead"); } MultiThreadMDC multiThreadMDC = new MultiThreadMDC(); + Object capturedSubsegment = XRayTraceEntityPropagator.captureTraceEntity(); + List batchItemFailures = event.getRecords() .parallelStream() // Parallel processing .map(sqsMessage -> { - - multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); - Optional failureOpt = processBatchItem(sqsMessage, context); - multiThreadMDC.removeThread(Thread.currentThread().getName()); - return failureOpt; + AtomicReference> result = new AtomicReference<>(); + + XRayTraceEntityPropagator.runWithEntity(capturedSubsegment, () -> { + multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); + try { + result.set(processBatchItem(sqsMessage, context)); + } finally { + multiThreadMDC.removeThread(Thread.currentThread().getName()); + } + }); + + return result.get(); }) .filter(Optional::isPresent) .map(Optional::get) @@ -128,17 +140,26 @@ public SQSBatchResponse processBatchInParallel(SQSEvent event, Context context) @Override public SQSBatchResponse processBatchInParallel(SQSEvent event, Context context, Executor executor) { if (isFIFOEnabled(event)) { - throw new UnsupportedOperationException("FIFO queues are not supported in parallel mode, use the processBatch method instead"); + throw new UnsupportedOperationException( + "FIFO queues are not supported in parallel mode, use the processBatch method instead"); } MultiThreadMDC multiThreadMDC = new MultiThreadMDC(); + Object capturedSubsegment = XRayTraceEntityPropagator.captureTraceEntity(); + List batchItemFailures = new ArrayList<>(); List> futures = event.getRecords().stream() .map(eventRecord -> CompletableFuture.runAsync(() -> { - multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); - Optional failureOpt = processBatchItem(eventRecord, context); - failureOpt.ifPresent(batchItemFailures::add); - multiThreadMDC.removeThread(Thread.currentThread().getName()); + XRayTraceEntityPropagator.runWithEntity(capturedSubsegment, () -> { + multiThreadMDC.copyMDCToThread(Thread.currentThread().getName()); + try { + Optional failureOpt = processBatchItem(eventRecord, + context); + failureOpt.ifPresent(batchItemFailures::add); + } finally { + multiThreadMDC.removeThread(Thread.currentThread().getName()); + } + }); }, executor)) .collect(Collectors.toList()); futures.forEach(CompletableFuture::join); @@ -182,6 +203,7 @@ private Optional processBatchItem(SQSEvent.SQ } private boolean isFIFOEnabled(SQSEvent sqsEvent) { - return !sqsEvent.getRecords().isEmpty() && sqsEvent.getRecords().get(0).getAttributes().get(MESSAGE_GROUP_ID_KEY) != null; + return !sqsEvent.getRecords().isEmpty() + && sqsEvent.getRecords().get(0).getAttributes().get(MESSAGE_GROUP_ID_KEY) != null; } } diff --git a/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/internal/XRayTraceEntityPropagator.java b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/internal/XRayTraceEntityPropagator.java new file mode 100644 index 000000000..2858f4756 --- /dev/null +++ b/powertools-batch/src/main/java/software/amazon/lambda/powertools/batch/internal/XRayTraceEntityPropagator.java @@ -0,0 +1,83 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package software.amazon.lambda.powertools.batch.internal; + +import java.lang.reflect.Method; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utility class to propagate X-Ray trace entity context to worker threads using reflection. + * Reflection is used to avoid taking a dependency on X-RAY SDK. + */ +public final class XRayTraceEntityPropagator { + private static final Logger LOGGER = LoggerFactory.getLogger(XRayTraceEntityPropagator.class); + private static final boolean XRAY_AVAILABLE; + private static final Method GET_TRACE_ENTITY_METHOD; + + // We do the more "expensive" Class.forName in this static block to detect exactly once at import time if X-RAY + // is available or not. Subsequent .invoke() are very fast on modern JDKs. + static { + Method method = null; + boolean available = false; + + try { + Class awsXRayClass = Class.forName("com.amazonaws.xray.AWSXRay"); + method = awsXRayClass.getMethod("getTraceEntity"); + available = true; + LOGGER.debug("X-Ray SDK detected. Trace context will be propagated to worker threads."); + } catch (ClassNotFoundException | NoSuchMethodException e) { + LOGGER.debug("X-Ray SDK not detected. Trace context propagation disabled"); + } + + GET_TRACE_ENTITY_METHOD = method; + XRAY_AVAILABLE = available; + } + + private XRayTraceEntityPropagator() { + // Utility class + } + + public static Object captureTraceEntity() { + if (!XRAY_AVAILABLE) { + return null; + } + + try { + return GET_TRACE_ENTITY_METHOD.invoke(null); + } catch (Exception e) { + // We don't want to break batch processing if this fails. + LOGGER.warn("Failed to capture trace entity.", e); + return null; + } + } + + // See https://docs.aws.amazon.com/xray/latest/devguide/scorekeep-workerthreads.html + public static void runWithEntity(Object traceEntity, Runnable runnable) { + if (!XRAY_AVAILABLE || traceEntity == null) { + runnable.run(); + return; + } + + try { + traceEntity.getClass().getMethod("run", Runnable.class).invoke(traceEntity, runnable); + } catch (Exception e) { + // We don't want to break batch processing if this fails. + LOGGER.warn("Failed to run with trace entity, falling back to direct execution.", e); + runnable.run(); + } + } +} From 39c26fe3db8bc7c8869e7e1c0900fdb16d03915b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 10:38:12 +0100 Subject: [PATCH 44/76] chore: bump io.github.ascopes:protobuf-maven-plugin (#2296) Bumps [io.github.ascopes:protobuf-maven-plugin](https://github.com/ascopes/protobuf-maven-plugin) from 3.10.2 to 3.10.3. - [Release notes](https://github.com/ascopes/protobuf-maven-plugin/releases) - [Commits](https://github.com/ascopes/protobuf-maven-plugin/compare/v3.10.2...v3.10.3) --- updated-dependencies: - dependency-name: io.github.ascopes:protobuf-maven-plugin dependency-version: 3.10.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-kafka/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 24856f80d..1e7450679 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -141,7 +141,7 @@ io.github.ascopes protobuf-maven-plugin - 3.10.2 + 3.10.3 diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index 0a76f0d2e..0d7a4c13b 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -200,7 +200,7 @@ io.github.ascopes protobuf-maven-plugin - 3.10.2 + 3.10.3 generate-test-sources From 3575cbfa1377480ea212cefd84d7f75755c8565c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 10:39:11 +0100 Subject: [PATCH 45/76] chore: bump software.amazon.awscdk:aws-cdk-lib from 2.223.0 to 2.224.0 (#2297) Bumps [software.amazon.awscdk:aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.223.0 to 2.224.0. - [Release notes](https://github.com/aws/aws-cdk/releases) - [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.alpha.md) - [Commits](https://github.com/aws/aws-cdk/compare/v2.223.0...v2.224.0) --- updated-dependencies: - dependency-name: software.amazon.awscdk:aws-cdk-lib dependency-version: 2.224.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 87dc26169..35e07bc62 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -7,7 +7,7 @@ 2.7.0 UTF-8 - 2.223.0 + 2.224.0 [10.0.0,11.0.0) 5.14.1 diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 6dd94910a..0fd169845 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -31,7 +31,7 @@ 11 11 10.4.3 - 2.223.0 + 2.224.0 From 0c0401f6ca9ca15e5bf75a46c151081ab009a459 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 10:39:33 +0100 Subject: [PATCH 46/76] chore: bump github/codeql-action from 4.31.3 to 4.31.4 (#2301) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.3 to 4.31.4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/014f16e7ab1402f30e7c3329d33797e7948572db...e12f0178983d466f2f6028f5cc7a6d786fd97f4b) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.31.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index bbf9490c4..205cb0ddc 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@014f16e7ab1402f30e7c3329d33797e7948572db # v3.29.5 + uses: github/codeql-action/upload-sarif@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v3.29.5 with: sarif_file: results.sarif From 5b3f0f583f46033edf5512d1b4472993f01fee6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 10:39:52 +0100 Subject: [PATCH 47/76] chore: bump aws.sdk.version from 2.38.2 to 2.38.7 (#2295) Bumps `aws.sdk.version` from 2.38.2 to 2.38.7. Updates `software.amazon.awssdk:url-connection-client` from 2.38.2 to 2.38.7 Updates `software.amazon.awssdk:sdk-core` from 2.38.2 to 2.38.7 Updates `software.amazon.awssdk:s3` from 2.38.2 to 2.38.7 Updates `software.amazon.awssdk:kinesis` from 2.38.2 to 2.38.7 Updates `software.amazon.awssdk:sqs` from 2.38.2 to 2.38.7 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.38.2 to 2.38.7 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.38.7 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sdk-core dependency-version: 2.38.7 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.38.7 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.38.7 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.38.7 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-version: 2.38.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 5226166fb..ec075cf87 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ 11 11 1.9.20.1 - 2.38.2 + 2.38.7 From e523a7eb42ddc7c7353f777a47a9167f7f46f803 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 12:27:48 +0100 Subject: [PATCH 48/76] chore(ci): bump version to 2.8.0 (#2303) * chore(ci): bump version to 2.8.0 * Restore CHANGELOG.md from main. --------- Co-authored-by: Powertools for AWS Lambda (Java) Bot <151832416+aws-powertools-bot@users.noreply.github.com> Co-authored-by: Philipp Page --- README.md | 6 +++--- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/README.md | 4 ++-- .../infra/sam-graalvm/README.md | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- .../powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- .../powertools-examples-core-utilities/gradle/build.gradle | 6 +++--- .../kotlin/build.gradle.kts | 6 +++--- .../powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- .../powertools-examples-core-utilities/serverless/pom.xml | 2 +- .../powertools-examples-core-utilities/terraform/pom.xml | 2 +- .../powertools-examples-idempotency/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-idempotency/sam/pom.xml | 2 +- examples/powertools-examples-kafka/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- .../powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-common/pom.xml | 2 +- powertools-e2e-tests/handlers/batch/pom.xml | 2 +- .../handlers/idempotency-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/idempotency-generics/pom.xml | 2 +- powertools-e2e-tests/handlers/idempotency/pom.xml | 2 +- .../handlers/largemessage-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/largemessage/pom.xml | 2 +- .../handlers/largemessage_idempotent/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-log4j/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-logback/pom.xml | 2 +- powertools-e2e-tests/handlers/metrics/pom.xml | 2 +- powertools-e2e-tests/handlers/parameters/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/handlers/tracing/pom.xml | 2 +- powertools-e2e-tests/handlers/validation-alb-event/pom.xml | 2 +- .../handlers/validation-apigw-event/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-idempotency/powertools-idempotency-core/pom.xml | 2 +- .../powertools-idempotency-dynamodb/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-logging/powertools-logging-log4j/pom.xml | 2 +- powertools-logging/powertools-logging-logback/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- .../powertools-parameters-appconfig/pom.xml | 2 +- .../powertools-parameters-dynamodb/pom.xml | 2 +- powertools-parameters/powertools-parameters-secrets/pom.xml | 2 +- powertools-parameters/powertools-parameters-ssm/pom.xml | 2 +- powertools-parameters/powertools-parameters-tests/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 62 files changed, 69 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index 6d3689092..2644c47ab 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your software.amazon.lambda powertools-tracing - 2.7.0 + 2.8.0 software.amazon.lambda powertools-logging-log4j - 2.7.0 + 2.8.0 software.amazon.lambda powertools-metrics - 2.7.0 + 2.8.0 ... diff --git a/examples/pom.xml b/examples/pom.xml index c02d318cc..511cdd5a9 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ software.amazon.lambda powertools-examples - 2.7.0 + 2.8.0 pom Powertools for AWS Lambda (Java) - Examples diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index ec075cf87..6d941a31f 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ 4.0.0 software.amazon.lambda.examples - 2.7.0 + 2.8.0 powertools-examples-batch jar Powertools for AWS Lambda (Java) - Examples - Batch diff --git a/examples/powertools-examples-cloudformation/README.md b/examples/powertools-examples-cloudformation/README.md index 84d9d7fac..b9e58eb82 100644 --- a/examples/powertools-examples-cloudformation/README.md +++ b/examples/powertools-examples-cloudformation/README.md @@ -15,7 +15,7 @@ Run the following in your shell: ```bash cd infra/sam sam build -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.7.0718 +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.8.0718 ``` ### Deploy with CDK @@ -32,5 +32,5 @@ To build and deploy this application for the first time, run the following in yo cd infra/cdk mvn package cdk synth -cdk deploy -c BucketNameParam=my-unique-bucket-2.7.0718 +cdk deploy -c BucketNameParam=my-unique-bucket-2.8.0718 ``` \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md index 85c8b386f..c2a9e2ffd 100644 --- a/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md +++ b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md @@ -40,7 +40,7 @@ sam build ## Deploy the sample application ```shell -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.7.0718 +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.8.0718 ``` This sample is based on Serverless Application Model (SAM). To deploy it, check out the instructions for getting started with SAM in [the examples directory](../../../README.md) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 9ca4e8dc2..8dde0a034 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ 4.0.0 software.amazon.lambda.examples - 2.7.0 + 2.8.0 powertools-examples-cloudformation jar diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index dbd6743a8..29acef5e2 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda.examples - 2.7.0 + 2.8.0 powertools-examples-core-utilities-cdk jar diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 35e07bc62..7daf3153f 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -4,7 +4,7 @@ 4.0.0 software.amazon.lambda.examples cdk - 2.7.0 + 2.8.0 UTF-8 2.224.0 diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle index 0b7951f11..6841d20ab 100644 --- a/examples/powertools-examples-core-utilities/gradle/build.gradle +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -29,8 +29,8 @@ dependencies { implementation 'com.amazonaws:aws-lambda-java-events:3.16.0' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2' implementation 'org.aspectj:aspectjrt:1.9.20.1' - aspect 'software.amazon.lambda:powertools-tracing:2.7.0' - aspect 'software.amazon.lambda:powertools-logging-log4j:2.7.0' - aspect 'software.amazon.lambda:powertools-metrics:2.7.0' + aspect 'software.amazon.lambda:powertools-tracing:2.8.0' + aspect 'software.amazon.lambda:powertools-logging-log4j:2.8.0' + aspect 'software.amazon.lambda:powertools-metrics:2.8.0' } diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index 47e82c5b8..bc11eb32e 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -15,9 +15,9 @@ dependencies { implementation("com.amazonaws:aws-lambda-java-events:3.16.0") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.2") implementation("org.aspectj:aspectjrt:1.9.20.1") - aspect("software.amazon.lambda:powertools-tracing:2.7.0") - aspect("software.amazon.lambda:powertools-logging-log4j:2.7.0") - aspect("software.amazon.lambda:powertools-metrics:2.7.0") + aspect("software.amazon.lambda:powertools-tracing:2.8.0") + aspect("software.amazon.lambda:powertools-logging-log4j:2.8.0") + aspect("software.amazon.lambda:powertools-metrics:2.8.0") implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.24") } diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index 682e1a10b..fceb4a322 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -4,7 +4,7 @@ Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM GraalVM software.amazon.lambda.examples - 2.7.0 + 2.8.0 powertools-examples-core-utilities-sam-graalvm jar diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index d03cafcfb..b8606a227 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -4,7 +4,7 @@ Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM software.amazon.lambda.examples - 2.7.0 + 2.8.0 powertools-examples-core-utilities-sam jar diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index 996f77d4b..d77b0ccd6 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -4,7 +4,7 @@ Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Serverless software.amazon.lambda.examples - 2.7.0 + 2.8.0 powertools-examples-core-utilities-serverless jar diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index d28835a8a..14c0e223e 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -4,7 +4,7 @@ Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Terraform software.amazon.lambda.examples - 2.7.0 + 2.8.0 powertools-examples-core-utilities-terraform jar diff --git a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml index 205958c9a..d1c0205ff 100644 --- a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml +++ b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.7.0 + 2.8.0 powertools-examples-idempotency-sam-graalvm jar Powertools for AWS Lambda (Java) - Examples - Idempotency GraalVM diff --git a/examples/powertools-examples-idempotency/sam/pom.xml b/examples/powertools-examples-idempotency/sam/pom.xml index 694b79342..b90705c67 100644 --- a/examples/powertools-examples-idempotency/sam/pom.xml +++ b/examples/powertools-examples-idempotency/sam/pom.xml @@ -17,7 +17,7 @@ 4.0.0 software.amazon.lambda.examples - 2.7.0 + 2.8.0 powertools-examples-idempotency jar Powertools for AWS Lambda (Java) - Examples - Idempotency diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 1e7450679..f4b75722c 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.7.0 + 2.8.0 powertools-examples-kafka jar Powertools for AWS Lambda (Java) - Examples - Kafka diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index a83ddddb8..6d0c3f4a2 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.7.0 + 2.8.0 powertools-examples-parameters-sam-graalvm jar Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index 83efef4d2..15d68ffcd 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.7.0 + 2.8.0 powertools-examples-parameters-sam jar Powertools for AWS Lambda (Java) - Examples - Parameters diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index b77ce975e..44661fb71 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.7.0 + 2.8.0 powertools-examples-serialization-sam-graalvm jar Powertools for AWS Lambda (Java) - Examples - Serialization GraalVM diff --git a/examples/powertools-examples-serialization/sam/pom.xml b/examples/powertools-examples-serialization/sam/pom.xml index 789d9969f..93c60a2cb 100644 --- a/examples/powertools-examples-serialization/sam/pom.xml +++ b/examples/powertools-examples-serialization/sam/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.7.0 + 2.8.0 powertools-examples-serialization-sam jar Powertools for AWS Lambda (Java) - Examples - Serialization diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index abcdd4e3a..1337bc2ee 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.7.0 + 2.8.0 powertools-examples-validation jar Powertools for AWS Lambda (Java) - Examples - Validation diff --git a/mkdocs.yml b/mkdocs.yml index 98f6945d7..5f35b96c0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -129,7 +129,7 @@ extra_javascript: extra: powertools: - version: 2.7.0 + version: 2.8.0 version: provider: mike default: latest diff --git a/pom.xml b/pom.xml index ba206feae..ed1d2aff9 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ software.amazon.lambda powertools-parent - 2.7.0 + 2.8.0 pom Powertools for AWS Lambda (Java) - Parent diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index bce58ab58..07d11d41c 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda powertools-parent - 2.7.0 + 2.8.0 A suite of utilities that makes batch message processing using AWS Lambda easier. diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index a3270563a..09e3299fd 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 2.7.0 + 2.8.0 Powertools for AWS Lambda (Java) - Cloudformation diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index d4e9f6213..f14783b5b 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 2.7.0 + 2.8.0 Powertools for AWS Lambda (Java) - Common Internal Utilities diff --git a/powertools-e2e-tests/handlers/batch/pom.xml b/powertools-e2e-tests/handlers/batch/pom.xml index 2c726340a..f8e0a037f 100644 --- a/powertools-e2e-tests/handlers/batch/pom.xml +++ b/powertools-e2e-tests/handlers/batch/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.7.0 + 2.8.0 e2e-test-handler-batch diff --git a/powertools-e2e-tests/handlers/idempotency-functional/pom.xml b/powertools-e2e-tests/handlers/idempotency-functional/pom.xml index 133a0ccff..2b53dc275 100644 --- a/powertools-e2e-tests/handlers/idempotency-functional/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency-functional/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.7.0 + 2.8.0 e2e-test-handler-idempotency-functional diff --git a/powertools-e2e-tests/handlers/idempotency-generics/pom.xml b/powertools-e2e-tests/handlers/idempotency-generics/pom.xml index a69babd0d..76e65edb1 100644 --- a/powertools-e2e-tests/handlers/idempotency-generics/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency-generics/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.7.0 + 2.8.0 e2e-test-handler-idempotency-generics diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml index cbe7e0cac..36181ea01 100644 --- a/powertools-e2e-tests/handlers/idempotency/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.7.0 + 2.8.0 e2e-test-handler-idempotency diff --git a/powertools-e2e-tests/handlers/largemessage-functional/pom.xml b/powertools-e2e-tests/handlers/largemessage-functional/pom.xml index e2e67b2da..d29017074 100644 --- a/powertools-e2e-tests/handlers/largemessage-functional/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage-functional/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.7.0 + 2.8.0 e2e-test-handler-largemessage-functional diff --git a/powertools-e2e-tests/handlers/largemessage/pom.xml b/powertools-e2e-tests/handlers/largemessage/pom.xml index 56d179c3b..53b1e7c10 100644 --- a/powertools-e2e-tests/handlers/largemessage/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.7.0 + 2.8.0 e2e-test-handler-largemessage diff --git a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml index 9896db217..9f6f23389 100644 --- a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.7.0 + 2.8.0 e2e-test-handler-large-msg-idempotent diff --git a/powertools-e2e-tests/handlers/logging-functional/pom.xml b/powertools-e2e-tests/handlers/logging-functional/pom.xml index ba532c3db..4616af233 100644 --- a/powertools-e2e-tests/handlers/logging-functional/pom.xml +++ b/powertools-e2e-tests/handlers/logging-functional/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.7.0 + 2.8.0 e2e-test-handler-logging-functional diff --git a/powertools-e2e-tests/handlers/logging-log4j/pom.xml b/powertools-e2e-tests/handlers/logging-log4j/pom.xml index 445da94e2..bba711163 100644 --- a/powertools-e2e-tests/handlers/logging-log4j/pom.xml +++ b/powertools-e2e-tests/handlers/logging-log4j/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.7.0 + 2.8.0 e2e-test-handler-logging-log4j diff --git a/powertools-e2e-tests/handlers/logging-logback/pom.xml b/powertools-e2e-tests/handlers/logging-logback/pom.xml index 9f5035722..bd442fe88 100644 --- a/powertools-e2e-tests/handlers/logging-logback/pom.xml +++ b/powertools-e2e-tests/handlers/logging-logback/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.7.0 + 2.8.0 e2e-test-handler-logging-logback diff --git a/powertools-e2e-tests/handlers/metrics/pom.xml b/powertools-e2e-tests/handlers/metrics/pom.xml index 5bf3bd5ef..90704c6d9 100644 --- a/powertools-e2e-tests/handlers/metrics/pom.xml +++ b/powertools-e2e-tests/handlers/metrics/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.7.0 + 2.8.0 e2e-test-handler-metrics diff --git a/powertools-e2e-tests/handlers/parameters/pom.xml b/powertools-e2e-tests/handlers/parameters/pom.xml index e30a51150..670eeb9c0 100644 --- a/powertools-e2e-tests/handlers/parameters/pom.xml +++ b/powertools-e2e-tests/handlers/parameters/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.7.0 + 2.8.0 e2e-test-handler-parameters diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index ef1d3e65f..f06db02f0 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -4,7 +4,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.7.0 + 2.8.0 pom Handlers for End-to-End tests Fake handlers that use Powertools for AWS Lambda (Java). diff --git a/powertools-e2e-tests/handlers/tracing/pom.xml b/powertools-e2e-tests/handlers/tracing/pom.xml index 1a3b56a77..89891df8c 100644 --- a/powertools-e2e-tests/handlers/tracing/pom.xml +++ b/powertools-e2e-tests/handlers/tracing/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.7.0 + 2.8.0 e2e-test-handler-tracing diff --git a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml index d2e1266fc..8e5e4dadb 100644 --- a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.7.0 + 2.8.0 e2e-test-handler-validation-alb-event diff --git a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml index 6832280a6..97e967c3b 100644 --- a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.7.0 + 2.8.0 e2e-test-handler-validation-apigw-event diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 0fd169845..484e3b4c2 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ powertools-parent software.amazon.lambda - 2.7.0 + 2.8.0 powertools-e2e-tests diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index f119ca445..d15d3445d 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ software.amazon.lambda powertools-parent - 2.7.0 + 2.8.0 powertools-idempotency diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index 5e57ee136..801664b4a 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -21,7 +21,7 @@ software.amazon.lambda powertools-idempotency - 2.7.0 + 2.8.0 powertools-idempotency-core diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index 25f6f77c7..a9cf5739b 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -21,7 +21,7 @@ software.amazon.lambda powertools-idempotency - 2.7.0 + 2.8.0 powertools-idempotency-dynamodb diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index 0d7a4c13b..ad564897f 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -21,7 +21,7 @@ powertools-parent software.amazon.lambda - 2.7.0 + 2.8.0 powertools-kafka diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index 6353cb089..d72877552 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ software.amazon.lambda powertools-parent - 2.7.0 + 2.8.0 powertools-large-messages diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 9fd7b1e62..ae5788291 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -21,7 +21,7 @@ powertools-parent software.amazon.lambda - 2.7.0 + 2.8.0 Powertools for AWS Lambda (Java) - Logging diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index 8006baa7d..1cf3bf265 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 2.7.0 + 2.8.0 ../../pom.xml diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 70bbbdfc4..b5a50573d 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -6,7 +6,7 @@ powertools-parent software.amazon.lambda - 2.7.0 + 2.8.0 ../../pom.xml diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 1ea59493c..4da0c0a9f 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 2.7.0 + 2.8.0 Powertools for AWS Lambda (Java) - Metrics diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 158fdc978..a2c4a65dc 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ powertools-parent software.amazon.lambda - 2.7.0 + 2.8.0 powertools-parameters diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index 9d9adc16b..ab8ae0e67 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 2.7.0 + 2.8.0 ../../pom.xml diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index d66153da6..76a7b8845 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 2.7.0 + 2.8.0 ../../pom.xml diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index f126716d6..fbe4905be 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 2.7.0 + 2.8.0 ../../pom.xml diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index e6bf1ea27..c9de1db51 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 2.7.0 + 2.8.0 ../../pom.xml diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index b40046bde..3bcd4d604 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda powertools-parent - 2.7.0 + 2.8.0 ../../pom.xml diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index d7f33df28..c1eea4df7 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ powertools-parent software.amazon.lambda - 2.7.0 + 2.8.0 powertools-serialization diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 68231dbe1..ed2596059 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 2.7.0 + 2.8.0 Powertools for AWS Lambda (Java) - Tracing diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 74051989e..37063cf6d 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 2.7.0 + 2.8.0 Powertools for AWS Lambda (Java) - Validation From dfca97e5f1ebcc3cd293ea9f386246b88caec04d Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Mon, 24 Nov 2025 13:38:29 +0100 Subject: [PATCH 49/76] chore(ci): Remove branch protection workflow. (#2311) --- .github/branch_protection_settings/1.x.x.json | 53 -------------- .github/branch_protection_settings/main.json | 63 ---------------- .github/branch_protection_settings/v2.json | 63 ---------------- .../workflows/security-branch-protections.yml | 72 ------------------- 4 files changed, 251 deletions(-) delete mode 100644 .github/branch_protection_settings/1.x.x.json delete mode 100644 .github/branch_protection_settings/main.json delete mode 100644 .github/branch_protection_settings/v2.json delete mode 100644 .github/workflows/security-branch-protections.yml diff --git a/.github/branch_protection_settings/1.x.x.json b/.github/branch_protection_settings/1.x.x.json deleted file mode 100644 index e52aba745..000000000 --- a/.github/branch_protection_settings/1.x.x.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/1.x.x/protection", - "required_status_checks": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/1.x.x/protection/required_status_checks", - "strict": true, - "contexts": [ - "SonarCloud" - ], - "contexts_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/1.x.x/protection/required_status_checks/contexts", - "checks": [ - { - "context": "SonarCloud", - "app_id": null - } - ] - }, - "required_pull_request_reviews": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/1.x.x/protection/required_pull_request_reviews", - "dismiss_stale_reviews": false, - "require_code_owner_reviews": false, - "require_last_push_approval": false, - "required_approving_review_count": 0 - }, - "required_signatures": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/1.x.x/protection/required_signatures", - "enabled": false - }, - "enforce_admins": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/1.x.x/protection/enforce_admins", - "enabled": true - }, - "required_linear_history": { - "enabled": false - }, - "allow_force_pushes": { - "enabled": false - }, - "allow_deletions": { - "enabled": false - }, - "block_creations": { - "enabled": false - }, - "required_conversation_resolution": { - "enabled": false - }, - "lock_branch": { - "enabled": false - }, - "allow_fork_syncing": { - "enabled": false - } -} diff --git a/.github/branch_protection_settings/main.json b/.github/branch_protection_settings/main.json deleted file mode 100644 index 8ca32bb45..000000000 --- a/.github/branch_protection_settings/main.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection", - "required_status_checks": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/required_status_checks", - "strict": true, - "contexts": [], - "contexts_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/required_status_checks/contexts", - "checks": [] - }, - "restrictions": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/restrictions", - "users_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/restrictions/users", - "teams_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/restrictions/teams", - "apps_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/restrictions/apps", - "users": [], - "teams": [], - "apps": [] - }, - "required_pull_request_reviews": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/required_pull_request_reviews", - "dismiss_stale_reviews": true, - "require_code_owner_reviews": true, - "require_last_push_approval": true, - "required_approving_review_count": 1, - "dismissal_restrictions": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/dismissal_restrictions", - "users_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/dismissal_restrictions/users", - "teams_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/dismissal_restrictions/teams", - "users": [], - "teams": [], - "apps": [] - } - }, - "required_signatures": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/required_signatures", - "enabled": false - }, - "enforce_admins": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/main/protection/enforce_admins", - "enabled": true - }, - "required_linear_history": { - "enabled": true - }, - "allow_force_pushes": { - "enabled": false - }, - "allow_deletions": { - "enabled": false - }, - "block_creations": { - "enabled": true - }, - "required_conversation_resolution": { - "enabled": true - }, - "lock_branch": { - "enabled": false - }, - "allow_fork_syncing": { - "enabled": false - } -} diff --git a/.github/branch_protection_settings/v2.json b/.github/branch_protection_settings/v2.json deleted file mode 100644 index fb9fdebcd..000000000 --- a/.github/branch_protection_settings/v2.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection", - "required_status_checks": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/required_status_checks", - "strict": true, - "contexts": [], - "contexts_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/required_status_checks/contexts", - "checks": [] - }, - "restrictions": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/restrictions", - "users_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/restrictions/users", - "teams_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/restrictions/teams", - "apps_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/restrictions/apps", - "users": [], - "teams": [], - "apps": [] - }, - "required_pull_request_reviews": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/required_pull_request_reviews", - "dismiss_stale_reviews": true, - "require_code_owner_reviews": false, - "require_last_push_approval": true, - "required_approving_review_count": 1, - "dismissal_restrictions": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/dismissal_restrictions", - "users_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/dismissal_restrictions/users", - "teams_url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/dismissal_restrictions/teams", - "users": [], - "teams": [], - "apps": [] - } - }, - "required_signatures": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/required_signatures", - "enabled": false - }, - "enforce_admins": { - "url": "https://api.github.com/repos/aws-powertools/powertools-lambda-java/branches/v2/protection/enforce_admins", - "enabled": false - }, - "required_linear_history": { - "enabled": true - }, - "allow_force_pushes": { - "enabled": false - }, - "allow_deletions": { - "enabled": false - }, - "block_creations": { - "enabled": true - }, - "required_conversation_resolution": { - "enabled": true - }, - "lock_branch": { - "enabled": false - }, - "allow_fork_syncing": { - "enabled": false - } -} diff --git a/.github/workflows/security-branch-protections.yml b/.github/workflows/security-branch-protections.yml deleted file mode 100644 index af6477802..000000000 --- a/.github/workflows/security-branch-protections.yml +++ /dev/null @@ -1,72 +0,0 @@ -# Branch Protections -# -# Description: -# This workflow compares current security branch protections against those stored, -# if there's any changes, it'll fail the job and alert using a Slack webhook -# -# Triggers: -# - pull_request -# - branch_protection_rule -# - cron: daily at 16:40 -# -# Secrets: -# - SECURITY.BRANCH_PROTECTION_TOKEN -# - SECURITY.SLACK_WEBHOOK_URL -# -# Notes: -# Modified copy of: https://github.com/github/docs/blob/main/.github/workflows/alert-changed-branch-protections.yml - -on: - branch_protection_rule: - schedule: - - cron: '20 16 * * *' # Run daily at 16:20 UTC - pull_request: - paths: - - .github/workflows/security-branch-protections.yml - - .github/branch_protection_settings/*.json - -name: Alert Changed Branch Protections -run-name: Alert Changed Branch Protections - -permissions: - contents: read - -jobs: - check-branch-protections: - runs-on: ubuntu-latest - permissions: - contents: write - environment: Security - if: ${{ github.repository == 'aws-powertools/powertools-lambda-java' }} - strategy: - matrix: - # List of branches we want to monitor for protection changes - branch: - - main - - v1 - steps: - - name: Checkout repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Fetch branch protections - id: fetch - env: - GH_TOKEN: ${{ secrets.BRANCH_PROTECTION_TOKEN }} - run: | - # Fetch branch protections and store them in a file - gh api /repos/${{ github.repository }}/branches/${{ matrix.branch }}/protection | jq \ - > .github/branch_protection_settings/${{ matrix.branch }}.json - - name: Compare branch protections - id: compare - run: | - git diff --quiet .github/branch_protection_settings/${{ matrix.branch }}.json \ - || echo "diff_failed=true" >> $GITHUB_ENV - - name: Send webhook - if: ${{ env.diff_failed == 'true' }} - run: | - curl -X POST -d '{"message": "Branch protections have changed for ${{ github.repository }} on ${{ matrix.branch }}. Please review the changes or revert the changes in GitHub. https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"}' \ - ${{ secrets.SLACK_WEBHOOK_URL }} - - name: Fail workflow - if: ${{ env.diff_failed == 'true' }} - run: | - git diff .github/branch_protection_settings/${{ matrix.branch }}.json - echo "::error::Branch protections have been changed" \ No newline at end of file From 8208272fb8839eba312e27c1ee5a73a466ae7800 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 13:44:16 +0100 Subject: [PATCH 50/76] chore: bump org.wiremock:wiremock from 3.13.1 to 3.13.2 (#2306) Bumps [org.wiremock:wiremock](https://github.com/wiremock/wiremock) from 3.13.1 to 3.13.2. - [Release notes](https://github.com/wiremock/wiremock/releases) - [Commits](https://github.com/wiremock/wiremock/compare/3.13.1...3.13.2) --- updated-dependencies: - dependency-name: org.wiremock:wiremock dependency-version: 3.13.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ed1d2aff9..64b6e0e0d 100644 --- a/pom.xml +++ b/pom.xml @@ -389,7 +389,7 @@ org.wiremock wiremock - 3.13.1 + 3.13.2 test From df4a757bdaaad2dd90093e95416a3ac8a435e30b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 13:44:53 +0100 Subject: [PATCH 51/76] chore: bump aws.sdk.version from 2.38.6 to 2.39.1 (#2305) Bumps `aws.sdk.version` from 2.38.6 to 2.39.1. Updates `software.amazon.awssdk:bom` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:http-client-spi` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:url-connection-client` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:s3` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:dynamodb` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:lambda` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:kinesis` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:cloudwatch` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:xray` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:sqs` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:cloudformation` from 2.38.6 to 2.39.1 Updates `software.amazon.awssdk:sts` from 2.38.6 to 2.39.1 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.39.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.39.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.39.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.39.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.39.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.39.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.39.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.39.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-version: 2.39.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.39.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.39.1 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-version: 2.39.1 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 8dde0a034..09adbfa18 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ 11 1.4.0 3.16.1 - 2.38.6 + 2.39.1 1.9.20.1 diff --git a/pom.xml b/pom.xml index 64b6e0e0d..dc2decfd6 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ 2.25.2 2.0.17 2.20.1 - 2.38.6 + 2.39.1 2.20.0 2.2.0 UTF-8 diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index f06db02f0..a9d3c3ab5 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ 3.6.1 1.14.1 3.14.1 - 2.38.6 + 2.39.1 1.9.20.1 true From 51eeaf47431f6a7d93bc3bd67942849595ebbf37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 13:45:08 +0100 Subject: [PATCH 52/76] chore: bump org.apache.commons:commons-lang3 from 3.19.0 to 3.20.0 (#2304) Bumps org.apache.commons:commons-lang3 from 3.19.0 to 3.20.0. --- updated-dependencies: - dependency-name: org.apache.commons:commons-lang3 dependency-version: 3.20.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dc2decfd6..1e4c3a9fd 100644 --- a/pom.xml +++ b/pom.xml @@ -310,7 +310,7 @@ org.apache.commons commons-lang3 - 3.19.0 + 3.20.0 From ca96cd82ac46b8e54fdd4271570d6c2e5941fd85 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 15:58:09 +0100 Subject: [PATCH 53/76] chore: bump actions/checkout from 5.0.0 to 6.0.0 (#2308) Bumps [actions/checkout](https://github.com/actions/checkout) from 5.0.0 to 6.0.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/08c6903cd8c0fde910a37f88322edcfb5dd907a8...1af3b93b6815bc44a9784bd300feb67ff0d1eeb3) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-docs.yml | 2 +- .github/workflows/check-build.yml | 4 ++-- .github/workflows/check-e2e.yml | 4 ++-- .github/workflows/check-pmd.yml | 2 +- .github/workflows/check-spotbugs.yml | 2 +- .github/workflows/release.yml | 6 +++--- .github/workflows/security-dependencies-check.yml | 2 +- .github/workflows/security-scorecard.yml | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 8cef6040e..be53fbfaa 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -32,7 +32,7 @@ jobs: environment: Docs steps: - name: Checkout Repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 with: fetch-depth: 0 - name: Build diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index 88f1eabee..a972084a6 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -77,7 +77,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Setup Java uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 with: @@ -94,7 +94,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: fetch-depth: 0 - name: Get changed files diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml index 54e7cd023..6ed3b248e 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -57,7 +57,7 @@ jobs: - 25 steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Setup java uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: @@ -88,7 +88,7 @@ jobs: - 25 steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Setup java uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: diff --git a/.github/workflows/check-pmd.yml b/.github/workflows/check-pmd.yml index 41983f89b..66150821d 100644 --- a/.github/workflows/check-pmd.yml +++ b/.github/workflows/check-pmd.yml @@ -29,7 +29,7 @@ jobs: id-token: write steps: - name: Checkout Repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Setup Java uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: diff --git a/.github/workflows/check-spotbugs.yml b/.github/workflows/check-spotbugs.yml index bf746320d..8af0e4d9d 100644 --- a/.github/workflows/check-spotbugs.yml +++ b/.github/workflows/check-spotbugs.yml @@ -41,7 +41,7 @@ jobs: codecheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Setup Java uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 79cb5b04f..bd2e23487 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -103,7 +103,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - id: version name: version uses: ./.github/actions/version @@ -224,7 +224,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: ref: ${{ env.RELEASE_COMMIT }} - id: download_source @@ -272,7 +272,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: # Checkout PR branch to make sure we build the version-bumped docs ref: ci-${{ github.run_id }} diff --git a/.github/workflows/security-dependencies-check.yml b/.github/workflows/security-dependencies-check.yml index c6e06950f..1e85d9e96 100644 --- a/.github/workflows/security-dependencies-check.yml +++ b/.github/workflows/security-dependencies-check.yml @@ -24,7 +24,7 @@ jobs: pull-requests: write steps: - name: Checkout Repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Verify Contents uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2 with: diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 205cb0ddc..d59367015 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -35,7 +35,7 @@ jobs: id-token: write steps: - name: Checkout Repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: persist-credentials: false - name: Run Analysis From 54991e6bf589ac29b26960b53782ab5a8fe13371 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 15:58:32 +0100 Subject: [PATCH 54/76] chore: bump sam/build-java25 (#2309) Bumps sam/build-java25 from `2ef9e5b` to `af599d0`. --- updated-dependencies: - dependency-name: sam/build-java25 dependency-version: af599d010afef63c83a04265dee9adbd4557d94b4a81941f28ae42657af7890d dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/src/test/resources/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/src/test/resources/docker/Dockerfile b/powertools-e2e-tests/src/test/resources/docker/Dockerfile index 5d374eaa8..6643760b4 100644 --- a/powertools-e2e-tests/src/test/resources/docker/Dockerfile +++ b/powertools-e2e-tests/src/test/resources/docker/Dockerfile @@ -1,5 +1,5 @@ # Use the official AWS SAM base image for Java 25 -FROM public.ecr.aws/sam/build-java25@sha256:2ef9e5b950cc79489691be16c7edff904bf196955633dc7fbbc282a1ea421ba8 +FROM public.ecr.aws/sam/build-java25@sha256:af599d010afef63c83a04265dee9adbd4557d94b4a81941f28ae42657af7890d # Install GraalVM dependencies RUN curl -4 -L https://download.oracle.com/graalvm/25/latest/graalvm-jdk-25_linux-x64_bin.tar.gz | tar -xvz From 93d302f09fbb37770bccedb96ab1e1acc5ea260e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 15:58:58 +0100 Subject: [PATCH 55/76] chore: bump github/codeql-action from 4.31.4 to 4.31.5 (#2312) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.4 to 4.31.5. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/e12f0178983d466f2f6028f5cc7a6d786fd97f4b...fdbfb4d2750291e159f0156def62b853c2798ca2) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.31.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index d59367015..0944694f7 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v3.29.5 + uses: github/codeql-action/upload-sarif@fdbfb4d2750291e159f0156def62b853c2798ca2 # v3.29.5 with: sarif_file: results.sarif From 7ea89ded9d25796e9acc0ae1a4b68b5ae4193481 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Nov 2025 11:54:38 +0100 Subject: [PATCH 56/76] chore: bump aws-actions/configure-aws-credentials from 5.1.0 to 5.1.1 (#2316) Bumps [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials) from 5.1.0 to 5.1.1. - [Release notes](https://github.com/aws-actions/configure-aws-credentials/releases) - [Changelog](https://github.com/aws-actions/configure-aws-credentials/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws-actions/configure-aws-credentials/compare/00943011d9042930efac3dcd3a170e4273319bc8...61815dcd50bd041e203e49132bacad1fd04d2708) --- updated-dependencies: - dependency-name: aws-actions/configure-aws-credentials dependency-version: 5.1.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-docs.yml | 2 +- .github/workflows/check-e2e.yml | 4 ++-- .github/workflows/release.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index be53fbfaa..417f21c48 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -41,7 +41,7 @@ jobs: docker build -t squidfunk/mkdocs-material ./docs/ docker run --rm -t -v ${PWD}:/docs squidfunk/mkdocs-material build - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@00943011d9042930efac3dcd3a170e4273319bc8 + uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 with: aws-region: us-east-1 role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml index 6ed3b248e..2ddb78ec0 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -65,7 +65,7 @@ jobs: java-version: ${{ matrix.java }} cache: maven - name: Setup AWS credentials - uses: aws-actions/configure-aws-credentials@00943011d9042930efac3dcd3a170e4273319bc8 # v5.1.0 + uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 # v5.1.1 with: role-to-assume: ${{ secrets.AWS_IAM_ROLE }} aws-region: us-east-1 @@ -96,7 +96,7 @@ jobs: java-version: ${{ matrix.java }} cache: maven - name: Setup AWS credentials - uses: aws-actions/configure-aws-credentials@00943011d9042930efac3dcd3a170e4273319bc8 # v5.1.0 + uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 # v5.1.1 with: role-to-assume: ${{ secrets.AWS_IAM_ROLE }} aws-region: us-east-1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bd2e23487..20ff19a69 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -282,7 +282,7 @@ jobs: docker build -t squidfunk/mkdocs-material ./docs/ docker run --rm -t -v ${PWD}:/docs squidfunk/mkdocs-material build - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@00943011d9042930efac3dcd3a170e4273319bc8 + uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 with: aws-region: us-east-1 role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} From bb70c1be87072674a46456cfb73cd86818f45d46 Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Wed, 10 Dec 2025 16:21:53 +0100 Subject: [PATCH 57/76] fix(metrics): Clear custom dimensions when flushing. (#2328) --- .../metrics/internal/EmfMetricsLogger.java | 24 ++- .../internal/EmfMetricsLoggerTest.java | 189 ++++++++++++++++++ 2 files changed, 212 insertions(+), 1 deletion(-) diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLogger.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLogger.java index 37f2d193a..611d4dcc6 100644 --- a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLogger.java +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLogger.java @@ -164,6 +164,27 @@ public void flush() { } } else { emfLogger.flush(); + + // Clear custom dimensions after flush while preserving default dimensions + clearCustomDimensions(); + } + } + + private void clearCustomDimensions() { + // Reset all dimensions in the EMF logger + emfLogger.resetDimensions(false); + + // Re-apply default dimensions if they exist + if (!defaultDimensions.isEmpty()) { + DimensionSet emfDimensionSet = new DimensionSet(); + defaultDimensions.forEach((key, value) -> { + try { + emfDimensionSet.addDimension(key, value); + } catch (Exception e) { + // Ignore dimension errors + } + }); + emfLogger.setDimensions(emfDimensionSet); } } @@ -198,7 +219,8 @@ public void flushMetrics(Consumer metricsConsumer) { metrics.setNamespace(this.namespace); } if (!defaultDimensions.isEmpty()) { - metrics.setDefaultDimensions(software.amazon.lambda.powertools.metrics.model.DimensionSet.of(defaultDimensions)); + metrics.setDefaultDimensions( + software.amazon.lambda.powertools.metrics.model.DimensionSet.of(defaultDimensions)); } properties.forEach(metrics::addMetadata); diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLoggerTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLoggerTest.java index c2238711a..5e597e835 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLoggerTest.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/EmfMetricsLoggerTest.java @@ -263,6 +263,65 @@ void shouldAddMetadata() throws Exception { assertThat(rootNode.get("CustomMetadata").asText()).isEqualTo("MetadataValue"); } + @Test + void shouldClearMetadataAfterFlush() throws Exception { + // Given - Add metadata and flush first time + metrics.addMetadata("RequestId", "req-123"); + metrics.addMetadata("UserAgent", "test-agent"); + metrics.addMetric("FirstMetric", 1.0); + metrics.flush(); + + // Capture first flush output and reset for second flush + String firstFlushOutput = outputStreamCaptor.toString().trim(); + outputStreamCaptor.reset(); + + // When - Add another metric and flush again using the SAME metrics instance + metrics.addMetric("SecondMetric", 2.0); + metrics.flush(); + + // Then - Verify first flush had metadata + JsonNode firstRootNode = objectMapper.readTree(firstFlushOutput); + assertThat(firstRootNode.has("RequestId")).isTrue(); + assertThat(firstRootNode.get("RequestId").asText()).isEqualTo("req-123"); + assertThat(firstRootNode.has("UserAgent")).isTrue(); + assertThat(firstRootNode.get("UserAgent").asText()).isEqualTo("test-agent"); + assertThat(firstRootNode.has("FirstMetric")).isTrue(); + + // Verify second flush does NOT have metadata from first flush + // The EMF library automatically clears metadata after flush + String secondFlushOutput = outputStreamCaptor.toString().trim(); + JsonNode secondRootNode = objectMapper.readTree(secondFlushOutput); + + // Metadata should be cleared after first flush by the EMF library + assertThat(secondRootNode.has("RequestId")).isFalse(); + assertThat(secondRootNode.has("UserAgent")).isFalse(); + assertThat(secondRootNode.has("SecondMetric")).isTrue(); + } + + @Test + void shouldInheritMetadataInFlushMetricsMethod() throws Exception { + // Given - Add metadata to the main metrics instance + metrics.addMetadata("PersistentMetadata", "should-inherit"); + metrics.addMetadata("GlobalContext", "main-instance"); + + // When - Use flushMetrics to create a separate metrics context + metrics.flushMetrics(separateMetrics -> { + separateMetrics.addMetric("SeparateMetric", 1.0); + // Don't add any metadata to the separate instance + }); + + // Then - The separate metrics context SHOULD inherit metadata from main instance + String flushMetricsOutput = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(flushMetricsOutput); + + // The separate metrics should have inherited metadata (this is expected behavior) + assertThat(rootNode.has("PersistentMetadata")).isTrue(); + assertThat(rootNode.get("PersistentMetadata").asText()).isEqualTo("should-inherit"); + assertThat(rootNode.has("GlobalContext")).isTrue(); + assertThat(rootNode.get("GlobalContext").asText()).isEqualTo("main-instance"); + assertThat(rootNode.has("SeparateMetric")).isTrue(); + } + @Test void shouldSetDefaultDimensions() throws Exception { // Given @@ -547,4 +606,134 @@ void shouldNotFlushSingleMetricWhenDisabled() { String emfOutput = outputStreamCaptor.toString().trim(); assertThat(emfOutput).isEmpty(); } + + @Test + void shouldClearCustomDimensionsAfterFlush() throws Exception { + // Given - Set up default dimensions that should persist + DimensionSet defaultDimensions = DimensionSet.of("Service", "TestService", "Environment", "Test"); + metrics.setDefaultDimensions(defaultDimensions); + + // First invocation - add custom dimensions and flush + DimensionSet customDimensions = DimensionSet.of("EXAMPLE_KEY", "EXAMPLE_VALUE"); + metrics.addDimension(customDimensions); + metrics.addMetric("SERL", 1.0); + metrics.flush(); + + // Capture first flush output + String firstFlushOutput = outputStreamCaptor.toString().trim(); + outputStreamCaptor.reset(); // Clear for second flush + + // Second invocation - should NOT have custom dimensions from first invocation + metrics.addMetric("Expected", 1.0); + metrics.flush(); + + // Then - Verify first flush had both default and custom dimensions + JsonNode firstRootNode = objectMapper.readTree(firstFlushOutput); + assertThat(firstRootNode.has("Service")).isTrue(); + assertThat(firstRootNode.get("Service").asText()).isEqualTo("TestService"); + assertThat(firstRootNode.has("Environment")).isTrue(); + assertThat(firstRootNode.get("Environment").asText()).isEqualTo("Test"); + assertThat(firstRootNode.has("EXAMPLE_KEY")).isTrue(); + assertThat(firstRootNode.get("EXAMPLE_KEY").asText()).isEqualTo("EXAMPLE_VALUE"); + assertThat(firstRootNode.has("SERL")).isTrue(); + + // Verify second flush has ONLY default dimensions (custom dimensions should be cleared) + String secondFlushOutput = outputStreamCaptor.toString().trim(); + JsonNode secondRootNode = objectMapper.readTree(secondFlushOutput); + + // Default dimensions should still be present + assertThat(secondRootNode.has("Service")).isTrue(); + assertThat(secondRootNode.get("Service").asText()).isEqualTo("TestService"); + assertThat(secondRootNode.has("Environment")).isTrue(); + assertThat(secondRootNode.get("Environment").asText()).isEqualTo("Test"); + + // Custom dimensions should be cleared (this is the failing assertion that demonstrates the bug) + assertThat(secondRootNode.has("EXAMPLE_KEY")).isFalse(); + assertThat(secondRootNode.has("Expected")).isTrue(); + + // Verify dimensions in CloudWatchMetrics section + JsonNode secondDimensions = secondRootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Dimensions").get(0); + boolean hasExampleKey = false; + boolean hasService = false; + boolean hasEnvironment = false; + + for (JsonNode dimension : secondDimensions) { + String dimName = dimension.asText(); + if ("EXAMPLE_KEY".equals(dimName)) { + hasExampleKey = true; + } else if ("Service".equals(dimName)) { + hasService = true; + } else if ("Environment".equals(dimName)) { + hasEnvironment = true; + } + } + + // Default dimensions should be in CloudWatchMetrics + assertThat(hasService).isTrue(); + assertThat(hasEnvironment).isTrue(); + // Custom dimension should NOT be in CloudWatchMetrics (this should fail initially) + assertThat(hasExampleKey).isFalse(); + } + + @Test + void shouldHandleEmptyCustomDimensionsGracefully() throws Exception { + // Given - Only default dimensions, no custom dimensions + metrics.setDefaultDimensions(DimensionSet.of("Service", "TestService")); + + // When - Flush without adding custom dimensions + metrics.addMetric("TestMetric", 1.0); + metrics.flush(); + outputStreamCaptor.reset(); + + // Second flush + metrics.addMetric("TestMetric2", 2.0); + metrics.flush(); + + // Then - Should work normally with only default dimensions + String output = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(output); + + assertThat(rootNode.has("Service")).isTrue(); + assertThat(rootNode.get("Service").asText()).isEqualTo("TestService"); + assertThat(rootNode.has("TestMetric2")).isTrue(); + } + + @Test + void shouldClearCustomDimensionsWhenNoDefaultDimensionsSet() throws Exception { + // Given - No default dimensions set + metrics.clearDefaultDimensions(); + + // When - Add custom dimensions and flush + metrics.addDimension("CustomDim", "CustomValue"); + metrics.addMetric("Metric1", 1.0); + metrics.flush(); + outputStreamCaptor.reset(); + + // Second flush without custom dimensions + metrics.addMetric("Metric2", 2.0); + metrics.flush(); + + // Then - Custom dimensions should be cleared + String output = outputStreamCaptor.toString().trim(); + JsonNode rootNode = objectMapper.readTree(output); + + assertThat(rootNode.has("CustomDim")).isFalse(); + assertThat(rootNode.has("Metric2")).isTrue(); + + // Verify no custom dimensions in CloudWatchMetrics section + JsonNode dimensionsArray = rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Dimensions"); + boolean hasCustomDim = false; + if (dimensionsArray != null && dimensionsArray.size() > 0) { + JsonNode dimensions = dimensionsArray.get(0); + if (dimensions != null) { + for (JsonNode dimension : dimensions) { + if ("CustomDim".equals(dimension.asText())) { + hasCustomDim = true; + break; + } + } + } + } + assertThat(hasCustomDim).isFalse(); + } } From 79e31370cf85d1c44b304930b4f063272a5e3a68 Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Mon, 15 Dec 2025 13:12:40 +0100 Subject: [PATCH 58/76] feat(cold-start-detection): Suppress cold start detection for non ON-DEMAND invocations (#2329) * fix: cold start only on on-demand invocation * fix: cold start only on on-demand invocation * fix: cold start only on on-demand invocation * mvn clean test is successful * reversed the sqllite changes * feat(cold-start-detection): Suppress cold start for non ON-DEMAND invocation (since there are no cold starts). --------- Co-authored-by: Attyuttam --- .../terraform/pom.xml | 6 +++--- .../sam-graalvm/pom.xml | 7 +++---- .../powertools-examples-parameters/sam/pom.xml | 4 +--- pom.xml | 2 -- powertools-common/pom.xml | 1 - .../common/internal/LambdaConstants.java | 2 ++ .../common/internal/LambdaHandlerProcessor.java | 9 ++++++++- .../internal/LambdaHandlerProcessorTest.java | 1 + powertools-e2e-tests/pom.xml | 2 -- powertools-logging/pom.xml | 3 +-- .../powertools-logging-log4j/pom.xml | 3 +-- .../powertools-logging-logback/pom.xml | 3 +-- powertools-metrics/pom.xml | 1 + powertools-parameters/pom.xml | 2 -- .../powertools-parameters-appconfig/pom.xml | 2 -- .../powertools-parameters-dynamodb/pom.xml | 2 -- .../powertools-parameters-secrets/pom.xml | 2 -- .../powertools-parameters-ssm/pom.xml | 2 -- .../powertools-parameters-tests/pom.xml | 1 - powertools-serialization/pom.xml | 1 - powertools-tracing/pom.xml | 15 ++++++++++++++- 21 files changed, 36 insertions(+), 35 deletions(-) diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index 14c0e223e..a6237b00f 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -1,5 +1,5 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Terraform @@ -101,7 +101,8 @@ false - + @@ -126,7 +127,6 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.4 handler diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 6d0c3f4a2..09401eeff 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -1,5 +1,5 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples 2.8.0 @@ -98,8 +98,6 @@ org.apache.maven.plugins maven-surefire-plugin - - 3.5.4 dev.aspectj @@ -144,7 +142,8 @@ false - + diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index 15d68ffcd..3e8f99aa0 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -1,5 +1,5 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples 2.8.0 @@ -72,8 +72,6 @@ org.apache.maven.plugins maven-surefire-plugin - - 3.5.4 dev.aspectj diff --git a/pom.xml b/pom.xml index 1e4c3a9fd..6f6a9ce10 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,6 @@ 3.14.1 1.9.7 1.13.1 - 3.2.5 0.8.11 1.6.13 3.12.0 @@ -625,7 +624,6 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.4 @{argLine} diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index f14783b5b..551fae8bc 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -106,7 +106,6 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.4 -Dorg.graalvm.nativeimage.imagecode=agent diff --git a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java index 69fc1283a..4c4e8e9db 100644 --- a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java +++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaConstants.java @@ -28,4 +28,6 @@ private LambdaConstants() { public static final String ROOT_EQUALS = "Root="; public static final String POWERTOOLS_SERVICE_NAME = "POWERTOOLS_SERVICE_NAME"; public static final String SERVICE_UNDEFINED = "service_undefined"; + public static final String AWS_LAMBDA_INITIALIZATION_TYPE = "AWS_LAMBDA_INITIALIZATION_TYPE"; + public static final String ON_DEMAND_INVOCATION_TYPE = "on-demand"; } diff --git a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java index 393835d1e..15bff15d6 100644 --- a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java +++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessor.java @@ -93,7 +93,14 @@ protected static void resetServiceName() { } public static boolean isColdStart() { - return isColdStart == null; + if (isColdStart != null) { + return isColdStart; + } + + String initType = System.getenv(LambdaConstants.AWS_LAMBDA_INITIALIZATION_TYPE); + isColdStart = LambdaConstants.ON_DEMAND_INVOCATION_TYPE.equals(initType); + + return isColdStart; } public static void coldStartDone() { diff --git a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java index 5c6bdc020..0726a9e77 100644 --- a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java +++ b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/LambdaHandlerProcessorTest.java @@ -216,6 +216,7 @@ void extractContext_notKnownHandler() { } @Test + @SetEnvironmentVariable(key = LambdaConstants.AWS_LAMBDA_INITIALIZATION_TYPE, value = LambdaConstants.ON_DEMAND_INVOCATION_TYPE) void isColdStart() { boolean isColdStart = LambdaHandlerProcessor.isColdStart(); diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 484e3b4c2..bfd4350a0 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -214,7 +214,6 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.5.4 @@ -241,7 +240,6 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.5.4 diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index ae5788291..923d6d133 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -123,7 +123,6 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.4 -Dorg.graalvm.nativeimage.imagecode=agent @@ -182,11 +181,11 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.4 JSON testService + on-demand diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index 1cf3bf265..19a8b9428 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -112,7 +112,6 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.4 -Dorg.graalvm.nativeimage.imagecode=agent @@ -204,12 +203,12 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.4 testLog4j eu-central-1 <_X_AMZN_TRACE_ID>Root=1-63441c4a-abcdef012345678912345678 + on-demand diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index b5a50573d..7ed1f1a17 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -110,7 +110,6 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.4 -Dorg.graalvm.nativeimage.imagecode=agent @@ -203,12 +202,12 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.4 testLogback eu-central-1 <_X_AMZN_TRACE_ID>Root=1-63441c4a-abcdef012345678912345678 + on-demand diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 4da0c0a9f..86bdcc560 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -231,6 +231,7 @@ Lambda + on-demand diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index a2c4a65dc..78234b5cc 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -80,7 +80,6 @@ maven-surefire-plugin - 3.5.4 eu-central-1 @@ -98,7 +97,6 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.4 -Dorg.graalvm.nativeimage.imagecode=agent diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index ab8ae0e67..3c38963a1 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -108,7 +108,6 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.4 -Dorg.graalvm.nativeimage.imagecode=agent @@ -175,7 +174,6 @@ maven-surefire-plugin - 3.5.4 eu-central-1 diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index 76a7b8845..b9e48567a 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -109,7 +109,6 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.4 -Dorg.graalvm.nativeimage.imagecode=agent @@ -176,7 +175,6 @@ maven-surefire-plugin - 3.5.4 eu-central-1 diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index fbe4905be..c83c674d5 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -109,7 +109,6 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.4 -Dorg.graalvm.nativeimage.imagecode=agent @@ -176,7 +175,6 @@ maven-surefire-plugin - 3.5.4 eu-central-1 diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index c9de1db51..d23b84bf6 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -98,7 +98,6 @@ maven-surefire-plugin - 3.5.4 eu-central-1 @@ -123,7 +122,6 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.4 -Dorg.graalvm.nativeimage.imagecode=agent diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index 3bcd4d604..9b93b3f79 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -110,7 +110,6 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.4 -Dorg.graalvm.nativeimage.imagecode=agent diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index c1eea4df7..535f23190 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -107,7 +107,6 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.4 -Dorg.graalvm.nativeimage.imagecode=agent diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index ed2596059..9e5603d70 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -129,6 +129,20 @@ + + + + org.apache.maven.plugins + maven-surefire-plugin + + + on-demand + + + + + + generate-graalvm-files @@ -137,7 +151,6 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.4 -Dorg.graalvm.nativeimage.imagecode=agent From e6f6a92892291670387274d1d83253e577008722 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:09:57 +0100 Subject: [PATCH 59/76] chore: bump aws.sdk.version from 2.38.7 to 2.39.3 (#2313) Bumps `aws.sdk.version` from 2.38.7 to 2.39.3. Updates `software.amazon.awssdk:url-connection-client` from 2.38.7 to 2.39.3 Updates `software.amazon.awssdk:sdk-core` from 2.38.7 to 2.39.3 Updates `software.amazon.awssdk:s3` from 2.38.7 to 2.39.3 Updates `software.amazon.awssdk:kinesis` from 2.38.7 to 2.39.3 Updates `software.amazon.awssdk:sqs` from 2.38.7 to 2.39.3 Updates `software.amazon.awssdk:dynamodb-enhanced` from 2.38.7 to 2.39.3 --- updated-dependencies: - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.39.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sdk-core dependency-version: 2.39.3 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.39.3 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.39.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.39.3 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb-enhanced dependency-version: 2.39.3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-batch/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 6d941a31f..3a8e3ad8d 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -14,7 +14,7 @@ 11 11 1.9.20.1 - 2.38.7 + 2.39.3 From 314e56997a34273107ba4e13d5f1143a77d59600 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:10:23 +0100 Subject: [PATCH 60/76] chore: bump com.google.protobuf:protobuf-java from 4.33.0 to 4.33.1 (#2314) Bumps [com.google.protobuf:protobuf-java](https://github.com/protocolbuffers/protobuf) from 4.33.0 to 4.33.1. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/main/protobuf_release.bzl) - [Commits](https://github.com/protocolbuffers/protobuf/commits) --- updated-dependencies: - dependency-name: com.google.protobuf:protobuf-java dependency-version: 4.33.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-kafka/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index f4b75722c..1b8e8f09d 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -12,7 +12,7 @@ 11 1.9.20.1 1.12.1 - 4.33.0 + 4.33.1 diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index ad564897f..82f6b8021 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -36,7 +36,7 @@ 4.1.1 1.12.1 - 4.33.0 + 4.33.1 1.1.6 From ff8122d90e4ef686613207fd91daf036e317babc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:10:33 +0100 Subject: [PATCH 61/76] chore: bump org.codehaus.mojo:versions-maven-plugin (#2315) Bumps [org.codehaus.mojo:versions-maven-plugin](https://github.com/mojohaus/versions) from 2.19.1 to 2.20.1. - [Release notes](https://github.com/mojohaus/versions/releases) - [Changelog](https://github.com/mojohaus/versions/blob/master/ReleaseNotes.md) - [Commits](https://github.com/mojohaus/versions/compare/2.19.1...2.20.1) --- updated-dependencies: - dependency-name: org.codehaus.mojo:versions-maven-plugin dependency-version: 2.20.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Andrea Amorosi --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6f6a9ce10..f400e0b26 100644 --- a/pom.xml +++ b/pom.xml @@ -111,7 +111,7 @@ 4.1.2 0.6.0 1.12.781 - 2.19.1 + 2.20.1 1.7.0 5.20.0 5.20.0 From 057f94e2e2028291b88331c135c16093cc58fc86 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:10:44 +0100 Subject: [PATCH 62/76] chore: bump graalvm/setup-graalvm from 1.4.2 to 1.4.4 (#2322) Bumps [graalvm/setup-graalvm](https://github.com/graalvm/setup-graalvm) from 1.4.2 to 1.4.4. - [Release notes](https://github.com/graalvm/setup-graalvm/releases) - [Commits](https://github.com/graalvm/setup-graalvm/compare/eec48106e0bf45f2976c2ff0c3e22395cced8243...790e28947b79a9c09c3391c0f18bf8d0f102ed69) --- updated-dependencies: - dependency-name: graalvm/setup-graalvm dependency-version: 1.4.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index a972084a6..f23968815 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -105,7 +105,7 @@ jobs: powertools-*/** pom.xml - name: Setup GraalVM - uses: graalvm/setup-graalvm@eec48106e0bf45f2976c2ff0c3e22395cced8243 # v1.4.2 + uses: graalvm/setup-graalvm@790e28947b79a9c09c3391c0f18bf8d0f102ed69 # v1.4.4 with: java-version: "21" distribution: "graalvm" From 9f4668a7d75285a529893abd28415b578a9aab5a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:10:56 +0100 Subject: [PATCH 63/76] chore: bump actions/checkout from 6.0.0 to 6.0.1 (#2323) Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.0 to 6.0.1. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/1af3b93b6815bc44a9784bd300feb67ff0d1eeb3...8e8c483db84b4bee98b60c0593521ed34d9990e8) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-docs.yml | 2 +- .github/workflows/check-build.yml | 4 ++-- .github/workflows/check-e2e.yml | 4 ++-- .github/workflows/check-pmd.yml | 2 +- .github/workflows/check-spotbugs.yml | 2 +- .github/workflows/release.yml | 6 +++--- .github/workflows/security-dependencies-check.yml | 2 +- .github/workflows/security-scorecard.yml | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 417f21c48..a94ace711 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -32,7 +32,7 @@ jobs: environment: Docs steps: - name: Checkout Repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 with: fetch-depth: 0 - name: Build diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index f23968815..108a32a11 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -77,7 +77,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup Java uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 with: @@ -94,7 +94,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: fetch-depth: 0 - name: Get changed files diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml index 2ddb78ec0..8a527835d 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -57,7 +57,7 @@ jobs: - 25 steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup java uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: @@ -88,7 +88,7 @@ jobs: - 25 steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup java uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: diff --git a/.github/workflows/check-pmd.yml b/.github/workflows/check-pmd.yml index 66150821d..cab6c16a8 100644 --- a/.github/workflows/check-pmd.yml +++ b/.github/workflows/check-pmd.yml @@ -29,7 +29,7 @@ jobs: id-token: write steps: - name: Checkout Repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup Java uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: diff --git a/.github/workflows/check-spotbugs.yml b/.github/workflows/check-spotbugs.yml index 8af0e4d9d..db4014aea 100644 --- a/.github/workflows/check-spotbugs.yml +++ b/.github/workflows/check-spotbugs.yml @@ -41,7 +41,7 @@ jobs: codecheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup Java uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 20ff19a69..a59cab7cb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -103,7 +103,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - id: version name: version uses: ./.github/actions/version @@ -224,7 +224,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ env.RELEASE_COMMIT }} - id: download_source @@ -272,7 +272,7 @@ jobs: steps: - id: checkout name: Checkout repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: # Checkout PR branch to make sure we build the version-bumped docs ref: ci-${{ github.run_id }} diff --git a/.github/workflows/security-dependencies-check.yml b/.github/workflows/security-dependencies-check.yml index 1e85d9e96..6729fd304 100644 --- a/.github/workflows/security-dependencies-check.yml +++ b/.github/workflows/security-dependencies-check.yml @@ -24,7 +24,7 @@ jobs: pull-requests: write steps: - name: Checkout Repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Verify Contents uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2 with: diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 0944694f7..e15b0b5dc 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -35,7 +35,7 @@ jobs: id-token: write steps: - name: Checkout Repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Run Analysis From 8fd60742bd06c695c9ab55f63701a38630a3d9fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:11:08 +0100 Subject: [PATCH 64/76] chore: bump sam/build-java25 (#2324) Bumps sam/build-java25 from `af599d0` to `b34fc78`. --- updated-dependencies: - dependency-name: sam/build-java25 dependency-version: b34fc789a70464f080232ee96590a6d0c7249eb59b1c33deccaae08a882cac23 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/src/test/resources/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/src/test/resources/docker/Dockerfile b/powertools-e2e-tests/src/test/resources/docker/Dockerfile index 6643760b4..fbefe46dd 100644 --- a/powertools-e2e-tests/src/test/resources/docker/Dockerfile +++ b/powertools-e2e-tests/src/test/resources/docker/Dockerfile @@ -1,5 +1,5 @@ # Use the official AWS SAM base image for Java 25 -FROM public.ecr.aws/sam/build-java25@sha256:af599d010afef63c83a04265dee9adbd4557d94b4a81941f28ae42657af7890d +FROM public.ecr.aws/sam/build-java25@sha256:b34fc789a70464f080232ee96590a6d0c7249eb59b1c33deccaae08a882cac23 # Install GraalVM dependencies RUN curl -4 -L https://download.oracle.com/graalvm/25/latest/graalvm-jdk-25_linux-x64_bin.tar.gz | tar -xvz From 8d5a364e795cfa28c9911fd89d8d8283c029a308 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:11:21 +0100 Subject: [PATCH 65/76] chore: bump actions/setup-java from 5.0.0 to 5.1.0 (#2325) Bumps [actions/setup-java](https://github.com/actions/setup-java) from 5.0.0 to 5.1.0. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/dded0888837ed1f317902acf8a20df0ad188d165...f2beeb24e141e01a676f977032f5a29d81c9e27e) --- updated-dependencies: - dependency-name: actions/setup-java dependency-version: 5.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-build.yml | 2 +- .github/workflows/check-e2e.yml | 4 ++-- .github/workflows/check-pmd.yml | 2 +- .github/workflows/check-spotbugs.yml | 2 +- .github/workflows/release.yml | 6 +++--- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index 108a32a11..cdfd5aea5 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -79,7 +79,7 @@ jobs: name: Checkout repository uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup Java - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e with: distribution: corretto java-version: ${{ matrix.java }} diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml index 8a527835d..378d48a60 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -59,7 +59,7 @@ jobs: steps: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup java - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 with: distribution: 'corretto' java-version: ${{ matrix.java }} @@ -90,7 +90,7 @@ jobs: steps: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup java - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 with: distribution: 'corretto' java-version: ${{ matrix.java }} diff --git a/.github/workflows/check-pmd.yml b/.github/workflows/check-pmd.yml index cab6c16a8..7e7dce429 100644 --- a/.github/workflows/check-pmd.yml +++ b/.github/workflows/check-pmd.yml @@ -31,7 +31,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup Java - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 with: java-version: 21 distribution: corretto diff --git a/.github/workflows/check-spotbugs.yml b/.github/workflows/check-spotbugs.yml index db4014aea..c5c8197f9 100644 --- a/.github/workflows/check-spotbugs.yml +++ b/.github/workflows/check-spotbugs.yml @@ -43,7 +43,7 @@ jobs: steps: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Setup Java - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 with: distribution: 'corretto' java-version: 21 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a59cab7cb..c9b2d80c6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -137,7 +137,7 @@ jobs: with: name: source - name: Setup Java - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e with: distribution: corretto java-version: 21 @@ -172,7 +172,7 @@ jobs: with: name: source - name: Setup Java - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e with: distribution: corretto java-version: ${{ matrix.java }} @@ -195,7 +195,7 @@ jobs: with: name: source - name: Setup Java - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e with: distribution: corretto java-version: 21 From a94274eba5ca1bf04432910db7776e551f236799 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:11:32 +0100 Subject: [PATCH 66/76] chore: bump tj-actions/changed-files from 47.0.0 to 47.0.1 (#2330) Bumps [tj-actions/changed-files](https://github.com/tj-actions/changed-files) from 47.0.0 to 47.0.1. - [Release notes](https://github.com/tj-actions/changed-files/releases) - [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md) - [Commits](https://github.com/tj-actions/changed-files/compare/24d32ffd492484c1d75e0c0b894501ddb9d30d62...e0021407031f5be11a464abee9a0776171c79891) --- updated-dependencies: - dependency-name: tj-actions/changed-files dependency-version: 47.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index cdfd5aea5..339d6fab8 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -99,7 +99,7 @@ jobs: fetch-depth: 0 - name: Get changed files id: changed-files - uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 # v47.0.0 + uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1 with: files: | powertools-*/** From ff3e3e6a3470199773715e51626f60d99338598a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:11:42 +0100 Subject: [PATCH 67/76] chore: bump github/codeql-action from 4.31.5 to 4.31.8 (#2331) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.5 to 4.31.8. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/fdbfb4d2750291e159f0156def62b853c2798ca2...1b168cd39490f61582a9beae412bb7057a6b2c4e) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.31.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index e15b0b5dc..7f3286dfe 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@fdbfb4d2750291e159f0156def62b853c2798ca2 # v3.29.5 + uses: github/codeql-action/upload-sarif@1b168cd39490f61582a9beae412bb7057a6b2c4e # v3.29.5 with: sarif_file: results.sarif From 42567838f53a764516f12126fe76d3bb289fb09a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 11:34:12 +0100 Subject: [PATCH 68/76] chore(ci): bump version to 2.9.0 (#2332) * chore(ci): bump version to 2.9.0 * Restore CHANGELOG.md from main. --------- Co-authored-by: Powertools for AWS Lambda (Java) Bot <151832416+aws-powertools-bot@users.noreply.github.com> Co-authored-by: Philipp Page --- README.md | 6 +++--- examples/pom.xml | 2 +- examples/powertools-examples-batch/pom.xml | 2 +- examples/powertools-examples-cloudformation/README.md | 4 ++-- .../infra/sam-graalvm/README.md | 2 +- examples/powertools-examples-cloudformation/pom.xml | 2 +- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- .../powertools-examples-core-utilities/cdk/infra/pom.xml | 2 +- .../powertools-examples-core-utilities/gradle/build.gradle | 6 +++--- .../kotlin/build.gradle.kts | 6 +++--- .../powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam/pom.xml | 2 +- .../powertools-examples-core-utilities/serverless/pom.xml | 2 +- .../powertools-examples-core-utilities/terraform/pom.xml | 2 +- .../powertools-examples-idempotency/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-idempotency/sam/pom.xml | 2 +- examples/powertools-examples-kafka/pom.xml | 2 +- .../main/java/org/demo/kafka/protobuf/ProtobufProduct.java | 4 ++-- .../org/demo/kafka/protobuf/ProtobufProductOrBuilder.java | 2 +- .../org/demo/kafka/protobuf/ProtobufProductOuterClass.java | 4 ++-- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam/pom.xml | 2 +- .../powertools-examples-serialization/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-serialization/sam/pom.xml | 2 +- examples/powertools-examples-validation/pom.xml | 2 +- mkdocs.yml | 2 +- pom.xml | 2 +- powertools-batch/pom.xml | 2 +- powertools-cloudformation/pom.xml | 2 +- powertools-common/pom.xml | 2 +- powertools-e2e-tests/handlers/batch/pom.xml | 2 +- .../handlers/idempotency-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/idempotency-generics/pom.xml | 2 +- powertools-e2e-tests/handlers/idempotency/pom.xml | 2 +- .../handlers/largemessage-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/largemessage/pom.xml | 2 +- .../handlers/largemessage_idempotent/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-functional/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-log4j/pom.xml | 2 +- powertools-e2e-tests/handlers/logging-logback/pom.xml | 2 +- powertools-e2e-tests/handlers/metrics/pom.xml | 2 +- powertools-e2e-tests/handlers/parameters/pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- powertools-e2e-tests/handlers/tracing/pom.xml | 2 +- powertools-e2e-tests/handlers/validation-alb-event/pom.xml | 2 +- .../handlers/validation-apigw-event/pom.xml | 2 +- powertools-e2e-tests/pom.xml | 2 +- powertools-idempotency/pom.xml | 2 +- powertools-idempotency/powertools-idempotency-core/pom.xml | 2 +- .../powertools-idempotency-dynamodb/pom.xml | 2 +- powertools-kafka/pom.xml | 2 +- powertools-large-messages/pom.xml | 2 +- powertools-logging/pom.xml | 2 +- powertools-logging/powertools-logging-log4j/pom.xml | 2 +- powertools-logging/powertools-logging-logback/pom.xml | 2 +- powertools-metrics/pom.xml | 2 +- powertools-parameters/pom.xml | 2 +- .../powertools-parameters-appconfig/pom.xml | 2 +- .../powertools-parameters-dynamodb/pom.xml | 2 +- powertools-parameters/powertools-parameters-secrets/pom.xml | 2 +- powertools-parameters/powertools-parameters-ssm/pom.xml | 2 +- powertools-parameters/powertools-parameters-tests/pom.xml | 2 +- powertools-serialization/pom.xml | 2 +- powertools-tracing/pom.xml | 2 +- powertools-validation/pom.xml | 2 +- 65 files changed, 74 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index 2644c47ab..4c02e2d1f 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,17 @@ Powertools for AWS Lambda (Java) is available in Maven Central. You can use your software.amazon.lambda powertools-tracing - 2.8.0 + 2.9.0 software.amazon.lambda powertools-logging-log4j - 2.8.0 + 2.9.0 software.amazon.lambda powertools-metrics - 2.8.0 + 2.9.0 ... diff --git a/examples/pom.xml b/examples/pom.xml index 511cdd5a9..5d191063f 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ software.amazon.lambda powertools-examples - 2.8.0 + 2.9.0 pom Powertools for AWS Lambda (Java) - Examples diff --git a/examples/powertools-examples-batch/pom.xml b/examples/powertools-examples-batch/pom.xml index 3a8e3ad8d..0091fb5ca 100644 --- a/examples/powertools-examples-batch/pom.xml +++ b/examples/powertools-examples-batch/pom.xml @@ -5,7 +5,7 @@ 4.0.0 software.amazon.lambda.examples - 2.8.0 + 2.9.0 powertools-examples-batch jar Powertools for AWS Lambda (Java) - Examples - Batch diff --git a/examples/powertools-examples-cloudformation/README.md b/examples/powertools-examples-cloudformation/README.md index b9e58eb82..27e564bf3 100644 --- a/examples/powertools-examples-cloudformation/README.md +++ b/examples/powertools-examples-cloudformation/README.md @@ -15,7 +15,7 @@ Run the following in your shell: ```bash cd infra/sam sam build -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.8.0718 +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.9.0718 ``` ### Deploy with CDK @@ -32,5 +32,5 @@ To build and deploy this application for the first time, run the following in yo cd infra/cdk mvn package cdk synth -cdk deploy -c BucketNameParam=my-unique-bucket-2.8.0718 +cdk deploy -c BucketNameParam=my-unique-bucket-2.9.0718 ``` \ No newline at end of file diff --git a/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md index c2a9e2ffd..3aca1408a 100644 --- a/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md +++ b/examples/powertools-examples-cloudformation/infra/sam-graalvm/README.md @@ -40,7 +40,7 @@ sam build ## Deploy the sample application ```shell -sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.8.0718 +sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-2.9.0718 ``` This sample is based on Serverless Application Model (SAM). To deploy it, check out the instructions for getting started with SAM in [the examples directory](../../../README.md) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 09adbfa18..8581a6ceb 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -3,7 +3,7 @@ 4.0.0 software.amazon.lambda.examples - 2.8.0 + 2.9.0 powertools-examples-cloudformation jar diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 29acef5e2..28be679a5 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda.examples - 2.8.0 + 2.9.0 powertools-examples-core-utilities-cdk jar diff --git a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml index 7daf3153f..e3ceb7e65 100644 --- a/examples/powertools-examples-core-utilities/cdk/infra/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/infra/pom.xml @@ -4,7 +4,7 @@ 4.0.0 software.amazon.lambda.examples cdk - 2.8.0 + 2.9.0 UTF-8 2.224.0 diff --git a/examples/powertools-examples-core-utilities/gradle/build.gradle b/examples/powertools-examples-core-utilities/gradle/build.gradle index 6841d20ab..b01fdcfaa 100644 --- a/examples/powertools-examples-core-utilities/gradle/build.gradle +++ b/examples/powertools-examples-core-utilities/gradle/build.gradle @@ -29,8 +29,8 @@ dependencies { implementation 'com.amazonaws:aws-lambda-java-events:3.16.0' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2' implementation 'org.aspectj:aspectjrt:1.9.20.1' - aspect 'software.amazon.lambda:powertools-tracing:2.8.0' - aspect 'software.amazon.lambda:powertools-logging-log4j:2.8.0' - aspect 'software.amazon.lambda:powertools-metrics:2.8.0' + aspect 'software.amazon.lambda:powertools-tracing:2.9.0' + aspect 'software.amazon.lambda:powertools-logging-log4j:2.9.0' + aspect 'software.amazon.lambda:powertools-metrics:2.9.0' } diff --git a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts index bc11eb32e..3dae5015e 100644 --- a/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts +++ b/examples/powertools-examples-core-utilities/kotlin/build.gradle.kts @@ -15,9 +15,9 @@ dependencies { implementation("com.amazonaws:aws-lambda-java-events:3.16.0") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.2") implementation("org.aspectj:aspectjrt:1.9.20.1") - aspect("software.amazon.lambda:powertools-tracing:2.8.0") - aspect("software.amazon.lambda:powertools-logging-log4j:2.8.0") - aspect("software.amazon.lambda:powertools-metrics:2.8.0") + aspect("software.amazon.lambda:powertools-tracing:2.9.0") + aspect("software.amazon.lambda:powertools-logging-log4j:2.9.0") + aspect("software.amazon.lambda:powertools-metrics:2.9.0") implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.24") } diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index fceb4a322..e7a3f202d 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -4,7 +4,7 @@ Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM GraalVM software.amazon.lambda.examples - 2.8.0 + 2.9.0 powertools-examples-core-utilities-sam-graalvm jar diff --git a/examples/powertools-examples-core-utilities/sam/pom.xml b/examples/powertools-examples-core-utilities/sam/pom.xml index b8606a227..2d6a00161 100644 --- a/examples/powertools-examples-core-utilities/sam/pom.xml +++ b/examples/powertools-examples-core-utilities/sam/pom.xml @@ -4,7 +4,7 @@ Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with SAM software.amazon.lambda.examples - 2.8.0 + 2.9.0 powertools-examples-core-utilities-sam jar diff --git a/examples/powertools-examples-core-utilities/serverless/pom.xml b/examples/powertools-examples-core-utilities/serverless/pom.xml index d77b0ccd6..26e647dad 100644 --- a/examples/powertools-examples-core-utilities/serverless/pom.xml +++ b/examples/powertools-examples-core-utilities/serverless/pom.xml @@ -4,7 +4,7 @@ Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Serverless software.amazon.lambda.examples - 2.8.0 + 2.9.0 powertools-examples-core-utilities-serverless jar diff --git a/examples/powertools-examples-core-utilities/terraform/pom.xml b/examples/powertools-examples-core-utilities/terraform/pom.xml index a6237b00f..4de1e415c 100644 --- a/examples/powertools-examples-core-utilities/terraform/pom.xml +++ b/examples/powertools-examples-core-utilities/terraform/pom.xml @@ -4,7 +4,7 @@ Powertools for AWS Lambda (Java) - Examples - Core Utilities (logging, tracing, metrics) with Terraform software.amazon.lambda.examples - 2.8.0 + 2.9.0 powertools-examples-core-utilities-terraform jar diff --git a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml index d1c0205ff..0536951aa 100644 --- a/examples/powertools-examples-idempotency/sam-graalvm/pom.xml +++ b/examples/powertools-examples-idempotency/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.8.0 + 2.9.0 powertools-examples-idempotency-sam-graalvm jar Powertools for AWS Lambda (Java) - Examples - Idempotency GraalVM diff --git a/examples/powertools-examples-idempotency/sam/pom.xml b/examples/powertools-examples-idempotency/sam/pom.xml index b90705c67..22d6a9c81 100644 --- a/examples/powertools-examples-idempotency/sam/pom.xml +++ b/examples/powertools-examples-idempotency/sam/pom.xml @@ -17,7 +17,7 @@ 4.0.0 software.amazon.lambda.examples - 2.8.0 + 2.9.0 powertools-examples-idempotency jar Powertools for AWS Lambda (Java) - Examples - Idempotency diff --git a/examples/powertools-examples-kafka/pom.xml b/examples/powertools-examples-kafka/pom.xml index 1b8e8f09d..d152f46c0 100644 --- a/examples/powertools-examples-kafka/pom.xml +++ b/examples/powertools-examples-kafka/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.8.0 + 2.9.0 powertools-examples-kafka jar Powertools for AWS Lambda (Java) - Examples - Kafka diff --git a/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProduct.java b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProduct.java index 13d8905f2..2bf5db844 100644 --- a/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProduct.java +++ b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProduct.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: ProtobufProduct.proto -// Protobuf Java Version: 4.33.0 +// Protobuf Java Version: 4.33.1 package org.demo.kafka.protobuf; @@ -19,7 +19,7 @@ public final class ProtobufProduct extends com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, /* minor= */ 33, - /* patch= */ 0, + /* patch= */ 1, /* suffix= */ "", "ProtobufProduct"); } diff --git a/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOrBuilder.java b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOrBuilder.java index ae7cb2182..caf17ad50 100644 --- a/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOrBuilder.java +++ b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: ProtobufProduct.proto -// Protobuf Java Version: 4.33.0 +// Protobuf Java Version: 4.33.1 package org.demo.kafka.protobuf; diff --git a/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOuterClass.java b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOuterClass.java index 4fcac7bd7..ce3214777 100644 --- a/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOuterClass.java +++ b/examples/powertools-examples-kafka/src/main/java/org/demo/kafka/protobuf/ProtobufProductOuterClass.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: ProtobufProduct.proto -// Protobuf Java Version: 4.33.0 +// Protobuf Java Version: 4.33.1 package org.demo.kafka.protobuf; @@ -13,7 +13,7 @@ private ProtobufProductOuterClass() {} com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, /* minor= */ 33, - /* patch= */ 0, + /* patch= */ 1, /* suffix= */ "", "ProtobufProductOuterClass"); } diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 09401eeff..7fc652bca 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.8.0 + 2.9.0 powertools-examples-parameters-sam-graalvm jar Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM diff --git a/examples/powertools-examples-parameters/sam/pom.xml b/examples/powertools-examples-parameters/sam/pom.xml index 3e8f99aa0..d2c3e68d2 100644 --- a/examples/powertools-examples-parameters/sam/pom.xml +++ b/examples/powertools-examples-parameters/sam/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.8.0 + 2.9.0 powertools-examples-parameters-sam jar Powertools for AWS Lambda (Java) - Examples - Parameters diff --git a/examples/powertools-examples-serialization/sam-graalvm/pom.xml b/examples/powertools-examples-serialization/sam-graalvm/pom.xml index 44661fb71..5077c8989 100644 --- a/examples/powertools-examples-serialization/sam-graalvm/pom.xml +++ b/examples/powertools-examples-serialization/sam-graalvm/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.8.0 + 2.9.0 powertools-examples-serialization-sam-graalvm jar Powertools for AWS Lambda (Java) - Examples - Serialization GraalVM diff --git a/examples/powertools-examples-serialization/sam/pom.xml b/examples/powertools-examples-serialization/sam/pom.xml index 93c60a2cb..cf66c3e14 100644 --- a/examples/powertools-examples-serialization/sam/pom.xml +++ b/examples/powertools-examples-serialization/sam/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.8.0 + 2.9.0 powertools-examples-serialization-sam jar Powertools for AWS Lambda (Java) - Examples - Serialization diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 1337bc2ee..2fa8462a5 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -16,7 +16,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 software.amazon.lambda.examples - 2.8.0 + 2.9.0 powertools-examples-validation jar Powertools for AWS Lambda (Java) - Examples - Validation diff --git a/mkdocs.yml b/mkdocs.yml index 5f35b96c0..b52b88cca 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -129,7 +129,7 @@ extra_javascript: extra: powertools: - version: 2.8.0 + version: 2.9.0 version: provider: mike default: latest diff --git a/pom.xml b/pom.xml index f400e0b26..b300e911d 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ software.amazon.lambda powertools-parent - 2.8.0 + 2.9.0 pom Powertools for AWS Lambda (Java) - Parent diff --git a/powertools-batch/pom.xml b/powertools-batch/pom.xml index 07d11d41c..37cfdf7b2 100644 --- a/powertools-batch/pom.xml +++ b/powertools-batch/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda powertools-parent - 2.8.0 + 2.9.0 A suite of utilities that makes batch message processing using AWS Lambda easier. diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 09e3299fd..cb06dc1f3 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 2.8.0 + 2.9.0 Powertools for AWS Lambda (Java) - Cloudformation diff --git a/powertools-common/pom.xml b/powertools-common/pom.xml index 551fae8bc..75ef10beb 100644 --- a/powertools-common/pom.xml +++ b/powertools-common/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 2.8.0 + 2.9.0 Powertools for AWS Lambda (Java) - Common Internal Utilities diff --git a/powertools-e2e-tests/handlers/batch/pom.xml b/powertools-e2e-tests/handlers/batch/pom.xml index f8e0a037f..3e89aadd2 100644 --- a/powertools-e2e-tests/handlers/batch/pom.xml +++ b/powertools-e2e-tests/handlers/batch/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.8.0 + 2.9.0 e2e-test-handler-batch diff --git a/powertools-e2e-tests/handlers/idempotency-functional/pom.xml b/powertools-e2e-tests/handlers/idempotency-functional/pom.xml index 2b53dc275..b5669b21f 100644 --- a/powertools-e2e-tests/handlers/idempotency-functional/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency-functional/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.8.0 + 2.9.0 e2e-test-handler-idempotency-functional diff --git a/powertools-e2e-tests/handlers/idempotency-generics/pom.xml b/powertools-e2e-tests/handlers/idempotency-generics/pom.xml index 76e65edb1..21a658e6c 100644 --- a/powertools-e2e-tests/handlers/idempotency-generics/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency-generics/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.8.0 + 2.9.0 e2e-test-handler-idempotency-generics diff --git a/powertools-e2e-tests/handlers/idempotency/pom.xml b/powertools-e2e-tests/handlers/idempotency/pom.xml index 36181ea01..921599bdb 100644 --- a/powertools-e2e-tests/handlers/idempotency/pom.xml +++ b/powertools-e2e-tests/handlers/idempotency/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.8.0 + 2.9.0 e2e-test-handler-idempotency diff --git a/powertools-e2e-tests/handlers/largemessage-functional/pom.xml b/powertools-e2e-tests/handlers/largemessage-functional/pom.xml index d29017074..ddfe39a5e 100644 --- a/powertools-e2e-tests/handlers/largemessage-functional/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage-functional/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.8.0 + 2.9.0 e2e-test-handler-largemessage-functional diff --git a/powertools-e2e-tests/handlers/largemessage/pom.xml b/powertools-e2e-tests/handlers/largemessage/pom.xml index 53b1e7c10..bee253988 100644 --- a/powertools-e2e-tests/handlers/largemessage/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.8.0 + 2.9.0 e2e-test-handler-largemessage diff --git a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml index 9f6f23389..5ef7e1963 100644 --- a/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml +++ b/powertools-e2e-tests/handlers/largemessage_idempotent/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.8.0 + 2.9.0 e2e-test-handler-large-msg-idempotent diff --git a/powertools-e2e-tests/handlers/logging-functional/pom.xml b/powertools-e2e-tests/handlers/logging-functional/pom.xml index 4616af233..4ec6e5008 100644 --- a/powertools-e2e-tests/handlers/logging-functional/pom.xml +++ b/powertools-e2e-tests/handlers/logging-functional/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.8.0 + 2.9.0 e2e-test-handler-logging-functional diff --git a/powertools-e2e-tests/handlers/logging-log4j/pom.xml b/powertools-e2e-tests/handlers/logging-log4j/pom.xml index bba711163..022f029e6 100644 --- a/powertools-e2e-tests/handlers/logging-log4j/pom.xml +++ b/powertools-e2e-tests/handlers/logging-log4j/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.8.0 + 2.9.0 e2e-test-handler-logging-log4j diff --git a/powertools-e2e-tests/handlers/logging-logback/pom.xml b/powertools-e2e-tests/handlers/logging-logback/pom.xml index bd442fe88..f8458db25 100644 --- a/powertools-e2e-tests/handlers/logging-logback/pom.xml +++ b/powertools-e2e-tests/handlers/logging-logback/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.8.0 + 2.9.0 e2e-test-handler-logging-logback diff --git a/powertools-e2e-tests/handlers/metrics/pom.xml b/powertools-e2e-tests/handlers/metrics/pom.xml index 90704c6d9..ddc6ae1bd 100644 --- a/powertools-e2e-tests/handlers/metrics/pom.xml +++ b/powertools-e2e-tests/handlers/metrics/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.8.0 + 2.9.0 e2e-test-handler-metrics diff --git a/powertools-e2e-tests/handlers/parameters/pom.xml b/powertools-e2e-tests/handlers/parameters/pom.xml index 670eeb9c0..fb2deb2aa 100644 --- a/powertools-e2e-tests/handlers/parameters/pom.xml +++ b/powertools-e2e-tests/handlers/parameters/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.8.0 + 2.9.0 e2e-test-handler-parameters diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index a9d3c3ab5..54f07d681 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -4,7 +4,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.8.0 + 2.9.0 pom Handlers for End-to-End tests Fake handlers that use Powertools for AWS Lambda (Java). diff --git a/powertools-e2e-tests/handlers/tracing/pom.xml b/powertools-e2e-tests/handlers/tracing/pom.xml index 89891df8c..9874ce986 100644 --- a/powertools-e2e-tests/handlers/tracing/pom.xml +++ b/powertools-e2e-tests/handlers/tracing/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.8.0 + 2.9.0 e2e-test-handler-tracing diff --git a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml index 8e5e4dadb..14dbb9b13 100644 --- a/powertools-e2e-tests/handlers/validation-alb-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-alb-event/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.8.0 + 2.9.0 e2e-test-handler-validation-alb-event diff --git a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml index 97e967c3b..290e47b13 100644 --- a/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml +++ b/powertools-e2e-tests/handlers/validation-apigw-event/pom.xml @@ -5,7 +5,7 @@ software.amazon.lambda e2e-test-handlers-parent - 2.8.0 + 2.9.0 e2e-test-handler-validation-apigw-event diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index bfd4350a0..fec4dec92 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -20,7 +20,7 @@ powertools-parent software.amazon.lambda - 2.8.0 + 2.9.0 powertools-e2e-tests diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index d15d3445d..cbe2384ba 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -21,7 +21,7 @@ software.amazon.lambda powertools-parent - 2.8.0 + 2.9.0 powertools-idempotency diff --git a/powertools-idempotency/powertools-idempotency-core/pom.xml b/powertools-idempotency/powertools-idempotency-core/pom.xml index 801664b4a..4cba1956f 100644 --- a/powertools-idempotency/powertools-idempotency-core/pom.xml +++ b/powertools-idempotency/powertools-idempotency-core/pom.xml @@ -21,7 +21,7 @@ software.amazon.lambda powertools-idempotency - 2.8.0 + 2.9.0 powertools-idempotency-core diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml index a9cf5739b..d223e0d2f 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml +++ b/powertools-idempotency/powertools-idempotency-dynamodb/pom.xml @@ -21,7 +21,7 @@ software.amazon.lambda powertools-idempotency - 2.8.0 + 2.9.0 powertools-idempotency-dynamodb diff --git a/powertools-kafka/pom.xml b/powertools-kafka/pom.xml index 82f6b8021..c71ef94f6 100644 --- a/powertools-kafka/pom.xml +++ b/powertools-kafka/pom.xml @@ -21,7 +21,7 @@ powertools-parent software.amazon.lambda - 2.8.0 + 2.9.0 powertools-kafka diff --git a/powertools-large-messages/pom.xml b/powertools-large-messages/pom.xml index d72877552..ad5910ec1 100644 --- a/powertools-large-messages/pom.xml +++ b/powertools-large-messages/pom.xml @@ -23,7 +23,7 @@ software.amazon.lambda powertools-parent - 2.8.0 + 2.9.0 powertools-large-messages diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 923d6d133..200358e0b 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -21,7 +21,7 @@ powertools-parent software.amazon.lambda - 2.8.0 + 2.9.0 Powertools for AWS Lambda (Java) - Logging diff --git a/powertools-logging/powertools-logging-log4j/pom.xml b/powertools-logging/powertools-logging-log4j/pom.xml index 19a8b9428..aa4aca181 100644 --- a/powertools-logging/powertools-logging-log4j/pom.xml +++ b/powertools-logging/powertools-logging-log4j/pom.xml @@ -7,7 +7,7 @@ powertools-parent software.amazon.lambda - 2.8.0 + 2.9.0 ../../pom.xml diff --git a/powertools-logging/powertools-logging-logback/pom.xml b/powertools-logging/powertools-logging-logback/pom.xml index 7ed1f1a17..dbf7f5207 100644 --- a/powertools-logging/powertools-logging-logback/pom.xml +++ b/powertools-logging/powertools-logging-logback/pom.xml @@ -6,7 +6,7 @@ powertools-parent software.amazon.lambda - 2.8.0 + 2.9.0 ../../pom.xml diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index 86bdcc560..5f6c971e3 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 2.8.0 + 2.9.0 Powertools for AWS Lambda (Java) - Metrics diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index 78234b5cc..124e22186 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -21,7 +21,7 @@ powertools-parent software.amazon.lambda - 2.8.0 + 2.9.0 powertools-parameters diff --git a/powertools-parameters/powertools-parameters-appconfig/pom.xml b/powertools-parameters/powertools-parameters-appconfig/pom.xml index 3c38963a1..406f715d3 100644 --- a/powertools-parameters/powertools-parameters-appconfig/pom.xml +++ b/powertools-parameters/powertools-parameters-appconfig/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 2.8.0 + 2.9.0 ../../pom.xml diff --git a/powertools-parameters/powertools-parameters-dynamodb/pom.xml b/powertools-parameters/powertools-parameters-dynamodb/pom.xml index b9e48567a..eb5604046 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/pom.xml +++ b/powertools-parameters/powertools-parameters-dynamodb/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 2.8.0 + 2.9.0 ../../pom.xml diff --git a/powertools-parameters/powertools-parameters-secrets/pom.xml b/powertools-parameters/powertools-parameters-secrets/pom.xml index c83c674d5..b9535269e 100644 --- a/powertools-parameters/powertools-parameters-secrets/pom.xml +++ b/powertools-parameters/powertools-parameters-secrets/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 2.8.0 + 2.9.0 ../../pom.xml diff --git a/powertools-parameters/powertools-parameters-ssm/pom.xml b/powertools-parameters/powertools-parameters-ssm/pom.xml index d23b84bf6..e0253e10b 100644 --- a/powertools-parameters/powertools-parameters-ssm/pom.xml +++ b/powertools-parameters/powertools-parameters-ssm/pom.xml @@ -7,7 +7,7 @@ software.amazon.lambda powertools-parent - 2.8.0 + 2.9.0 ../../pom.xml diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml index 9b93b3f79..fa2542730 100644 --- a/powertools-parameters/powertools-parameters-tests/pom.xml +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -6,7 +6,7 @@ software.amazon.lambda powertools-parent - 2.8.0 + 2.9.0 ../../pom.xml diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index 535f23190..81603cd4f 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -21,7 +21,7 @@ powertools-parent software.amazon.lambda - 2.8.0 + 2.9.0 powertools-serialization diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 9e5603d70..545633c50 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 2.8.0 + 2.9.0 Powertools for AWS Lambda (Java) - Tracing diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 37063cf6d..8dfce6b6a 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -24,7 +24,7 @@ powertools-parent software.amazon.lambda - 2.8.0 + 2.9.0 Powertools for AWS Lambda (Java) - Validation From 058c61f57f172715328b2c76dc5a32b4fd765a1c Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Tue, 16 Dec 2025 11:46:06 +0100 Subject: [PATCH 69/76] docs: Announce end-of-life of version 1.x.x (#2333) --- docs/processes/versioning.md | 8 ++++---- docs/upgrade.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/processes/versioning.md b/docs/processes/versioning.md index bbb60f507..d20269001 100644 --- a/docs/processes/versioning.md +++ b/docs/processes/versioning.md @@ -55,7 +55,7 @@ To see the list of available major versions of Powertools for AWS Lambda and whe ### Version support matrix -| SDK | Major version | Current Phase | General Availability Date | Notes | -| -------------------------------- | ------------- | -------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------- | -| Powertools for AWS Lambda (Java) | 2.x | General Availability | 06/12/2025 | See [Release notes](https://github.com/aws-powertools/powertools-lambda-java/releases/tag/v2.0.0) | -| Powertools for AWS Lambda (Java) | 1.x | Maintenance | 11/04/2020 | End-of-support: December 12, 2025. See [upgrade guide](https://docs.powertools.aws.dev/lambda/java/latest/upgrade/) | +| SDK | Major version | Current Phase | General Availability Date | Notes | +| -------------------------------- | ------------- | -------------------- | ------------------------- | ------------------------------------------------------------------------------------------------- | +| Powertools for AWS Lambda (Java) | 2.x | General Availability | 06/12/2025 | See [Release notes](https://github.com/aws-powertools/powertools-lambda-java/releases/tag/v2.0.0) | +| Powertools for AWS Lambda (Java) | 1.x | End-of-life | 11/04/2020 | See [announcement](https://github.com/aws-powertools/powertools-lambda-java/issues/1895) | diff --git a/docs/upgrade.md b/docs/upgrade.md index 5b6d16d99..c9662a3db 100644 --- a/docs/upgrade.md +++ b/docs/upgrade.md @@ -7,7 +7,7 @@ description: Guide to update between major Powertools for AWS Lambda (Java) vers !!! warning "End of support notice" - On December 12th, 2025, Powertools for AWS Lambda (Java) v1 will reach end of support and will no longer receive updates or releases. If you are still using v1, we strongly recommend you to read our upgrade guide and update to the latest version. + On December 12th, 2025, Powertools for AWS Lambda (Java) v1 reached end-of-life and will no longer receive updates or releases. If you are still using v1, we strongly recommend you to read our upgrade guide and update to the latest version. Refer to [our announcement](https://github.com/aws-powertools/powertools-lambda-java/issues/1895) for details. Given our commitment to all of our customers using Powertools for AWS Lambda (Java), we will keep [Maven Central](https://central.sonatype.com/search?q=powertools){target="\_blank"} `v1` releases and a `v1` documentation archive to prevent any disruption. From df51f8b56ce63af4e2fc86a31ba90daaf2a9d38b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 14:23:43 +0000 Subject: [PATCH 70/76] chore: bump aws.sdk.version from 2.39.1 to 2.40.9 (#2335) Bumps `aws.sdk.version` from 2.39.1 to 2.40.9. Updates `software.amazon.awssdk:bom` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:http-client-spi` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:url-connection-client` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:s3` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:dynamodb` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:lambda` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:kinesis` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:cloudwatch` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:xray` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:sqs` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:cloudformation` from 2.39.1 to 2.40.9 Updates `software.amazon.awssdk:sts` from 2.39.1 to 2.40.9 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-version: 2.40.9 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.40.9 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:url-connection-client dependency-version: 2.40.9 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:s3 dependency-version: 2.40.9 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:dynamodb dependency-version: 2.40.9 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:lambda dependency-version: 2.40.9 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:kinesis dependency-version: 2.40.9 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudwatch dependency-version: 2.40.9 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:xray dependency-version: 2.40.9 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sqs dependency-version: 2.40.9 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:cloudformation dependency-version: 2.40.9 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: software.amazon.awssdk:sts dependency-version: 2.40.9 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-cloudformation/pom.xml | 2 +- pom.xml | 2 +- powertools-e2e-tests/handlers/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml index 8581a6ceb..212c0966b 100644 --- a/examples/powertools-examples-cloudformation/pom.xml +++ b/examples/powertools-examples-cloudformation/pom.xml @@ -14,7 +14,7 @@ 11 1.4.0 3.16.1 - 2.39.1 + 2.40.9 1.9.20.1 diff --git a/pom.xml b/pom.xml index b300e911d..a7b3d2fee 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ 2.25.2 2.0.17 2.20.1 - 2.39.1 + 2.40.9 2.20.0 2.2.0 UTF-8 diff --git a/powertools-e2e-tests/handlers/pom.xml b/powertools-e2e-tests/handlers/pom.xml index 54f07d681..477b49dc0 100644 --- a/powertools-e2e-tests/handlers/pom.xml +++ b/powertools-e2e-tests/handlers/pom.xml @@ -19,7 +19,7 @@ 3.6.1 1.14.1 3.14.1 - 2.39.1 + 2.40.9 1.9.20.1 true From e8b1bb7cd74ab69a2c98c1afc0299ad6e683ae5a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 14:24:37 +0000 Subject: [PATCH 71/76] chore: bump org.mockito:mockito-junit-jupiter from 5.20.0 to 5.21.0 (#2336) Bumps [org.mockito:mockito-junit-jupiter](https://github.com/mockito/mockito) from 5.20.0 to 5.21.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v5.20.0...v5.21.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-junit-jupiter dependency-version: 5.21.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a7b3d2fee..6188b47a4 100644 --- a/pom.xml +++ b/pom.xml @@ -114,7 +114,7 @@ 2.20.1 1.7.0 5.20.0 - 5.20.0 + 5.21.0 2.3.0 1.5.0 From 9a18c5d5356053993e60bfdc3a4a94212b9d68d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 14:24:48 +0000 Subject: [PATCH 72/76] chore: bump actions/upload-artifact from 5.0.0 to 6.0.0 (#2338) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5.0.0 to 6.0.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/330a01c490aca151604b8cf639adc76d48f6c5d4...b7c566a772e6b6bfb58ed0dc250532a479d7789f) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: 6.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- .github/workflows/security-scorecard.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c9b2d80c6..9e52301dd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -112,7 +112,7 @@ jobs: snapshot: ${{ inputs.snapshot}} - id: upload_source name: Upload artifacts - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: if-no-files-found: error name: source diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index 7f3286dfe..f9aaaea52 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -46,7 +46,7 @@ jobs: publish_results: true repo_token: ${{ secrets.SCORECARD_TOKEN }} - name: Upload Results - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: SARIF file path: results.sarif From 9ee9a95081f1559fa86f04221cbf6aa5b8c692dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 14:24:59 +0000 Subject: [PATCH 73/76] chore: bump actions/download-artifact from 6.0.0 to 7.0.0 (#2339) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 6.0.0 to 7.0.0. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/018cc2cf5baa6db3ef3c5f8a56943fffe632ef53...37930b1c2abaa49bbe596cd826c3c89aef350131) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9e52301dd..630b91321 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -133,7 +133,7 @@ jobs: steps: - id: download_source name: Download artifacts - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v4.6.1 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v4.6.1 with: name: source - name: Setup Java @@ -168,7 +168,7 @@ jobs: steps: - id: download_source name: Download artifacts - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v4.6.1 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v4.6.1 with: name: source - name: Setup Java @@ -191,7 +191,7 @@ jobs: steps: - id: download_source name: Download artifacts - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v4.6.1 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v4.6.1 with: name: source - name: Setup Java @@ -229,7 +229,7 @@ jobs: ref: ${{ env.RELEASE_COMMIT }} - id: download_source name: Download artifacts - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v4.6.1 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v4.6.1 with: name: source - id: setup-git From ad640f7a61385f20208c8dc3a0296e162934e138 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 14:25:08 +0000 Subject: [PATCH 74/76] chore: bump github/codeql-action from 4.31.8 to 4.31.9 (#2340) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.8 to 4.31.9. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/1b168cd39490f61582a9beae412bb7057a6b2c4e...5d4e8d1aca955e8d8589aabd499c5cae939e33c7) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.31.9 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/security-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml index f9aaaea52..b91e78c69 100644 --- a/.github/workflows/security-scorecard.yml +++ b/.github/workflows/security-scorecard.yml @@ -52,6 +52,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to Code-Scanning - uses: github/codeql-action/upload-sarif@1b168cd39490f61582a9beae412bb7057a6b2c4e # v3.29.5 + uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v3.29.5 with: sarif_file: results.sarif From 21e1fcffc3793c51235bcfd6778c483d57320dae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 14:25:44 +0000 Subject: [PATCH 75/76] chore: bump sam/build-java25 (#2342) Bumps sam/build-java25 from `b34fc78` to `bffac7d`. --- updated-dependencies: - dependency-name: sam/build-java25 dependency-version: bffac7de6e418a93d2aefc1e8e7c79eda0971e7a026725fe618b58ddfba7a128 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- powertools-e2e-tests/src/test/resources/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powertools-e2e-tests/src/test/resources/docker/Dockerfile b/powertools-e2e-tests/src/test/resources/docker/Dockerfile index fbefe46dd..1ceb29aa0 100644 --- a/powertools-e2e-tests/src/test/resources/docker/Dockerfile +++ b/powertools-e2e-tests/src/test/resources/docker/Dockerfile @@ -1,5 +1,5 @@ # Use the official AWS SAM base image for Java 25 -FROM public.ecr.aws/sam/build-java25@sha256:b34fc789a70464f080232ee96590a6d0c7249eb59b1c33deccaae08a882cac23 +FROM public.ecr.aws/sam/build-java25@sha256:bffac7de6e418a93d2aefc1e8e7c79eda0971e7a026725fe618b58ddfba7a128 # Install GraalVM dependencies RUN curl -4 -L https://download.oracle.com/graalvm/25/latest/graalvm-jdk-25_linux-x64_bin.tar.gz | tar -xvz From 54fe0be69938dbecb0d4efaa6c0152ee20c34eac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 14:25:57 +0000 Subject: [PATCH 76/76] chore: bump org.apache.logging.log4j:log4j-core from 2.25.2 to 2.25.3 (#2344) Bumps org.apache.logging.log4j:log4j-core from 2.25.2 to 2.25.3. --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-core dependency-version: 2.25.3 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/powertools-examples-core-utilities/cdk/app/pom.xml | 2 +- examples/powertools-examples-core-utilities/sam-graalvm/pom.xml | 2 +- examples/powertools-examples-parameters/sam-graalvm/pom.xml | 2 +- pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/powertools-examples-core-utilities/cdk/app/pom.xml b/examples/powertools-examples-core-utilities/cdk/app/pom.xml index 28be679a5..c02b73026 100644 --- a/examples/powertools-examples-core-utilities/cdk/app/pom.xml +++ b/examples/powertools-examples-core-utilities/cdk/app/pom.xml @@ -11,7 +11,7 @@ jar - 2.25.2 + 2.25.3 11 11 1.9.20.1 diff --git a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml index e7a3f202d..eea0357e9 100644 --- a/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml +++ b/examples/powertools-examples-core-utilities/sam-graalvm/pom.xml @@ -9,7 +9,7 @@ jar - 2.25.2 + 2.25.3 11 11 1.9.20.1 diff --git a/examples/powertools-examples-parameters/sam-graalvm/pom.xml b/examples/powertools-examples-parameters/sam-graalvm/pom.xml index 7fc652bca..2aaffd9d1 100644 --- a/examples/powertools-examples-parameters/sam-graalvm/pom.xml +++ b/examples/powertools-examples-parameters/sam-graalvm/pom.xml @@ -8,7 +8,7 @@ Powertools for AWS Lambda (Java) - Examples - Parameters GraalVM - 2.25.2 + 2.25.3 11 11 1.9.20.1 diff --git a/pom.xml b/pom.xml index 6188b47a4..e6cf78b14 100644 --- a/pom.xml +++ b/pom.xml @@ -82,7 +82,7 @@ 11 11 3.1.2 - 2.25.2 + 2.25.3 2.0.17 2.20.1 2.40.9