CustomConfigExample.java
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package software.amazon.lambda.durable.examples.general;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import java.io.IOException;
import java.time.Duration;
import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
import software.amazon.awssdk.core.SdkSystemSetting;
import software.amazon.awssdk.http.apache.ApacheHttpClient;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.lambda.LambdaClient;
import software.amazon.lambda.durable.DurableConfig;
import software.amazon.lambda.durable.DurableContext;
import software.amazon.lambda.durable.DurableHandler;
import software.amazon.lambda.durable.TypeToken;
import software.amazon.lambda.durable.client.LambdaDurableFunctionsClient;
import software.amazon.lambda.durable.serde.SerDes;
/**
* Example demonstrating custom configuration with both custom HTTP client and custom SerDes. Shows how to configure a
* custom Apache HTTP client for the Lambda client while maintaining automatic credentials detection and region
* fallback, plus a custom SerDes with snake_case naming.
*
* <p>This example demonstrates:
*
* <ul>
* <li>Custom Apache HTTP client configuration for improved performance
* <li>Automatic region detection with fallback to us-east-1 for testing environments
* <li>Environment variable credentials provider
* <li>Custom SerDes with snake_case property naming
* </ul>
*/
public class CustomConfigExample extends DurableHandler<String, String> {
@Override
protected DurableConfig createConfiguration() {
// Configure custom Apache HTTP client for better performance
var httpClient = ApacheHttpClient.builder()
.maxConnections(50)
.connectionTimeout(Duration.ofSeconds(30))
.socketTimeout(Duration.ofSeconds(60))
.build();
// Get region with fallback to us-east-1 if AWS_REGION not set
// This prevents initialization failures in testing environments
var region = System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable());
if (region == null || region.isEmpty()) {
region = "us-east-1";
}
// Create Lambda client with custom HTTP client
// Uses automatic credentials detection and region fallback
var lambdaClient = LambdaClient.builder()
.httpClient(httpClient)
.credentialsProvider(EnvironmentVariableCredentialsProvider.create())
.region(Region.of(region))
.build();
// Wrap the Lambda client with LambdaDurableFunctionsClient
var durableClient = new LambdaDurableFunctionsClient(lambdaClient);
// Create custom SerDes with snake_case naming
var customSerDes = new SnakeCaseSerDes();
return DurableConfig.builder()
.withDurableExecutionClient(durableClient)
.withSerDes(customSerDes)
.build();
}
@Override
public String handleRequest(String input, DurableContext context) {
// Step 1: Create a custom object with camelCase fields to demonstrate snake_case serialization
var customObject = context.step(
"create-custom-object",
CustomData.class,
stepCtx -> new CustomData("user123", "John Doe", 25, "john.doe@example.com"));
return "Created custom object: " + customObject.userId + ", " + customObject.fullName + ", "
+ customObject.userAge + ", " + customObject.emailAddress;
}
/**
* Custom data class with camelCase field names to demonstrate snake_case serialization. The SerDes will convert
* these field names to snake_case in the JSON output.
*/
public static class CustomData {
public String userId;
public String fullName;
public int userAge;
public String emailAddress;
public CustomData() {}
public CustomData(String userId, String fullName, int userAge, String emailAddress) {
this.userId = userId;
this.fullName = fullName;
this.userAge = userAge;
this.emailAddress = emailAddress;
}
}
/**
* Custom SerDes implementation using snake_case property naming. Demonstrates how to provide custom serialization
* behavior.
*/
private static class SnakeCaseSerDes implements SerDes {
private final ObjectMapper objectMapper;
public SnakeCaseSerDes() {
this.objectMapper = new ObjectMapper().setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
}
@Override
public String serialize(Object obj) {
try {
return objectMapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
throw new RuntimeException("Serialization failed", e);
}
}
@Override
public <T> T deserialize(String json, TypeToken<T> typeToken) {
try {
return objectMapper.readValue(json, objectMapper.constructType(typeToken.getType()));
} catch (IOException e) {
throw new RuntimeException("Deserialization failed", e);
}
}
}
}