aws / aws/aws-lambda-java-libs

Custom Serialization feature for handler class that implements RequestHandler and uses method name handler syntax does not use exact types during serialization

Đang mở
#552 0 bình luận 2 reaction 0 người được giao Xem trên GitHub
Ngôn ngữ chính
Java
Star
548
Fork
241
Merge trung bình
2 ngày 5 phút
Pull request đã merge (30 ngày)
11

Mô tả

The [custom serialization](https://docs.aws.amazon.com/lambda/latest/dg/java-custom-serialization.html) feature does not use the exact types of type parameters in the presence of inheritance when (a) the implementation class implements `RequestHandler`; and (b) the configured handler uses method name syntax (as opposed to class name syntax, so, `com.example.ExampleLambdaFunction#requestHandler`).

For example, consider the below Lambda implementation with the given custom serializer. Note that the `ExampleLambdaFunction` is the actual handler class, and it extends `LambdaFunctionBase`, which implements `RequestHandler` and uses generic type parameters for the `handleRequest` method parameters and result.

package com.example;

public abstract class LambdaFunctionBase
implements RequestHandler {
@Override
public OutputT handleRequest(InputT input, Context context) {
System.out.println("Enter handleRequest");
OutputT result = doHandleRequest(input, context);
System.out.println("Exit handleRequest");
return result;
}

protected abstract OutputT doHandleRequest(InputT input, Context context);
}

public class ExampleLambdaFunction
extends LambdaFunctionBase {
@Override
public ExampleResponse doHandleRequest(ExampleRequest request) {
ExampleResponse result;
result = businessLogic(request);
return result;
}
}

public class ExampleCustomPojoSerializer
implements CustomPojoSerializer {
@Override
public T fromJson(InputStream input, Type type) {
System.err.println("fromJson(" + type + ")");
return deserializeImplementation(input, type);
}

@Override
public T fromJson(String input, Type type) {
System.err.println("fromJson(" + type + ")");
return deserializeImplementation(input, type);
}

@Override
public void toJson(T value, OutputStream output, Type type) {
System.err.println("toJson(" + type + ")");
return serializeImplementation(value, output, type);
}
}

If the configured handler looks like `com.example.ExampleLambdaFunction`, then the result of creating, deploying, and invoking the lambda would produce the following output on stderr:

fromJson(ExampleRequest)
toJson(ExampleResponse)

So everything works as expected.

However, if the configured handler looks like `com.example.ExampleLambdaFunction::handleRequest`, then the result is different:

fromJson(InputT)
toJson(OutputT) // <-- Assuming serialization succeeded, and the lambda completed successfully

Having poked around in the code a bit, it looks like the difference is due to code in aws-lambda-java-runtime-interface-client. If we look at [`EventHandler#getHandlerFromOverload(Class,Method)`](https://github.com/aws/aws-lambda-java-libs/blob/150b1b73bfad950cce9948ff836d80d80aa3dc1f/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/EventHandlerLoader.java#L415), we see that it creates the `LambdaRequestHandler` from the literal return and parameter types of the given method. However, we know that in this example, those are going to be generic type parameters. It needs to use [the exact return type of the method](https://github.com/leangen/geantyref?tab=readme-ov-file#getting-the-exact-return-type-of-a-method) and [the exact parameter types of the method](https://github.com/leangen/geantyref?tab=readme-ov-file#getting-the-exact-types-of-method-parameters) (examples linked from the excellent [https://github.com/leangen/geantyref](geantyref) library, purely for illustration and reference).

The call stack looks like this when the error occurs:

EventHandlerLoader::loadEventHandler // <-- Branch in this method is why handler syntax matters
EventHandlerLoader::loadEventPojoHandler
EventHandlerLoader::getHandlerFromOverload // <-- Error is here

The feature works as expected when the handler looks like `com.example.ExampleLambdaFunction` because in [`EventHandlerLoader#loadEventHandler`](https://github.com/aws/aws-lambda-java-libs/blob/150b1b73bfad950cce9948ff836d80d80aa3dc1f/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/EventHandlerLoader.java#L336), control flows through [`EventHandler.loadStreamingRequestHandler`](https://github.com/aws/aws-lambda-java-libs/blob/150b1b73bfad950cce9948ff836d80d80aa3dc1f/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/EventHandlerLoader.java#L325), which calls through methods to handle generic types, i.e., [`EventHandlerLoader#wrapPojoHandler`](https://github.com/aws/aws-lambda-java-libs/blob/150b1b73bfad950cce9948ff836d80d80aa3dc1f/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/EventHandlerLoader.java#L522).

The feature does not work as expected when the handler looks like `com.example.ExampleLambdaFunction::handleRequest` because in [`EventHandlerLoader#loadEventHandler`](https://github.com/aws/aws-lambda-java-libs/blob/150b1b73bfad950cce9948ff836d80d80aa3dc1f/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/EventHandlerLoader.java#L336), control flows through [`EventHandler.loadEventPojoHandler`](https://github.com/aws/aws-lambda-java-libs/blob/150b1b73bfad950cce9948ff836d80d80aa3dc1f/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/EventHandlerLoader.java#L452), which uses the literal types of the indicated method, as noted above.

It's not clear whether or not this is a "bug", per se. Based on the code, it seems like the intent is that developers use `com.example.ExampleLambdaFunction` syntax when their implementation inherits from `RequestHandler`, and `com.example.ExampleLambdaFunction::methodName` when it doesn't. On the one hand, method name syntax should always work as expected. On the other hand, if the user uses method name syntax for a class that inherits from RequestHandler, it's "belt and suspenders" at best, and potentially confusing at worst.

In any case, I lost some time debugging my custom serializer due to this issue, so I thought I'd write it up. Fortunately, there should be a pretty simple fix, if the team agrees that this is a bug. (I'd be happy to contribute a patch!) Otherwise, a line in the documentation like "If your class implements RequestHandler, then use `com.example.ExampleLambdaFunction` syntax; otherwise, use `com.example.ExampleLambdaFunction::methodName` syntax. If your RequestHandler implementation uses method name syntax, you might get unexpected behavior in some cases." might be useful to others. (Please ignore if that documentation already exists! I looked, but didn't find it.)

Please let me know if you have any questions, or if I can help in any way!

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Hướng nghiên cứu

Bắt đầu tại aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/EventHandlerLoader.java, lần theo loadEventHandler qua loadEventPojoHandler đến getHandlerFromOverload. So sánh đường đi này với loadStreamingRequestHandler và wrapPojoHandler, sau đó xác minh rằng cú pháp tên phương thức ánh xạ các tham số generic được kế thừa thành ExampleRequest và ExampleResponse cho quá trình tuần tự hóa tùy chỉnh.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
aws, java
Lĩnh vực
api, backend
Loại issue
Lỗi
Độ khó
3/5
Thời gian dự kiến
1-2 ngày
Mức độ hoạt động
Đình trệ
Độ rõ ràng
Đặc tả rõ ràng
Mức phù hợp với người mới
52/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.