Search code examples
spring-bootspring-cloud-function

Understanding difference between Custom Handler and SpringBootApiGatewayRequestHandler


I'm new to Spring Cloud Function and came across it as one of best solution for developing FaaS based solution. I am specifically writing application for AWS Lambda Service which is back-end of API Gateway. I ran into very interesting problem with My test application and it is around Handler. My test application works well with Custom Handler written as -

public class UserProfileHandler extends SpringBootRequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
}

which works well when configured as Handler in the AWS Lambda. Then I came across org.springframework.cloud.function.adapter.aws.SpringBootApiGatewayRequestHandler which is available in Spring Cloud Function dependency, so I wanted to get rid of UserProfileHandler hence I changed Handler configuration in AWS Lambda to org.springframework.cloud.function.adapter.aws.SpringBootApiGatewayRequestHandler instead of ...UserProfileHandler and now lambda fails with following error message. Has anyone run into this problem?

{
  "errorMessage": "java.util.Optional cannot be cast to com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent",
  "errorType": "java.lang.ClassCastException",
  "stackTrace": [
    "com.transformco.hs.css.userprofile.function.UserProfileFunction.apply(UserProfileFunction.java:16)",
    "org.springframework.cloud.function.context.catalog.BeanFactoryAwareFunctionRegistry$FunctionInvocationWrapper.invokeFunction(BeanFactoryAwareFunctionRegistry.java:499)",
    "org.springframework.cloud.function.context.catalog.BeanFactoryAwareFunctionRegistry$FunctionInvocationWrapper.lambda$doApply$1(BeanFactoryAwareFunctionRegistry.java:543)",
    "reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:107)",
    "reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:121)",
    "reactor.core.publisher.FluxJust$WeakScalarSubscription.request(FluxJust.java:99)",
    "reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:162)",
    "reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:162)",
    "reactor.core.publisher.BlockingIterable$SubscriberIterator.onSubscribe(BlockingIterable.java:218)",
    "reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:90)",
    "reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:90)",
    "reactor.core.publisher.FluxJust.subscribe(FluxJust.java:70)",
    "reactor.core.publisher.InternalFluxOperator.subscribe(InternalFluxOperator.java:53)",
    "reactor.core.publisher.BlockingIterable.iterator(BlockingIterable.java:80)",
    "org.springframework.cloud.function.adapter.aws.SpringBootRequestHandler.result(SpringBootRequestHandler.java:59)",
    "org.springframework.cloud.function.adapter.aws.SpringBootRequestHandler.handleRequest(SpringBootRequestHandler.java:52)",
    "org.springframework.cloud.function.adapter.aws.SpringBootApiGatewayRequestHandler.handleRequest(SpringBootApiGatewayRequestHandler.java:140)",
    "org.springframework.cloud.function.adapter.aws.SpringBootApiGatewayRequestHandler.handleRequest(SpringBootApiGatewayRequestHandler.java:43)"
  ]
}

Solution

  • Ganesh, I believe you have already raise the issue in Github of SCF. So as I stated there, we have recently did several enhancements, polished the sample and modified documentation by adding a Getting Started guide.

    That said, with new generic request handler you no longer need to provide implementation of AWS request handler including SpringBootApiGatewayRequestHandler.

    Simply write your boot application to contain function bean

    @SpringBootApplication
    public class FunctionConfiguration {
    
        public static void main(String[] args) {
            SpringApplication.run(FunctionConfiguration.class, args);
        }
    
        @Bean
        public Function<String, String> uppercase() {
            return value -> value.toUpperCase();
        }
    }
    

    . . . and specify org.springframework.cloud.function.adapter.aws.FunctionInvoker as a handler in AWS dashboard. We'll do the rest for you.