Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HandleRequestCollectionHelper for Quarkus Amazon Lambda #40349

Closed
hamburml opened this issue Apr 29, 2024 · 5 comments · Fixed by #40464
Closed

HandleRequestCollectionHelper for Quarkus Amazon Lambda #40349

hamburml opened this issue Apr 29, 2024 · 5 comments · Fixed by #40464
Labels
Milestone

Comments

@hamburml
Copy link
Contributor

hamburml commented Apr 29, 2024

Description

Hello.

We run several quarkus application as AWS Lambdas. Some are triggered by an AWS Api gateway and some by sqs or sns. A queue can collect some messages and send them via batching to a lambda. In JSON this would look like this if the message has a name and description attribute.

[{"name":"apple","description":"an apple a day..."},{"name":"strawberry","description":"red fruit"}]

A RequestHandler would look like this:

public class FruitsLambda implements RequestHandler<List<Fruit>, Void> {

    @Override
    public Void handleRequest(List<Fruit> fruit, Context context) {
        fruit.forEach(this::printName);
        return null;
    }

    private void printName(Fruit fruit) {
        Log.info(fruit.name());
        Log.info(fruit.description());
    }
}

When this runs sadly an exception occurs.

ERROR [io.qua.ama.lam.run.AbstractLambdaPollLoop] (Lambda Thread (TEST)) Failed to run lambda (TEST): java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class de.hamburml.quarkus.aws.lambda.Fruit (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; de.hamburml.quarkus.aws.lambda.Fruit is in unnamed module of loader io.quarkus.bootstrap.classloading.QuarkusClassLoader @27b71f50)

The ObjectMapper cannot infer the correct type and as fallback uses a LinkedHashMap.

This can be solved with a little trick and this is my enhancement idea...

public abstract class HandleRequestCollectionHelper<INPUT_TYPE, OUTPUT_TYPE> implements RequestHandler<Object, OUTPUT_TYPE> {
    @Inject
    ObjectMapper objectMapper;

    @Override
    public OUTPUT_TYPE handleRequest(Object input, Context context) {
        INPUT_TYPE convertedInput = objectMapper.convertValue(input, this.getCollectionReference());
        this.handle(convertedInput, context);
        return null;
    }

    public abstract TypeReference<INPUT_TYPE> getCollectionReference();

    public abstract OUTPUT_TYPE handle(INPUT_TYPE input, Context context);

}
public class FruitsLambda2 extends HandleRequestCollectionHelper<List<Fruit>, Void> {

    @Override
    public TypeReference<List<Fruit>> getCollectionReference() {
        return new TypeReference<>() { };
    }

    @Override
    public Void handle(List<Fruit> input, Context context) {
        input.forEach(this::printName);
        return null;
    }

    private void printName(Fruit fruit) {
        Log.info(fruit.name());
        Log.info(fruit.description());
    }
}

With HandleRequestCollectionHelper the subclass is forced to implement getCollectionReference so that the ObjectMapper is able to convert the LinkedHashMap to the Collection-Type. If this is done inside a HandleRequestHelper this is hidden from the developer. Maybe getCollectionReference can also be saved with some reflection magic because the type should be already there in the INPUT_TYPE parameter.

HandleRequestCollectionHelper could be added to the amazon-lambda lib. Or maybe automatically used if a Collection-Type is used in first parameter of implementation of RequestHandler-Interface.

I have a simple example here https://github.com/hamburml/quarkus-lambda-handler-with-collection Under src/test are two packages, working and notworking.

Let me know what you think of that.

Thanks
Michael

@hamburml hamburml added the kind/enhancement New feature or request label Apr 29, 2024
Copy link

quarkus-bot bot commented Apr 29, 2024

/cc @matejvasek (amazon-lambda), @patriot1burke (amazon-lambda)

@hamburml
Copy link
Contributor Author

@scrocquesel maybe this is also interesting for you

@patriot1burke
Copy link
Contributor

It would be better to change the reader created by the object mapper here:

https://github.com/quarkusio/quarkus/blob/main/extensions/amazon-lambda/runtime/src/main/java/io/quarkus/amazon/lambda/runtime/AmazonLambdaRecorder.java#L59

To use ObjectMapper.readerFor(TypeReference)

@hamburml
Copy link
Contributor Author

@patriot1burke Do you have an idea how this can be done in the linked class?

@patriot1burke
Copy link
Contributor

Yeah,

Type genericType = handlerMethod.getGenericParameterTypes[0];
JavaType javaType = objectMapper.getTypeFactory().constructType(parameterType);
objectReader = new JacksonInputReader(objectMapper.reader(javaType));

I think that will work. Should do the same for the writer too. Also, probably need to handle the case where the handler class is parameterized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants