0

I'm currently looking into Spring Cloud Function and its possibilities to deploy one function on different cloud environments (AWS Lambda and Azure Functions).

My function looks like this (of course very simplified):

@Component
public class EchoFunction implements Function<String, String> {

    @Override
    public String apply(String m) {
        String message = "Received message: " + m;
        return message;
    }
}

When deploying that on AWS Lambda, it works perfectly (the full project can be found here).

However, if I run the same function as local Azure Functions deployment using the Azure Functions Core Tools, I get the following exception when calling the function:

24.01.19 21:58:50] Caused by: java.lang.ClassCastException: reactor.core.publisher.FluxJust cannot be cast to java.lang.String
[24.01.19 21:58:50]     at de.margul.awstutorials.springcloudfunction.function.EchoFunction.apply(EchoFunction.java:9)
[24.01.19 21:58:50]     at org.springframework.cloud.function.adapter.azure.AzureSpringBootRequestHandler.handleRequest(AzureSpringBootRequestHandler.java:56)
[24.01.19 21:58:50]     at de.margul.awstutorials.springcloudfunction.azure.handler.FunctionHandler.execute(FunctionHandler.java:19)
[24.01.19 21:58:50]     ... 16 more 

For some reason, the function seems to expect a Flux instead of a String. I think this might be related to what [the documentation] (https://cloud.spring.io/spring-cloud-static/spring-cloud-function/2.0.0.RELEASE/single/spring-cloud-function.html#_function_catalog_and_flexible_function_signatures) says about this:

One of the main features of Spring Cloud Function is to adapt and support a range of type signatures for user-defined functions, while providing a consistent execution model. That’s why all user defined functions are transformed into a canonical representation by FunctionCatalog, using primitives defined by the Project Reactor (i.e., Flux and Mono). Users can supply a bean of type Function, for instance, and the FunctionCatalog will wrap it into a Function,Flux>.

So the problem might be related to this:

If I change the function in the following way, it works:

@Component
public class EchoFunction implements Function<String, Flux<String>> {

    @Override
    public Flux<String> apply(String m) {
        String message = "Received message: "+m;
        return Flux.just(message);
    }
}

My function handler looks like this:

public class FunctionHandler extends AzureSpringBootRequestHandler<String, String> {
    @FunctionName("createEntityFunction")
    public String execute(@HttpTrigger(name = "req", methods = {
        HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<String> entity,
        ExecutionContext context) {
        return handleRequest(entity.getBody(), context);
    }

    @Bean
    public EchoFunction createEntityFunction() {
        return new EchoFunction();
    }
}

For the AWS deployment, I had the following dependencies:

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-function-adapter-aws</artifactId>
        <version>2.0.0</version>
    </dependency>
    <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-lambda-java-core</artifactId>
        <version>1.2.0</version>
    </dependency>
    <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-lambda-java-events</artifactId>
        <version>2.2.5</version>
    </dependency>
</dependencies>

For the Azure deployment, I have only one dependency:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-function-adapter-azure</artifactId>
    <version>2.0.0</version>
</dependency>

I've already looked into the source code of both adapters: On AWS, the SpringBootRequestHandler invokes the target function (in line 48).

On Azure, the AzureSpringBootRequestHandler invokes the target function (in line 56).

For me, it looks like in both cases, a Flux is handed over. However, for the AWS adapter, the Object is unwrapped somewhere in between obviously. But this is not the case with the Azure adapter.

Any ideas why?

Oleg Zhurakousky
  • 5,820
  • 16
  • 17
markusgulden
  • 503
  • 1
  • 6
  • 18

1 Answers1

0

@margul Sorry for the late reply/ Without the newly created spring-cloud-function tag your question was kind of lost. I just looked at it and also the issue you opened in GH and it appears to be a bug on our side. Basically it seems like if we can't find function in catalog we fallback on bean factory. The problem with this approach is that bean factory has raw function bean (function not fluxified yet), hence the ClassCast exception.

Anyway, i'll address the rest in GH.

Just to close this off, please see this issue

Oleg Zhurakousky
  • 5,820
  • 16
  • 17