= Spring Cloud Function

Mark Fisher, Dave Syer, Oleg Zhurakousky, Anshul Mehra

*{spring-cloud-function-version}*

---

:github: https://github.com/spring-cloud/spring-cloud-function
:githubmaster: {github}/tree/master
:docslink: {githubmaster}/docs/src/main/asciidoc
:nofooter:
:branch: master

== Introduction

include::_intro.adoc[]

include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/contributing-docs.adoc[]

== Getting Started

include::getting-started.adoc[]

== Programming model


=== 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`.

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.

It is also important to understand that Spring Cloud Function provides first class support for reactive API
provided by https://projectreactor.io/[Project Reactor] allowing reactive primitives such as `Mono` and `Flux`
to be used as types in user defined functions providing greater flexibility when choosing programming model for
your function implementation.
Reactive programming model also enables functional support for features that would be otherwise difficult to impossible to implement
using imperative programming style. For more on this please read <<Function Arity>> section.

=== Java 8 function support

Spring Cloud Function embraces and builds on top of the 3 core functional interfaces defined by Java
and available to us since Java 8.

- Supplier<O>
- Function<I, O>
- Consumer<I>

==== Supplier
Supplier can be _reactive_ - `Supplier<Flux<T>>`
or _imperative_ - `Supplier<T>`. From the invocation standpoint this should make no difference
to the implementor of such Supplier. However, when used within frameworks
(e.g., https://spring.io/projects/spring-cloud-stream[Spring Cloud Stream]), Suppliers, especially reactive,
often used to represent the source of the stream, therefore they are invoked once to get the stream (e.g., Flux)
to which consumers can subscribe to. In other words such suppliers represent an equivalent of an _infinite stream_.
However, the same reactive suppliers can also represent _finite_ stream(s) (e.g., result set on the polled JDBC data).
In those cases such reactive suppliers must be hooked up to some polling mechanism of the underlying framework.

To assist with that Spring Cloud Function provides a marker annotation
`org.springframework.cloud.function.context.PollableSupplier` to signal that such supplier produces a
finite stream and may need to be polled again. That said, it is important to understand that Spring Cloud Function itself
provides no behavior for this annotation.

In addition `PollableSupplier` annotation exposes a _splittable_ attribute to signal that produced stream
needs to be split (see https://www.enterpriseintegrationpatterns.com/patterns/messaging/Sequencer.html[Splitter EIP])

Here is the example:

[source, java]
----
@PollableSupplier(splittable = true)
public Supplier<Flux<String>> someSupplier() {
	return () -> {
		String v1 = String.valueOf(System.nanoTime());
		String v2 = String.valueOf(System.nanoTime());
		String v3 = String.valueOf(System.nanoTime());
		return Flux.just(v1, v2, v3);
	};
}
----

==== Function
Function can also be written in imperative or reactive way, yet unlike Supplier and Consumer there are
no special considerations for the implementor other then understanding that when used within frameworks
such as https://spring.io/projects/spring-cloud-stream[Spring Cloud Stream] and others, reactive function is
invoked only once to pass a reference to the stream (Flux or Mono) and imperative is invoked once per event.

==== Consumer
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. 

=== Function Composition

Function Composition is a feature that allows one to compose several functions into one.
The core support is based on function composition feature available with https://docs.oracle.com/javase/8/docs/api/java/util/function/Function.html#andThen-java.util.function.Function-[Function.andThen(..)]
support available since Java 8. However on top of it, we provide few additional features.

==== Declarative Function Composition

This feature allows you to provide composition instruction in a declarative way using `|` (pipe) or `,` (comma) delimiter
when providing `spring.cloud.function.definition` property.

For example
----
--spring.cloud.function.definition=uppercase|reverse
----
Here we effectively provided a definition of a single function which itself is a composition of
function `uppercase` and function `reverse`. In fact that is one of the reasons why the property name is _definition_ and not _name_,
since the definition of a function can be a composition of several named functions.
And as mentioned you can use `,` instead of pipe (such as `...definition=uppercase,reverse`).

==== Composing non-Functions
Spring Cloud Function also supports composing Supplier with `Consumer` or `Function` as well as `Function` with `Consumer`.
What's important here is to understand the end product of such definitions.
Composing Supplier with Function still results in Supplier while composing Supplier with Consumer will effectively render Runnable.
Following the same logic composing Function with Consumer will result in Consumer.

And of course you can't compose uncomposable such as Consumer and Function, Consumer and Supplier etc.


=== Function Routing

Since version 2.2 Spring Cloud Function provides routing feature allowing
you to invoke a single function which acts as a router to an actual function you wish to invoke
This feature is very useful in certain FAAS environments where maintaining configurations
for several functions could be cumbersome or exposing more then one function is not possible.

The `RoutingFunction` is registered in _FunctionCatalog_  under the name `functionRouter`. For simplicity
and consistency you can also refer to `RoutingFunction.FUNCTION_NAME` constant.

This function has the following signature:

[source, java]
----
public class RoutingFunction implements Function<Object, Object> {
. . .
}
----
The routing instructions could be communicated in several ways;

*Message Headers*

If the input argument is of type `Message<?>`, you can communicate routing instruction by setting one of
`spring.cloud.function.definition` or `spring.cloud.function.routing-expression` Message headers.
For more static cases you can use `spring.cloud.function.definition` header which allows you to provide
the name of a single function (e.g., `...definition=foo`) or a composition instruction (e.g., `...definition=foo|bar|baz`).
For more dynamic cases you can use `spring.cloud.function.routing-expression` header which allows
you to use Spring Expression Language (SpEL) and provide SpEL expression that should resolve
into definition of a function (as described above).

NOTE: SpEL evaluation context's root object is the
actual input argument, so in he case of `Message<?>` you can construct expression that has access
to both `payload` and `headers` (e.g., `spring.cloud.function.routing-expression=headers.function_name`).

In specific execution environments/models the adapters are responsible to translate and communicate
`spring.cloud.function.definition` and/or `spring.cloud.function.routing-expression` via Message header.
For example, when using _spring-cloud-function-web_ you can provide `spring.cloud.function.definition` as an HTTP
header and the framework will propagate it as well as other HTTP headers as Message headers.

*Application Properties*

Routing instruction can also be communicated via `spring.cloud.function.definition`
or `spring.cloud.function.routing-expression` as application properties. The rules described in the
previous section apply here as well. The only difference is you provide these instructions as
application properties (e.g., `--spring.cloud.function.definition=foo`).

IMPORTANT: When dealing with reactive inputs (e.g., Publisher), routing instructions must only be provided via Function properties. This is
due to the nature of the reactive functions which are invoked only once to pass a Publisher and the rest
is handled by the reactor, hence we can not access and/or rely on the routing instructions communicated via individual
values (e.g., Message).

=== Function Arity

There are times when a stream of data needs to be categorized and organized. For example,
consider a classic big-data use case of dealing with unorganized data containing, let’s say,
‘orders’ and ‘invoices’, and you want each to go into a separate data store.
This is where function arity (functions with multiple inputs and outputs) support
comes to play.

Let’s look at an example of such a function (full implementation details are available
https://github.com/spring-cloud/spring-cloud-stream/blob/master/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/function/MultipleInputOutputFunctionTests.java#L342[here]),

[source, java]
----
@Bean
public Function<Flux<Integer>, Tuple2<Flux<String>, Flux<String>>> organise() {
	return flux -> ...;
}
----

Given that Project Reactor is a core dependency of SCF, we are using its Tuple library.
Tuples give us a unique advantage by communicating to us both _cardinality_ and _type_ information.
Both are extremely important in the context of SCSt. Cardinality lets us know
how many input and output bindings need to be created and bound to the corresponding
inputs and outputs of a function. Awareness of the type information ensures proper type
conversion.

Also, this is where the ‘index’ part of the naming convention for binding
names comes into play, since, in this function, the two output binding
names are `organise-out-0` and `organise-out-1`.

IMPORTANT: IMPORTANT: At the moment, function arity is *only* supported for reactive functions
(`Function<TupleN<Flux<?>...>, TupleN<Flux<?>...>>`) centered on Complex event processing
where evaluation and computation on confluence of events typically requires view into a
stream of events rather than single event.

=== Kotlin Lambda support

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

[source, java]
----
@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.

=== Function Component Scan

Spring Cloud Function will scan for implementations of `Function`,
`Consumer` and `Supplier` in a package called `functions` if it
exists. Using this feature you can write functions that have no
dependencies on Spring - not even the `@Component` annotation is
needed. If you want to use a different package, you can set
`spring.cloud.function.scan.packages`. You can also use
`spring.cloud.function.scan.enabled=false` to switch off the scan
completely.

== Standalone Web Applications

Functions could be automatically exported as HTTP endpoints.

The `spring-cloud-function-web` module has autoconfiguration that
activates when it is included in a Spring Boot web application (with
MVC support). There is also a `spring-cloud-starter-function-web` to
collect all the optional dependencies in case you just want a simple
getting started experience.

With the web configurations activated your app will have an MVC
endpoint (on "/" by default, but configurable with
`spring.cloud.function.web.path`) that can be used to access the
functions in the application context where function name becomes part of the URL path. The supported content types are
plain text and JSON.

|===
| Method | Path | Request | Response | Status

| GET    | /{supplier} | - | Items from the named supplier              | 200 OK
| POST   | /{consumer} | JSON object or text | Mirrors input and pushes request body into consumer           | 202 Accepted
| POST   | /{consumer} | JSON array or text with new lines | Mirrors input and pushes body into consumer one by one | 202 Accepted
| POST   | /{function} | JSON object or text | The result of applying the named function | 200 OK
| POST   | /{function} | JSON array or text with new lines | The result of applying the named function | 200 OK
| GET    | /{function}/{item} | - | Convert the item into an object and return the result of applying the function | 200 OK

|===

As the table above shows the behaviour of the endpoint depends on the method and also the type of incoming request data. When the incoming data 
is single valued, and the target function is declared as obviously single valued (i.e. not returning a collection or `Flux`), then the response 
will also contain a single value.
For multi-valued responses the client can ask for a server-sent event stream by sending `Accept: text/event-stream".

