Summary
📣 We are excited to announce support for Java 25 across all utilities, including compatibility with the new AWS Lambda Java 25 runtime. Read more in the announcement blog post.
We also introduced a new functional approach for all utilities. This pattern is ideal when you want to avoid AspectJ setup or prefer a more imperative style. Key benefits include:
- No AspectJ dependency: Eliminates AspectJ runtime dependency, making your deployment package more lightweight.
- Works with any framework regardless of using vanilla Lambda handlers or not (Quarkus, Micronaut, Spring Cloud Function).
- 100% feature parity with annotation-based approach.
- 100% Java 25 compatible.
📈 New: Functional Approach
Idempotency
You can now make any function idempotent without AspectJ using the new functional API. This approach provides explicit control over idempotency behavior.
Basic usage:
public class App implements RequestHandler<Subscription, SubscriptionResult> {
public App() {
Idempotency.config().withPersistenceStore(
DynamoDBPersistenceStore.builder()
.withTableName(System.getenv("TABLE_NAME"))
.build()
).configure();
}
public SubscriptionResult handleRequest(Subscription event, Context context) {
Idempotency.registerLambdaContext(context);
return Idempotency.makeIdempotent(this::processEvent, event, SubscriptionResult.class);
}
private SubscriptionResult processEvent(Subscription event) {
// Your business logic here
return new SubscriptionResult(payment.getId(), "success", 200);
}
}✨ New feature: Generic return types support
The functional API now supports making methods with generic return types idempotent using Jackson's TypeReference. This was not possible with the @Idempotent annotation due to type erasure.
public Map<String, Basket> handleRequest(Product input, Context context) {
Idempotency.registerLambdaContext(context);
return Idempotency.makeIdempotent(
this::processProduct,
input,
new TypeReference<Map<String, Basket>>() {}
);
}
private Map<String, Basket> processProduct(Product product) {
// business logic returning generic type
}Logging
The new functional API for Logging provides a way to add structured logging to your Lambda functions without AspectJ configuration. It automatically manages logging context and state cleanup.
Basic usage:
public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
private static final Logger log = LoggerFactory.getLogger(App.class);
public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) {
return PowertoolsLogging.withLogging(context, () -> {
log.info("Processing request");
return new APIGatewayProxyResponseEvent().withStatusCode(200);
});
}
}With correlation ID and sampling:
public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) {
return PowertoolsLogging.withLogging(
context,
0.5, // 50% sampling rate
CorrelationIdPaths.API_GATEWAY_REST,
input,
() -> {
log.info("Processing payment");
return new APIGatewayProxyResponseEvent().withStatusCode(200);
}
);
}Large Messages
The Large Messages utility now supports a functional API for processing SQS and SNS messages with payloads offloaded to S3. This approach is thread-safe and works seamlessly with the Batch utility.
public class SqsBatchHandler implements RequestHandler<SQSEvent, SQSBatchResponse> {
private final BatchMessageHandler<SQSEvent, SQSBatchResponse> handler;
public SqsBatchHandler() {
// Compatible with the Batch utility
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
}
}Changes
- chore: bump commons-io:commons-io from 2.20.0 to 2.21.0 (#2288) by @dependabot[bot]
- chore: bump aws.sdk.version from 2.38.3 to 2.38.5 (#2287) by @dependabot[bot]
- improv(parameters): Make parameters utility thread-safe (#2284) by @phipag
- chore(ci): bump version to 2.7.0 (#2286) by @github-actions[bot]
- chore(ci): Enable Java 25 builds. (#2285) by @phipag
- chore: bump actions/dependency-review-action from 4.8.1 to 4.8.2 (#2281) by @dependabot[bot]
- chore: bump squidfunk/mkdocs-material from
58dee36to980e11fin /docs (#2280) by @dependabot[bot] - chore: bump software.amazon.awscdk:aws-cdk-lib from 2.222.0 to 2.223.0 (#2279) by @dependabot[bot]
- chore: bump aws.sdk.version from 2.37.5 to 2.38.3 (#2278) by @dependabot[bot]
- chore: bump software.constructs:constructs from 10.4.2 to 10.4.3 (#2277) by @dependabot[bot]
- chore: bump aws.sdk.version from 2.37.5 to 2.38.2 (#2276) by @dependabot[bot]
- chore: bump sam/build-java21 from
72709a0to51709aein /powertools-e2e-tests/src/test/resources/docker (#2275) by @dependabot[bot] - chore: bump software.amazon.awscdk:aws-cdk-lib from 2.221.1 to 2.222.0 (#2274) by @dependabot[bot]
- chore: bump aws.sdk.version from 2.37.2 to 2.37.5 (#2273) by @dependabot[bot]
- chore: bump aws.sdk.version from 2.37.4 to 2.37.5 (#2271) by @dependabot[bot]
- chore: bump com.amazonaws:aws-lambda-java-runtime-interface-client from 2.8.6 to 2.8.7 (#2272) by @dependabot[bot]
- chore(ci): bump version to 2.6.0 (#2270) by @github-actions[bot]