Skip to content

v2.7.0

Latest

Choose a tag to compare

@github-actions github-actions released this 17 Nov 08:42
· 10 commits to refs/heads/main since this release
c2763c5

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

Docs

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

Docs

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

Docs

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

📜 Documentation updates

This release was made possible by the following contributors:

@phipag