Functions and consumers that are declared with input and output in `Message<?>` will see the request headers on the input messages, and the output message headers will be converted to HTTP headers.

When POSTing text the response format might be different with Spring Boot 2.0 and older versions, depending on the content negotiation (provide content type and accept headers for the best results).

See <<Testing Functional Applications>> to see the details and example on how to test such application.


=== Function Mapping rules 

If there is only a single function (consumer etc.) in the catalog, the name in the path is optional.
In other words, providing you only have `uppercase` function in catalog 
`curl -H "Content-Type: text/plain" localhost:8080/uppercase -d hello` and `curl -H "Content-Type: text/plain" localhost:8080/ -d hello` calls are identical.

Composite functions can be addressed using pipes or commas to separate function names (pipes are legal in URL paths, but a bit awkward to type on the command line).
For example,  `curl -H "Content-Type: text/plain" localhost:8080/uppercase,reverse -d hello`.

For cases where there is more then a single function in catalog, each function will be exported and mapped with function name being 
part of the path (e.g., `localhost:8080/uppercase`). 
In this scenario you can still map specific function or function composition to the root path by providing
`spring.cloud.function.definition` property 

For example,
----
--spring.cloud.function.definition=foo|bar
----

The above property will compose 'foo' and 'bar' function and map the composed function to the "/" path.

