Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5ec3c21
fix: cold start only on on-demand invocation
Attyuttam Nov 26, 2025
090de1d
fix: cold start only on on-demand invocation
Attyuttam Nov 26, 2025
5d5d43d
fix: cold start only on on-demand invocation
Attyuttam Nov 27, 2025
75d04c1
Merge branch 'main' into main
Attyuttam Nov 27, 2025
ff08406
mvn clean test is successful
Attyuttam Nov 28, 2025
35bfb4e
Merge branch 'main' of https://github.com/Attyuttam/powertools-lambda…
Attyuttam Nov 28, 2025
5b47395
reversed the sqllite changes
Attyuttam Nov 28, 2025
d0e7a4c
fix: priming for powertools-tracing
Attyuttam Dec 22, 2025
c56f431
Merge branch 'aws-powertools:main' into main
Attyuttam Dec 22, 2025
c9debda
reverted OS specific changes
Attyuttam Dec 24, 2025
e7a2781
mvn clean test is successful
Attyuttam Nov 28, 2025
9ea5501
reversed the sqllite changes
Attyuttam Nov 28, 2025
bfc5fb1
fix: priming for powertools-tracing
Attyuttam Dec 22, 2025
68d6157
reverted OS specific changes
Attyuttam Dec 24, 2025
380176e
resolved testing issues
Attyuttam Dec 24, 2025
69aafa5
resolved testing issues
Attyuttam Dec 24, 2025
142e102
Merge remote-tracking branch 'origin/main'
Attyuttam Dec 24, 2025
b94942e
resolved testing issues
Attyuttam Dec 24, 2025
836a3a0
resolved testing issues
Attyuttam Dec 24, 2025
774e316
resolved testing issues
Attyuttam Dec 24, 2025
4f9e8f0
incorporated comments
Attyuttam Dec 25, 2025
fbb84b4
Update pom.xml
Attyuttam Dec 31, 2025
eadd04d
Added CRaC support for serialization utility
Attyuttam Jan 6, 2026
d3d0498
Added CRaC support for serialization utility
Attyuttam Jan 19, 2026
d8a338f
Added CRaC support for serialization utility
Attyuttam Jan 19, 2026
6621095
Added CRaC support for serialization utility
Attyuttam Jan 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions docs/utilities/serialization.md
Original file line number Diff line number Diff line change
Expand Up @@ -472,3 +472,50 @@ to powertools.You can then use it to do your validation or in idempotency module
}
}
```
## Advanced

### Lambda SnapStart priming

The Serialization utility integrates with AWS Lambda SnapStart to improve restore durations. To make sure the SnapStart priming logic of this utility runs correctly, you need an explicit reference to `EventDeserializer` in your code to allow the library to register before SnapStart takes a memory snapshot. Learn more about what priming is in this [blog post](https://aws.amazon.com/blogs/compute/optimizing-cold-start-performance-of-aws-lambda-using-advanced-priming-strategies-with-snapstart/){target="_blank"}.

If you don't set a custom `EventDeserializer` in your code yet, make sure to reference `EventDeserializer` in your Lambda handler initialization code. This can be done by adding one of the following lines to your handler class:

=== "Constructor"

```java hl_lines="7"
import software.amazon.lambda.powertools.utilities.EventDeserializer;

public class MyFunctionHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {

public MyFunctionHandler() {
EventDeserializer.init(); // Ensure TracingUtils is loaded for SnapStart
}

@Override
public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) {
// ...
return something;
}
}
```

=== "Static Initializer"

```java hl_lines="7"
import software.amazon.lambda.powertools.utilities.EventDeserializer;


