Skip to content
This repository has been archived by the owner on Mar 13, 2021. It is now read-only.

Commit

Permalink
[#138] Rewrite invoker to support riff streaming protocol
Browse files Browse the repository at this point in the history
Add support for multi I/O functions.
Marshalling/unmarshalling is now fully extensible and handled by SCF.

Only supports streaming via gRPC. http support comes via
http://github.com/projectriff/streaming-http-adapter-buildpack.

Also fixes #135 and #136.
  • Loading branch information
ericbottard authored and Florent Biville committed Sep 18, 2019
1 parent 90b1cb2 commit 119b922
Show file tree
Hide file tree
Showing 116 changed files with 1,635 additions and 5,674 deletions.
21 changes: 11 additions & 10 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
.classpath
.factorypath
.project
.settings/
.springBeans
target
.sts4-cache/
.attach_*
/.classpath
/.factorypath
/.project
/.settings/
/.springBeans
target/
/.sts4-cache/
/.attach_*

.idea/
*.iml
/.idea/
/*.iml
.DS_Store
19 changes: 9 additions & 10 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
language: java
jdk:
- openjdk11
- openjdk11
cache:
directories:
- "$HOME/.m2"
install:
- "./mvnw install -DskipTests=true -Dmaven.javadoc.skip=true -P spring"
matrix:
include:
- stage: test
script: "./mvnw clean test -U -P spring"
stages:
- test
- "$HOME/.m2"
install: true # skip travis' pre-installation of dependencies
script: ./mvnw package integration-test
deploy:
- provider: script
script: .travis/publish-gcs.sh
on:
branch: master
notifications:
slack:
secure: WxZ+HjMsGIj+O4PHTVZIkSi6vVGxRO9XSbYj7HIQq7HglAg5U39oS3KvWDgh04g59HmiiFGLZrQWmOVDbEb/sTjujT0cirCEM4rdvBTK53aVe88nCgj/9PF68phHQCGVBDWqTllZK+OBegue/jHL00mXL/p4GsIhseAxvYNV8DfKvlSKl+Ar1kI4akwG3Kbz4L/I4NM9ZVG6XP3OK9lxXF1dnbRkgLELJ5ffqKklO8dNRINzQbUjLD4ip7Hd68ksXv2Avhzwpa8odXHw34TMFR/nXo19scmT8lhExBclpzFmrGy6wm8REHrO0T/mOE3rI4tnPlgn0oBHExfkhQyjM3Thr8X+Ua+1gW0VGyi70ScJPzqBxj4vio5rA77HzJ5iRHkr+Mi3OGD+inS/yGPJPogEEdewna42sIOmHpzRjZtX3rlQipV9XZLnw9ef8/yIGuz13VJMLpuIK8EaegzibznqTrIdQfDgKb3oI+KrdgBhCv2bQ2Nk3WlcsPM1AR/cSma0b8B7L7Bma3kZZE1e43G5h03At6PCpiidGaZCNF2bHD017I14/kOeC006AE/3peNG0chD5/6N133SBdkBHNLM5OOzxad734oa5MMTuc3KPfoau7w7CTqPX/a613zGfe/3UtHjpJkTcO7itG56s2Nt6uWP/pOwpTHvZ+j35uE=
14 changes: 14 additions & 0 deletions .travis/publish-gcs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash

version=${1:-$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout)}
commit=$(git rev-parse HEAD)

gcloud auth activate-service-account --key-file <(echo "$GCLOUD_CLIENT_SECRET" | base64 --decode)

package=java-function-invoker-${version}.jar
bucket=gs://projectriff/java-function-invoker/releases

gsutil cp -a public-read "${package}" ${bucket}/v${version}/${package}
gsutil cp -a public-read "${package}" ${bucket}/v${version}/snapshots/java-function-invoker-${version}-${commit}.tgz
gsutil cp -a public-read "${package}" ${bucket}/latest/java-function-invoker.jar

98 changes: 25 additions & 73 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

## What It Does

The "Java Function Invoker" lets you concentrate on writing your business logic as a Java function while the invoker takes care of the rest that is needed to run your functions in a Kubernetes cluster with riff and Knative installed.
The invoker is a [Spring Boot](https://projects.spring.io/spring-boot) application that will locate your function in the JAR file you provide based on some configuration settings.
It will then invoke the function for each request.
When used in a function service like [riff on Knative](https://projectriff.io/), the invoker boot application is provided by the platform when functions are built.
The "Java Function Invoker" lets you concentrate on writing your business logic as a Java function while the invoker
takes care of the rest that is needed to run your functions in a Kubernetes cluster with riff installed.
The invoker is a [Spring Boot](https://projects.spring.io/spring-boot) application that will locate your function in
the JAR file you provide based on some configuration settings.
It will then expose the function over gRPC using riff's [streaming protocol](src/main/proto/riff-rpc.proto).
When used in a function service like [riff](https://projectriff.io/), the invoker boot application is provided by
the platform when functions are built and basic request/reply http support is added via
[the streaming/http adapter](http://github.com/projectriff/streaming-http-adapter-buildpack).

## How To Use It

Expand All @@ -17,7 +21,6 @@ If you are not using Spring Boot for your function code then you need to create
There are no required dependencies from the Java Function Invoker, it only requires that your function implements the `java.util.function.Function` interface.

The [Spring Cloud Function](https://cloud.spring.io/spring-cloud-function/) project provides support for writing functions as part of a Spring Boot app.
The Java Function Invoker uses Spring Cloud Function to invoke your functions, so most features available with Spring Cloud Function are also available with the Java Function Invoker.

Example of a plain Java function:

Expand Down Expand Up @@ -63,9 +66,10 @@ public class UppercaseApplication {

Spring Cloud Function will attempt to detect the function from the function source.
If you have a single function declared with `@Bean` in a Spring Boot app then that is the function that will be used.
If you have multiple functions in the source then you have to specify which one you want using a handler (see the next section).
If you have multiple functions in the source then you have to specify which one you want using a bean name (see the next section).

If you have a Plain Java class with a function then you can provide a `Function-Class` entry in the JAR file manifest to indicate which function class to use.
If you have a Plain Java class with a function then you can provide a `Function-Class` entry in the JAR file manifest
to indicate which function class to use or specify its class explicitly.
Here is an example of using a plug-in for Maven to add this information to the manifest:

```xml
Expand All @@ -91,84 +95,32 @@ Here is an example of using a plug-in for Maven to add this information to the m

If your function can't be automatically detected then you need to provide a handler specification.
The simplest form of the handler is a bean name or a class name that can be instantiated (with a default constructor).
More complex creation scenarios can be handled by giving the handler in the form `<bean>[&main=<main>]` where
More complex creation scenarios can be handled by giving the handler via configuration properties:

* `<bean>` is a class name or bean name, and
* `<main` is a Spring `@Configuration` class to create an application context
* `spring.cloud.function.function-location` the file path of a jar file containing the function class, Boot uber-jar or vanilla jar,
* `spring.cloud.function.function-class` is a class name,
* `spring.cloud.function.definition` is a bean name,

If you provide a `main` parameter you need to include Spring Boot and all the other dependencies of the context in your archive.
A Spring Boot jar file does not need an explicit `main` (there is one in the `MANIFEST.MF`), but if you supply one it will be used.
If the `Function` has POJO (i.e. not generic JDK classes) as parameter types, then you also need to depend on `spring-cloud-function-context` (and include that in the archive).

Example:

```
handler=functions.Greeter
```

Example with Spring Boot application:

```
handler=greeter
```

Example with Spring application context and an explicit main

```
handler='greeter&main=functions.FunctionApp'
```

#### Function samples

We have sample application for [plain Java function](https://github.com/projectriff/java-function-invoker/tree/master/samples/greeter) and for [Spring Boot app with a function bean](https://github.com/projectriff/java-function-invoker/tree/master/samples/uppercase).
We have sample application for
[plain Java function](src/test/function-sources/hundred-divider)
and for [Spring Boot app with a function bean](src/test/function-sources/repeater-as-bean).

### Cloud Native Buildpacks

[Buildpacks](https://buildpacks.io/) provide a higher-level abstraction for building apps compared to Dockerfiles.

The [riff project](https://github.com/projectriff/riff) provides its own [buildpack](https://github.com/projectriff/riff-buildpack) that specifically targets building functions using the riff function invokers.
The [riff project](https://github.com/projectriff/riff) provides its own
[builder](https://github.com/projectriff/builder) that specifically targets building functions using the riff function invokers.

## How it Works

As long as the dependencies are included in the archive correctly, you can supply a `Function` with a wide range of input and output types.
The input or output types can be plain JDK classes, or POJOs defined in your archive, or `Message` (from `spring-messaging`) or `Publisher` (from `reactive-streams`) or `Flux` or `Mono` (from `reactor-core`).
The input or output types can be plain JDK classes, or POJOs defined in your archive, or `Message`
(from `spring-messaging`) or `Publisher` (from `reactive-streams`) or `Flux` or `Mono` (from `reactor-core`).
Input and output types can also be reactor's `TupleX` classes, thus allowing multi I/O functions.
The `Message` type will give you access to header metadata in the incoming and outgoing messages.
If the input or output is either a `Publisher` or a `Message` (or a `Publisher<Message>`) then both input and output must be in the same form, with possibly different payload types, obviously. POJOs are converted from incoming messages assuming the payload is JSON and using the GSON library.

The invoker is a [Spring Boot](https://projects.spring.io/spring-boot) application with a configuration key `function.uri` that can be used to point to a `java.util.function.Function`.
Because of the way Spring Boot works you can use an environment variable `FUNCTION_URI` or a System property `function.uri` or a command line argument `--function.uri` (amongst other options).
Its value points to a classpath archive, which can be a jar file or a directory, together with parameters:

* `handler`: the class name of a `Function` to execute, or a bean name of a `Function`. Can also be a comma, or pipe-separated list of functions, which are composed together at runtime.

* `main`: (optional) the class name of a Spring `@Configuration` that can be used to create a Spring application context, in which the `handler` is defined.

The jar archive can also be a comma-separated list of archive locations, in case you need to compose things together.

> NOTE: If your Spring Boot application contains a single function bean, then you can omit the `handler` and `main` parameters since the function can be automatically detected. You can also omit the the `handler` and `main` parameters if the JAR manifest has a `Function-Class` entry.
Examples:

* A jar file

```
file:target/app.jar?handler=functions.Greeter
```

* A Spring app (with `spring-cloud-function-context`) and function specified by bean class

```
file:target/app.jar?handler=functions.Greeter&main=functions.Application
```

* A Spring app and function specified by bean name

```
file:target/app.jar?handler=greeter&main=functions.Application
```

* A Spring app split between 2 jar files

```
file:target/app.jar,file:target/lib.jar?handler=greeter&main=functions.Application
```
POJOs are converted from incoming messages / to return values using the `Content-Type` header and the `expectedContentType` field value.
```
49 changes: 0 additions & 49 deletions docs/riff_create_java.md

This file was deleted.

47 changes: 0 additions & 47 deletions docs/riff_init_java.md

This file was deleted.

Loading

0 comments on commit 119b922

Please sign in to comment.