=== Function Filtering rules 

In situations where there are more then one function in catalog there may be a need to only export certain functions or function compositions. In that case you can use
the same `spring.cloud.function.definition` property listing functions you intend to export delimited by `;`.
Note that in this case nothing will be mapped to the root path and functions that are not listed (including compositions) are not going to be exported

For example,
----
--spring.cloud.function.definition=foo;bar
----

This will only export function `foo` and function `bar` regardless how many functions are available in catalog (e.g., `localhost:8080/foo`). 

----
--spring.cloud.function.definition=foo|bar;baz
----

This will only export function composition `foo|bar` and function `baz` regardless how many functions are available in catalog (e.g., `localhost:8080/foo,bar`). 


== Standalone Streaming Applications

To send or receive messages from a broker (such as RabbitMQ or Kafka) you can leverage `spring-cloud-stream` project and it's integration with Spring Cloud Function.
Please refer to https://cloud.spring.io/spring-cloud-static/spring-cloud-stream/current/reference/html/spring-cloud-stream.html#spring_cloud_function[Spring Cloud Function] section of the Spring Cloud Stream reference manual for more details and examples.

== Deploying a Packaged Function

Spring Cloud Function provides a "deployer" library that allows you to launch a jar file (or exploded archive, or set of jar files) with an isolated class loader and expose the functions defined in it. This is quite a powerful tool that would allow you to, for instance, adapt a function to a range of different input-output adapters without changing the target jar file. Serverless platforms often have this kind of feature built in, so you could see it as a building block for a function invoker in such a platform (indeed the https://projectriff.io[Riff] Java function invoker uses this library).

The standard entry point is to add `spring-cloud-function-deployer` to the classpath, the deployer kicks in and looks for some configuration to tell it where to find the function jar.

```xml
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-function-deployer</artifactId>
	<version>${spring.cloud.function.version}</version>
</dependency>
```


At a minimum the user has to provide a `spring.cloud.function.location` which is a URL or resource location for the archive containing the functions. It can optionally use a `maven:` prefix to locate the artifact via a dependency lookup (see `FunctionProperties` for complete details). A Spring Boot application is bootstrapped from the jar file, using the `MANIFEST.MF` to locate a start class, so that a standard Spring Boot fat jar works well, for example. If the target jar can be launched successfully then the result is a function registered in the main application's `FunctionCatalog`. The registered function can be applied by code in the main application, even though it was created in an isolated class loader (by deault).

Here is the example of deploying a JAR which contains an 'uppercase' function and invoking it .

```java
@SpringBootApplication
public class DeployFunctionDemo {

	public static void main(String[] args) {
		ApplicationContext context = SpringApplication.run(DeployFunctionDemo.class,
				"--spring.cloud.function.location=..../target/uppercase-0.0.1-SNAPSHOT.jar",
				"--spring.cloud.function.definition=uppercase");

		FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
		Function<String, String> function = catalog.lookup("uppercase");
		System.out.println(function.apply("hello"));
	}
}
```

=== Supported Packaging Scenarios 

Currently Spring Cloud Function supports several packaging scenarios to give you the most flexibility when it comes to deploying functions.

==== Simple JAR

This packaging option implies no dependency on anything related to Spring.
For example; Consider that such JAR contains the following class:
```java
package function.example;
. . .
public class UpperCaseFunction implements Function<String, String> {
	@Override
	public String apply(String value) {
		return value.toUpperCase();
	}
}
```
All you need to do is specify `location` and `function-class` properties when deploying such package:

```
--spring.cloud.function.location=target/it/simplestjar/target/simplestjar-1.0.0.RELEASE.jar
--spring.cloud.function.function-class=function.example.UpperCaseFunction
```

It's conceivable in some cases that you might want to package multiple functions together. For such scenarios you can use 
`spring.cloud.function.function-class` property to list several classes delimiting them by `;`.

For example,

```
--spring.cloud.function.function-class=function.example.UpperCaseFunction;function.example.ReverseFunction
```

Here we are identifying two functions to deploy, which we can now access in function catalog by name (e.g., `catalog.lookup("reverseFunction");`).


For more details please reference the complete sample available https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-deployer/src/it/simplestjar[here].
You can also find a corresponding test in https://github.com/spring-cloud/spring-cloud-function/blob/master/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/FunctionDeployerTests.java#L70[FunctionDeployerTests].

==== Spring Boot JAR

This packaging option implies there is a dependency on Spring Boot and that the JAR was generated as Spring Boot JAR. That said, given that the deployed JAR 
runs in the isolated class loader, there will not be any version conflict with the Spring Boot version used by the actual deployer.
For example; Consider that such JAR contains the following class (which could have some additional Spring dependencies providing Spring/Spring Boot is on the classpath):
```java
package function.example;
. . .
public class UpperCaseFunction implements Function<String, String> {
	@Override
	public String apply(String value) {
		return value.toUpperCase();
	}
}
```
As before all you need to do is specify `location` and `function-class` properties when deploying such package:

```
--spring.cloud.function.location=target/it/simplestjar/target/simplestjar-1.0.0.RELEASE.jar
--spring.cloud.function.function-class=function.example.UpperCaseFunction
```
For more details please reference the complete sample available https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-deployer/src/it/bootjar[here].
You can also find a corresponding test in https://github.com/spring-cloud/spring-cloud-function/blob/master/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/FunctionDeployerTests.java#L50[FunctionDeployerTests].

==== Spring Boot Application

This packaging option implies your JAR is complete stand alone Spring Boot application with functions as managed Spring beans.
As before there is an obvious assumption that there is a dependency on Spring Boot and that the JAR was generated as Spring Boot JAR. That said, given that the deployed JAR 
runs in the isolated class loader, there will not be any version conflict with the Spring Boot version used by the actual deployer.
For example; Consider that such JAR contains the following class:
```java
package function.example;
. . .
@SpringBootApplication
public class SimpleFunctionAppApplication {

	public static void main(String[] args) {
		SpringApplication.run(SimpleFunctionAppApplication.class, args);
	}

	@Bean
	public Function<String, String> uppercase() {
		return value -> value.toUpperCase();
	}
}
```
Given that we're effectively dealing with another Spring Application context and that functions are spring managed beans,
in addition to the `location` property we also specify `definition` property instead of `function-class`.

```
--spring.cloud.function.location=target/it/bootapp/target/bootapp-1.0.0.RELEASE-exec.jar
--spring.cloud.function.definition=uppercase
```
For more details please reference the complete sample available https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-deployer/src/it/bootapp[here].
You can also find a corresponding test in https://github.com/spring-cloud/spring-cloud-function/blob/master/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/FunctionDeployerTests.java#L164[FunctionDeployerTests].

NOTE: This particular deployment option may or may not have Spring Cloud Function on it's classpath. From the deployer perspective this doesn't matter. 

== Functional Bean Definitions

include::functional.adoc[leveloffset=+1]

== Dynamic Compilation

There is a sample app that uses the function compiler to create a
function from a configuration property. The vanilla "function-sample"
also has that feature. And there are some scripts that you can run to
see the compilation happening at run time. To run these examples,
change into the `scripts` directory:

----
cd scripts
----

Also, start a RabbitMQ server locally (e.g. execute `rabbitmq-server`).

Start the Function Registry Service:

----
./function-registry.sh
----

Register a Function:

----
./registerFunction.sh -n uppercase -f "f->f.map(s->s.toString().toUpperCase())"
----

Run a REST Microservice using that Function:

----
./web.sh -f uppercase -p 9000
curl -H "Content-Type: text/plain" -H "Accept: text/plain" localhost:9000/uppercase -d foo
----

Register a Supplier:

----
./registerSupplier.sh -n words -f "()->Flux.just(\"foo\",\"bar\")"
----

Run a REST Microservice using that Supplier:

----
./web.sh -s words -p 9001
curl -H "Accept: application/json" localhost:9001/words
----

Register a Consumer:

----
./registerConsumer.sh -n print -t String -f "System.out::println"
----

Run a REST Microservice using that Consumer:

----
./web.sh -c print -p 9002
curl -X POST -H "Content-Type: text/plain" -d foo localhost:9002/print
----

Run Stream Processing Microservices:

First register a streaming words supplier:

----
./registerSupplier.sh -n wordstream -f "()->Flux.interval(Duration.ofMillis(1000)).map(i->\"message-\"+i)"
----

Then start the source (supplier), processor (function), and sink (consumer) apps
(in reverse order):

----
./stream.sh -p 9103 -i uppercaseWords -c print
./stream.sh -p 9102 -i words -f uppercase -o uppercaseWords
./stream.sh -p 9101 -s wordstream -o words
----

The output will appear in the console of the sink app (one message per second, converted to uppercase):

----
MESSAGE-0
MESSAGE-1
MESSAGE-2
MESSAGE-3
MESSAGE-4
MESSAGE-5
MESSAGE-6
MESSAGE-7
MESSAGE-8
MESSAGE-9
...
----

== Serverless Platform Adapters

As well as being able to run as a standalone process, a Spring Cloud
Function application can be adapted to run one of the existing
serverless platforms. In the project there are adapters for
https://github.com/spring-cloud/spring-cloud-function/tree/{branch}/spring-cloud-function-adapters/spring-cloud-function-adapter-aws[AWS
Lambda],
https://github.com/spring-cloud/spring-cloud-function/tree/{branch}/spring-cloud-function-adapters/spring-cloud-function-adapter-azure[Azure],
and
https://github.com/spring-cloud/spring-cloud-function/tree/{branch}/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk[Apache
OpenWhisk]. The https://github.com/fnproject/fn[Oracle Fn platform]
has its own Spring Cloud Function adapter. And
https://projectriff.io[Riff] supports Java functions and its
https://github.com/projectriff/java-function-invoker[Java Function
Invoker] acts natively is an adapter for Spring Cloud Function jars.

include::adapters/aws-intro.adoc[]
include::adapters/azure-intro.adoc[]
include::adapters/gcp-intro.adoc[]

