From 6bb6656ae2ee86b6cb76e556793964fe1e1fd8f0 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Fri, 22 Nov 2019 11:50:15 +0100 Subject: [PATCH] Polishing documentation --- docs/src/main/asciidoc/getting-started.adoc | 25 ----- .../main/asciidoc/spring-cloud-function.adoc | 105 +++++++++++------- .../deployer/FunctionDeployerTests.java | 2 +- 3 files changed, 66 insertions(+), 66 deletions(-) diff --git a/docs/src/main/asciidoc/getting-started.adoc b/docs/src/main/asciidoc/getting-started.adoc index d4fdffacc..f46cbe0c7 100644 --- a/docs/src/main/asciidoc/getting-started.adoc +++ b/docs/src/main/asciidoc/getting-started.adoc @@ -31,28 +31,3 @@ HELLOWORLD (You can use `^Q^J` in a terminal to insert a new line in a literal string like that.) - -== Building and Running a Function - -The sample `@SpringBootApplication` above has a function that can be -decorated at runtime by Spring Cloud Function to be an HTTP endpoint, -or a Stream processor, for instance with RabbitMQ, Apache Kafka or -JMS. - -The `@Beans` can be `Function`, `Consumer` or `Supplier` (all from -`java.util`), and their parametric types can be String or POJO. - -Functions can also be of `Flux` or `Flux` and Spring -Cloud Function takes care of converting the data to and from the -desired types, as long as it comes in as plain text or (in the case of -the POJO) JSON. There is also support for `Message` where the -message headers are copied from the incoming event, depending on the -adapter. The web adapter also supports conversion from form-encoded -data to a `Map`, and if you are using the function with Spring Cloud -Stream then all the conversion and coercion features for message -payloads will be applicable as well. - -Functions can be grouped together in a single application, or deployed -one-per-jar. It's up to the developer to choose. An app with multiple -functions can be deployed multiple times in different "personalities", -exposing different functions over different physical transports. diff --git a/docs/src/main/asciidoc/spring-cloud-function.adoc b/docs/src/main/asciidoc/spring-cloud-function.adoc index e9cbf5db3..8b9c6a5c9 100644 --- a/docs/src/main/asciidoc/spring-cloud-function.adoc +++ b/docs/src/main/asciidoc/spring-cloud-function.adoc @@ -22,46 +22,36 @@ include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/maste include::getting-started.adoc[] -== Function Catalog and Flexible Function Signatures +== 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`, using primitives -defined by the https://projectreactor.io/[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>`. - -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. +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 <> section. + === 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. +Spring Cloud Function embraces and builds on top of the 3 core functional interfaces defined by Java +and available to us since Java 8. -|=== -| User Function | Catalog Registration | - -| `Function` | `Function, Flux>` | -| `Function,Message>` | `Function>, Flux>>` | -| `Function, Flux>` | `Function, Flux>` (pass through) | -| `Supplier` | `Supplier>` | -| `Supplier>` | `Supplier>` | -| `Consumer` | `Function, Mono>` | -| `Consumer>` | `Function>, Mono>` | -| `Consumer>` | `Consumer>` | - -|=== +- Supplier +- Function +- Consumer ==== Supplier -As you can see from the table above Supplier can be _reactive_ - `Supplier>` +Supplier can be _reactive_ - `Supplier>` or _imperative_ - `Supplier`. 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, @@ -108,17 +98,6 @@ 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. -=== 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. - === Function Composition Function Composition is a feature that allows one to compose several functions into one. @@ -199,6 +178,41 @@ due to the nature of the reactive functions which are invoked only once to pass 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, Tuple2, Flux>> 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...>>`) 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). @@ -230,6 +244,17 @@ same rules for signature transformation outlined in "Java 8 function support" se 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 The `spring-cloud-function-web` module has autoconfiguration that @@ -309,7 +334,7 @@ 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.function-name=uppercase"); + "--spring.cloud.function.definition=uppercase"); FunctionCatalog catalog = context.getBean(FunctionCatalog.class); Function function = catalog.lookup("uppercase"); diff --git a/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/FunctionDeployerTests.java b/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/FunctionDeployerTests.java index 907645cfe..7a7418db1 100644 --- a/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/FunctionDeployerTests.java +++ b/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/FunctionDeployerTests.java @@ -164,7 +164,7 @@ public class FunctionDeployerTests { public void testWithMainAndStartClassAndSpringConfiguration() throws Exception { String[] args = new String[] { "--spring.cloud.function.location=target/it/bootapp/target/bootapp-1.0.0.RELEASE-exec.jar", - "--spring.cloud.function.function-name=uppercase" }; + "--spring.cloud.function.definition=uppercase" }; ApplicationContext context = SpringApplication.run(DeployerApplication.class, args); FunctionCatalog catalog = context.getBean(FunctionCatalog.class); Function, Message> function = catalog.lookup("uppercase", "application/json");