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?