public class MyFunctionHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {

static {
EventDeserializer.init(); // Ensure TracingUtils is loaded for SnapStart
}

@Override
public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) {
// ...
return something;
}
}
```

45 changes: 45 additions & 0 deletions powertools-serialization/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,19 @@
<description>Utilities for JSON serialization used across the project.</description>

<dependencies>
<dependency>
<groupId>org.crac</groupId>
<artifactId>crac</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.lambda</groupId>
<artifactId>powertools-common</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.burt</groupId>
<artifactId>jmespath-jackson</artifactId>
Expand Down Expand Up @@ -100,8 +113,33 @@
</build>

<profiles>
<profile>
<id>generate-classesloaded-file</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
-Xlog:class+load=info:classesloaded.txt
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.lang=ALL-UNNAMED
</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>generate-graalvm-files</id>
<dependencies>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-subclass</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
Expand All @@ -118,6 +156,13 @@
</profile>
<profile>
<id>graalvm-native</id>
<dependencies>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-subclass</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,17 @@

package software.amazon.lambda.powertools.utilities;

import org.crac.Context;
import org.crac.Core;
import org.crac.Resource;
import software.amazon.lambda.powertools.common.internal.ClassPreLoader;

import static java.nio.charset.StandardCharsets.UTF_8;
import static software.amazon.lambda.powertools.utilities.jmespath.Base64Function.decode;
import static software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction.decompress;

import java.nio.ByteBuffer;

import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent;
import com.amazonaws.services.lambda.runtime.events.ActiveMQEvent;
Expand Down Expand Up @@ -48,9 +55,158 @@
* Class that can be used to extract the meaningful part of an event and deserialize it into a Java object.<br/>
* For example, extract the body of an API Gateway event, or messages from an SQS event.
*/
public class EventDeserializer {
public class EventDeserializer implements Resource{

private static final Logger LOG = LoggerFactory.getLogger(EventDeserializer.class);
private static final EventDeserializer INSTANCE = new EventDeserializer();

static {
Core.getGlobalContext().register(INSTANCE);
}

public static void init() {
// Placeholder method used to enable SnapStart priming. Users need a direct reference to this class in order for the CRaC hooks to execute.
new EventDeserializer();
}
@Override
public void beforeCheckpoint(Context<? extends Resource> context) throws Exception {
init();
primeAllEventTypes();
ClassPreLoader.preloadClasses();
}

@Override
public void afterRestore(Context<? extends Resource> context) throws Exception {
// This is a no-op, as we don't need to do anything after restore
}
private void primeAllEventTypes() {
try {
// API Gateway v1
APIGatewayProxyRequestEvent apiV1 = new APIGatewayProxyRequestEvent();
apiV1.setBody("{}");
extractDataFrom(apiV1);

// API Gateway v2
APIGatewayV2HTTPEvent apiV2 = new APIGatewayV2HTTPEvent();
apiV2.setBody("{}");
extractDataFrom(apiV2);

// SNS
SNSEvent snsEvent = new SNSEvent();
SNSEvent.SNS sns = new SNSEvent.SNS();
sns.setMessage("{}");
SNSEvent.SNSRecord snsRecord = new SNSEvent.SNSRecord();
snsRecord.setSns(sns);
snsEvent.setRecords(List.of(snsRecord));
extractDataFrom(snsEvent);

// SQS
SQSEvent sqsEvent = new SQSEvent();
SQSEvent.SQSMessage sqsMessage = new SQSEvent.SQSMessage();
sqsMessage.setBody("{}");
sqsEvent.setRecords(List.of(sqsMessage));
extractDataFrom(sqsEvent);

// Single SQS message
extractDataFrom(sqsMessage);

// Scheduled Event
ScheduledEvent scheduledEvent = new ScheduledEvent();
scheduledEvent.setDetail(Map.of("key", "value"));
extractDataFrom(scheduledEvent);

// ALB
ApplicationLoadBalancerRequestEvent albEvent =
new ApplicationLoadBalancerRequestEvent();
albEvent.setBody("{}");
extractDataFrom(albEvent);

// CloudWatch Logs
CloudWatchLogsEvent cwEvent = new CloudWatchLogsEvent();
CloudWatchLogsEvent.AWSLogs logs = new CloudWatchLogsEvent.AWSLogs();
logs.setData("ewogICJpZCI6IDEyMzQsCiAgIm5hbWUiOiAicHJvZHVjdCIsCiAgInByaWNlIjogNDIKfQ");
cwEvent.setAwsLogs(logs);
extractDataFrom(cwEvent);

// CloudFormation
CloudFormationCustomResourceEvent cfEvent =
new CloudFormationCustomResourceEvent();
cfEvent.setResourceProperties(Map.of("key", "value"));
extractDataFrom(cfEvent);

// Kinesis
KinesisEvent kinesisEvent = new KinesisEvent();
KinesisEvent.Record kinesisRecord = new KinesisEvent.Record();
kinesisRecord.setData(ByteBuffer.allocate(0));
KinesisEvent.KinesisEventRecord kinesisEventRecord =
new KinesisEvent.KinesisEventRecord();
kinesisEventRecord.setKinesis(kinesisRecord);
kinesisEvent.setRecords(List.of(kinesisEventRecord));
extractDataFrom(kinesisEvent);

// Single Kinesis record
extractDataFrom(kinesisEventRecord);

// Firehose
KinesisFirehoseEvent firehoseEvent = new KinesisFirehoseEvent();
KinesisFirehoseEvent.Record firehoseRecord =
new KinesisFirehoseEvent.Record();
firehoseRecord.setData(ByteBuffer.allocate(0));
firehoseEvent.setRecords(List.of(firehoseRecord));
extractDataFrom(firehoseEvent);

// Kafka
KafkaEvent kafkaEvent = new KafkaEvent();
KafkaEvent.KafkaEventRecord kafkaRecord =
new KafkaEvent.KafkaEventRecord();
kafkaRecord.setValue("eyJpZCI6MTIzNCwgIm5hbWUiOiJwcm9kdWN0IiwgInByaWNlIjo0Mn0=");
kafkaEvent.setRecords(
Map.of("topic", List.of(kafkaRecord))
);
extractDataFrom(kafkaEvent);

// ActiveMQ
ActiveMQEvent activeMQEvent = new ActiveMQEvent();
ActiveMQEvent.ActiveMQMessage activeMQMessage =
new ActiveMQEvent.ActiveMQMessage();
activeMQMessage.setData("eyJpZCI6MTIzNCwgIm5hbWUiOiJwcm9kdWN0IiwgInByaWNlIjo0Mn0=");
activeMQEvent.setMessages(List.of(activeMQMessage));
extractDataFrom(activeMQEvent);

// RabbitMQ
RabbitMQEvent rabbitMQEvent = new RabbitMQEvent();
RabbitMQEvent.RabbitMessage rabbitMessage =
new RabbitMQEvent.RabbitMessage();
rabbitMessage.setData("eyJpZCI6MTIzNCwgIm5hbWUiOiJwcm9kdWN0IiwgInByaWNlIjo0Mn0=");
rabbitMQEvent.setRmqMessagesByQueue(
Map.of("queue", List.of(rabbitMessage))
);
extractDataFrom(rabbitMQEvent);

// Kinesis Analytics Firehose preprocessing
KinesisAnalyticsFirehoseInputPreprocessingEvent kaFirehoseEvent =
new KinesisAnalyticsFirehoseInputPreprocessingEvent();
KinesisAnalyticsFirehoseInputPreprocessingEvent.Record kaFirehoseRecord =
new KinesisAnalyticsFirehoseInputPreprocessingEvent.Record();
kaFirehoseRecord.setData(ByteBuffer.allocate(0));
kaFirehoseEvent.setRecords(List.of(kaFirehoseRecord));
extractDataFrom(kaFirehoseEvent);

// Kinesis Analytics Streams preprocessing
KinesisAnalyticsStreamsInputPreprocessingEvent kaStreamsEvent =
new KinesisAnalyticsStreamsInputPreprocessingEvent();
KinesisAnalyticsStreamsInputPreprocessingEvent.Record kaStreamsRecord =
new KinesisAnalyticsStreamsInputPreprocessingEvent.Record();
kaStreamsRecord.setData(ByteBuffer.allocate(0));
kaStreamsEvent.setRecords(List.of(kaStreamsRecord));
extractDataFrom(kaStreamsEvent);

} catch (Exception e) {
// Best-effort priming only — never fail checkpointing
LOG.debug("EventDeserializer priming failed", e);
throw new PrimingException("Failed to prime event ", e);
}
}

/**
* Extract the meaningful part of a Lambda Event object. Main events are built-in:
Expand Down Expand Up @@ -165,7 +321,7 @@
* Meaningful part of a Lambda event.<br/>
* Use {@link #extractDataFrom(Object)} to retrieve an instance of this class.
*/
public static class EventPart {

Check failure on line 324 in powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java

View workflow job for this annotation

GitHub Actions / pmd_analyse

This class has only private constructors and may be final

Reports classes that may be made final because they cannot be extended from outside their compilation unit anyway. This is because all their constructors are private, so a subclass could not call the super constructor. ClassWithOnlyPrivateConstructorsShouldBeFinal (Priority: 1, Ruleset: Design) https://docs.pmd-code.org/snapshot/pmd_rules_java_design.html#classwithonlyprivateconstructorsshouldbefinal
private Map<String, Object> contentMap;
private String content;
private List<String> contentList;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package software.amazon.lambda.powertools.utilities;

/**
* Exception thrown when priming operations fail during CRaC checkpoint preparation.
*/
public class PrimingException extends RuntimeException {
public PrimingException(String message, Exception e) {
super(message,e);
}
}
Loading
Loading