129. Function Catalog and Flexible Function Signatures

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<T> and Mono<T>). Users can supply a bean of type Function<String,String>, for instance, and the FunctionCatalog will wrap it into a Function<Flux<String>,Flux<String>>.

Using Reactor based primitives not only helps with the canonical representation of user defined functions, but it also facilitates a more robust and flexible(reactive) execution model.

While users don’t normally have to care about the FunctionCatalog at all, it is useful to know what kind of functions are supported in user code.

129.1 Java 8 function support

Generally speaking users can expect that if they write a function for a plain old Java type (or primitive wrapper), then the function catalog will wrap it to a Flux of the same type. If the user writes a function using Message (from spring-messaging) it will receive and transmit headers from any adapter that supports key-value metadata (e.g. HTTP headers). Here are the details.

User FunctionCatalog Registration 

Function<S,T>

Function<Flux<S>, Flux<T>>

 

Function<Message<S>,Message<T>>

Function<Flux<Message<S>>, Flux<Message<T>>>

 

Function<Flux<S>, Flux<T>>

Function<Flux<S>, Flux<T>> (pass through)

 

Supplier<T>

Supplier<Flux<T>>

 

Supplier<Flux<T>>

Supplier<Flux<T>>

 

Consumer<T>

Function<Flux<T>, Mono<Void>>

 

Consumer<Message<T>>

Function<Flux<Message<T>>, Mono<Void>>

 

Consumer<Flux<T>>

Consumer<Flux<T>>

 

Consumer is a little bit special because it has a void return type, which implies blocking, at least potentially. Most likely you will not need to write Consumer<Flux<?>>, but if you do need to do that, remember to subscribe to the input flux. If you declare a Consumer of a non publisher type (which is normal), it will be converted to a function that returns a publisher, so that it can be subscribed to in a controlled way.

129.2 Kotlin Lambda support

We also provide support for Kotlin lambdas (since v2.0). Consider the following:

@Bean
open fun kotlinSupplier(): () -> String {
    return  { "Hello from Kotlin" }
}

@Bean
open fun kotlinFunction(): (String) -> String {
    return  { it.toUpperCase() }
}

@Bean
open fun kotlinConsumer(): (String) -> Unit {
    return  { println(it) }
}

The above represents Kotlin lambdas configured as Spring beans. The signature of each maps to a Java equivalent of Supplier, Function and Consumer, and thus supported/recognized signatures by the framework. While mechanics of Kotlin-to-Java mapping are outside of the scope of this documentation, it is important to understand that the same rules for signature transformation outlined in "Java 8 function support" section are applied here as well.

To enable Kotlin support all you need is to add spring-cloud-function-kotlin module to your classpath which contains the appropriate autoconfiguration and supporting classes.