Skip to content

AWS: AWSTypesMessageConverter fails to deserialize JSON array as body for functions that take reactive inputs of POJOs #1052

@ArnauAregall

Description

@ArnauAregall

Describe the bug

Using:

  • Spring Boot 3.1.1
  • Spring Cloud 2022.0.3 (SCF 4.0.3)

Related to org.springframework.cloud:spring-cloud-function-adapter-aws:

When running a Spring Cloud Function application on an AWS Lambda, AWSTypesMessageConverter seems to not be capable to correctly convert a JSON array body when the functions consume a Flux<T>, where T is a regular application domain class/POJO.

The domain class:

public record GeoLocation(Float latitude, Float longitude) {}

The function definition:

@Configuration
public class WeatherFunctions {

    @Bean
    public Function<Flux<GeoLocation>, Flux<Forecast>> forecast(WeatherService weatherService) {
        return weatherService::getForecast;
    }

}

When invoking the AWS Lambda function with a body such as

[
  {
    "latitude": 42.99,
    "longitude": 2.83
  }
]

It fails with the following Jackson conversion error:

2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 2023-06-25T14:07:34.526Z  WARN 9 --- [pool-5-thread-1] s.c.f.c.c.SmartCompositeMessageConverter : Failure during type conversion by org.springframework.cloud.function.adapter.aws.AWSTypesMessageConverter@7e33992. Will try the next converter.
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 java.lang.IllegalStateException: Failed to convert. Possible bug as the conversion probably shouldn't have been attempted here
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at org.springframework.cloud.function.json.JacksonMapper.doFromJson(JacksonMapper.java:65) ~[na:na]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at org.springframework.cloud.function.json.JsonMapper.fromJson(JsonMapper.java:66) ~[aws-lambda-spring-cloud-function-reactive-java:4.0.3]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at org.springframework.cloud.function.adapter.aws.AWSTypesMessageConverter.convertFromInternal(AWSTypesMessageConverter.java:90) ~[aws-lambda-spring-cloud-function-reactive-java:4.0.3]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at org.springframework.messaging.converter.AbstractMessageConverter.fromMessage(AbstractMessageConverter.java:185) ~[aws-lambda-spring-cloud-function-reactive-java:6.0.10]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at org.springframework.messaging.converter.AbstractMessageConverter.fromMessage(AbstractMessageConverter.java:176) ~[aws-lambda-spring-cloud-function-reactive-java:6.0.10]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at org.springframework.cloud.function.context.config.SmartCompositeMessageConverter.fromMessage(SmartCompositeMessageConverter.java:63) ~[na:na]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.convertInputMessageIfNecessary(SimpleFunctionRegistry.java:1353) ~[aws-lambda-spring-cloud-function-reactive-java:4.0.3]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.convertInputIfNecessary(SimpleFunctionRegistry.java:1106) ~[aws-lambda-spring-cloud-function-reactive-java:4.0.3]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.lambda$convertInputPublisherIfNecessary$24(SimpleFunctionRegistry.java:1465) ~[aws-lambda-spring-cloud-function-reactive-java:4.0.3]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:113) ~[na:na]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2545) ~[aws-lambda-spring-cloud-function-reactive-java:3.5.7]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:171) ~[na:na]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:171) ~[na:na]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:171) ~[na:na]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at reactor.core.publisher.FluxFlatMap$FlatMapMain.onSubscribe(FluxFlatMap.java:371) ~[aws-lambda-spring-cloud-function-reactive-java:3.5.7]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:96) ~[na:na]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:96) ~[na:na]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:96) ~[na:na]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at reactor.core.publisher.FluxJust.subscribe(FluxJust.java:68) ~[na:na]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at reactor.core.publisher.InternalFluxOperator.subscribe(InternalFluxOperator.java:62) ~[aws-lambda-spring-cloud-function-reactive-java:3.5.7]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at reactor.core.publisher.BlockingIterable.iterator(BlockingIterable.java:86) ~[na:na]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at org.springframework.cloud.function.adapter.aws.AWSLambdaUtils.generateOutputFromObject(AWSLambdaUtils.java:153) ~[na:na]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at org.springframework.cloud.function.adapter.aws.CustomRuntimeEventLoop.eventLoop(CustomRuntimeEventLoop.java:166) ~[na:na]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at org.springframework.cloud.function.adapter.aws.CustomRuntimeEventLoop.lambda$run$0(CustomRuntimeEventLoop.java:90) ~[na:na]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at java.base@17.0.5/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[aws-lambda-spring-cloud-function-reactive-java:na]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at java.base@17.0.5/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at java.base@17.0.5/java.lang.Thread.run(Thread.java:833) ~[aws-lambda-spring-cloud-function-reactive-java:na]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:775) ~[aws-lambda-spring-cloud-function-reactive-java:na]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at org.graalvm.nativeimage.builder/com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:203) ~[na:na]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `tech.aaregall.lab.function.weather.domain.GeoLocation` from Array value (token `JsonToken.START_ARRAY`)
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at [Source: (String)"[
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 {
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 "latitude": 42.99,
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 "longitude": 2.83
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 }
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 ]"; line: 1, column: 1]
2023/06/25/[$LATEST]2b2c53a6597a485481146cabafd1247e 2023-06-25T14:07:34.527000 at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1752) ~[aws-lambda-spring-cloud-function-reactive-java:2.15.2]

Nevertheless it works perfectly when the application is not running on AWS (locally, or on a Docker Image) being able to take more "geolocations" in the payload, which is the intention.

[
  {
    "latitude": 42.99,
    "longitude": 2.83
  },
  {
    "latitude": 43.01,
    "longitude": 2.21
  }
]

Sample

I built a sample application to demonstrate the issue: https://github.com/ArnauAregall/aws-lambda-spring-cloud-function-reactive-java

If you clone and run the application locally:

$ ./gradlew nativeRun

$ curl -X POST -H "Content-Type: application/json" -d '[{"latitude": 41.34, "longitude": 2.78}]' http://localhost:8080/question | jq > response.json

The response.json file will be a JSON array containing a single object representing the weather forecast for those coordinates.

But when running in AWS:

$ ./aws-image/build-aws-image.sh

$ sam build --use-container --build-image tech.aaregall.lab/amazonlinux-graalvm:latest

$ export AWS_REGION=...

$ sam deploy --region $AWS_REGION

The same command against the lambda URL will result in "Internal Server Error" with the mentioned stacktrace above.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions