aws / aws-lambda-java-libs

Official mirror for interface definitions and helper classes for Java code running on the AWS Lambda platform.

Home Page:https://aws.amazon.com/lambda/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Jackson deserializer is not case insensitive for SQSEvent in aws-lambda-java-runtime-interface-client and provided GraalVM runtime

kiritsuku opened this issue · comments

commented

A Java based lambda function that uses the runtime provided.al2, which was built by GraalVM, can't correctly deserialize all possible event types. In my case the deserialization crashed for SQSEvent. The event looks like this:

{
    "Records": [
        {
            "messageId": "83e95a89-bebc-4bbd-a209-181f6ab1110f",
            "receiptHandle": "...",
            "body": "...",
            "attributes": {
                "ApproximateReceiveCount": "1",
                "SentTimestamp": "1656267257645",
                "SenderId": "AIDAISDDSWNBEXIA6J64K",
                "ApproximateFirstReceiveTimestamp": "1656267257647"
            },
            "messageAttributes": {},
            "md5OfBody": "3154606fb27f5ffe194b5b97e3db3455",
            "eventSource": "aws:sqs",
            "eventSourceARN": "...",
            "awsRegion": "..."
        }
    ]
}

The field Records starts with an upper case letter, which is not an issue if the lambda is deployed through the provided runtime java11 but once the dependency com.amazonaws:aws-lambda-java-runtime-interface-client:2.1.1 is added and the lambda is compiled with GraalVM a different Jackson instance seems to be used that doesn't make use of the configuration mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true); Therefore this code does not work:

public abstract class SQSBatchLambda implements RequestHandler<SQSEvent, SQSBatchResponse> {

    public SQSBatchResponse handleRequest(final SQSEvent event, final Context context) {
        return handleSQSMessageBatch(event, context.getLogger());
    }

    private SQSBatchResponse handleSQSMessageBatch(final SQSEvent event, final LambdaLogger log) {
        ...
    }
}

However, if I do the deserialization manually, everything works fine:

public abstract class SQSBatchLambda implements RequestStreamHandler {
    private final JsonMapper mapper = JsonMapper.builder().configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true).build();

    @Override
    public void handleRequest(final InputStream input, final OutputStream output, final Context context) throws IOException {
        var event = mapper.readValue(input, SQSEvent.class);
        var resp = handleSQSMessageBatch(event, context.getLogger());
        output.write(mapper.writeValueAsString(resp).getBytes(StandardCharsets.UTF_8));
        output.close();
    }

    private SQSBatchResponse handleSQSMessageBatch(final SQSEvent event, final LambdaLogger log) {
        ...
    }
}

Do you have a public repo which reproduces the problem? It would be helpful to see how your application is configured.

commented

I created a public repo which can reproduce the problem: https://github.com/kiritsuku/serverless-graalvm/tree/6979e59fa8da56226029e93155bb32f875702c60

In the given setup the code crashes with a NPE because SQSEvent is not deserialized correctly (in lambda/src/main/java/pkg/GraalLambda.java).

As far as I can tell, GraalVM has to become aware of Jackson mixins in reflect-config.json. I was able to reproduce this issue in a minimal sample and got it fixed by adding SQSEventMixin to reflect-config.json:

https://github.com/maciej-scratches/aws-lambda-libs-gh-351/blob/master/src/main/resources/META-INF/native-image/com.amazonaws/aws-lambda-java-runtime-interface-client/reflect-config.json#L35

@kiritsuku does this solve your issue?

commented

Yes, this resolved the issue. My solution (providing my own deserialization code) is a little bit more flexible to use because the discussed solution here would require that users know how the provided Jackson instance is created.

The most comfortable solution would be that the library aws-lambda-java-runtime-interface-client already ships with all of the required configuration entries for GraalVM.

Please follow #272 and 👍🏻 it.