diff --git a/README.adoc b/README.adoc index 04d2d50d1..0a7392152 100644 --- a/README.adoc +++ b/README.adoc @@ -1,55 +1,173 @@ +Spring Cloud Function provides a new programming model for Spring Boot +applications, abstracting away all of the transport details and +infrastructure, allowing the developer to keep all the familar tools +and processes, and focus firmly on business logic. + +Here's a complete, executable, testable Spring Boot application +(implementing a simple string manipulation): + +``` +@SpringBootApplication +public class Application { + + @Bean + public Function, Flux> uppercase() { + return value -> value.toUppercase(); + } + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} +``` + +It's just a Spring Boot application, so it can be built, run and +tested, locally and in a CI build, the same way as any other Spring +Boot application. The `Function` is from `java.util` and `Flux` is a +http://www.reactive-streams.org/[Reactive Streams] `Publisher` from +https://projectreactor.io/[Project Reactor]. The function can be +accessed over HTTP or messaging. + +Spring Cloud Function has 4 main features: + +1. Wrappers for `@Beans` of type `Function`, `Consumer` and +`Supplier`, exposing them to the outside world as either HTTP +endpoints and/or message Streams with RabbitMQ, Kafka etc. + +2. Compiling strings which are Java function bodies into bytecode, and +then turning them into `@Beans` that can be wrapped as above. + +3. Deploying a JAR file containing such an application context with an +isolated classloader, so that you can pack them together in a single +JVM. + +4. TBD: adapters for AWS Lambda, and possibly other "serverless" +service providers. + +== Getting Started + +Build from the command line (and "install" the samples): + +``` +$ mvn clean install +``` + +(If you like to YOLO add `-DskipTests`.) + +Run one of the samples, e.g. + +``` +$ java -jar spring-cloud-function-samples/spring-cloud-function-sample/target/*.jar +... +``` + +This runs the app and exposes its functions over HTTP, so you can +convert a string to uppercase, like this: + +``` +$ curl -H "Content-Type: text/plain" localhost:8080/uppercase -d Hello +HELLO +``` + +You can convert multiple strings (a `Flux`) by separating them +with new lines + +``` +$ curl -H "Content-Type: text/plain" localhost:8080/uppercase -d 'Hello +World' +HELLO +WORLD +``` + +(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. A +`Function` is exposed as an HTTP POST if `spring-cloud-function-web` +is on the classpath, and as a Spring Cloud Stream `Processor` if +`spring-cloud-function-stream` is on the classpath, and a +`function.name` is configured in the Spring environment. A `Consumer` +is also exposed as an HTTP POST, or as a Stream `Sink`. A `Supplier` +translates to am HTTP GET, or a Stream `Source`. + +Functions can be of `Flux` or `Flux` and Spring Cloud +Function takes care of converting the data to and from the desired +types, and long as it comes in as plain text or (in the case of the +POJO) JSON. TBD: support for `Flux>` and maybe plain +`Pojo` types (fluxes implied and implemented by the framework). + +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. + +== Deploying a Packaged Function + +TBD: describe the deployer app. + +== Dynamic Compilation + To run these examples, change into the `scripts` directory: ---- cd scripts ---- -== Start the Function Registry Service: +=== Start the Function Registry Service: ---- ./function-registry.sh ---- -== Register a Function: +=== Register a Function: ---- ./registerFunction.sh -n uppercase -f "f->f.map(s->s.toString().toUpperCase())" ---- -== Run a REST Microservice using that Function: +=== Run a REST Microservice using that Function: ---- ./web.sh -f uppercase -p 9000 curl -H "Content-Type: text/plain" -H "Accept: text/plain" :9000/function -d foo ---- -== Register a Supplier: +=== Register a Supplier: ---- ./registerSupplier.sh -n words -f "()->Flux.just(\"foo\",\"bar\")" ---- -== Run a REST Microservice using that Supplier: +=== Run a REST Microservice using that Supplier: ---- ./web.sh -s words -p 9001 curl -H "Accept: application/json" :9001/supplier ---- -== Register a Consumer: +=== Register a Consumer: ---- ./registerConsumer.sh -n print -f "System.out::println" ---- -== Run a REST Microservice using that Consumer: +=== Run a REST Microservice using that Consumer: ---- ./web.sh -c print -p 9002 curl -X POST -H "Content-Type: text/plain" -d foo :9002/consumer ---- -== Run Stream Processing Microservices: +=== Run Stream Processing Microservices: First register a streaming words supplier: