diff --git a/.circleci/config.yml b/.circleci/config.yml
deleted file mode 100644
index b509d952..00000000
--- a/.circleci/config.yml
+++ /dev/null
@@ -1,108 +0,0 @@
-version: 2
-jobs:
- build:
- working_directory: ~/fn-java-fdk
- machine:
- java:
- version: oraclejdk8
- environment:
- # store_artifacts doesn't shell substitute so the variable
- # definitions are duplicated in those steps too.
- FDK_ARTIFACT_DIR: /tmp/artifacts/fdk
- TEST_ARTIFACT_DIR: /tmp/artifacts/tests
- STAGING_DIR: /tmp/staging-repository
- steps:
- - checkout
- - run:
- name: Set release to latest branch version
- command: |
- git checkout origin/${CIRCLE_BRANCH} release.version
- echo next release version is $(cat release.version)
- - run:
- name: Determine the release version
- command: ./.circleci/update-versions.sh
- - run:
- name: Build and deploy locally
- command: |
- rm -rf $STAGING_DIR
- mkdir -p $STAGING_DIR
- mvn deploy -DaltDeploymentRepository=localStagingDir::default::file://"$STAGING_DIR"
- - store_test_results:
- path: runtime/target/surefire-reports
- - store_test_results:
- path: testing/target/surefire-reports
-
- - run:
- name: Copy FDK artifacts to upload folder
- command: |
- mkdir -p "$FDK_ARTIFACT_DIR"
- cp -a api/target/*.jar "$FDK_ARTIFACT_DIR"
- cp -a runtime/target/*.jar "$FDK_ARTIFACT_DIR"
- - store_artifacts:
- name: Upload FDK artifacts
- path: /tmp/artifacts/fdk
-
- - run:
- name: Update Docker to latest
- command: ./.circleci/install-docker.sh
-
- - run:
- name: Login to Docker
- command: docker login -u $DOCKER_USER -p $DOCKER_PASS
-
- - run:
- name: Build fn-java-fdk Docker build image
- command: |
- cd build-image
- ./docker-build.sh -t fnproject/fn-java-fdk-build:$(cat ../release.version) .
-
- - run:
- name: Build fn-java-fdk-jdk9 Docker build image
- command: |
- cd build-image
- ./docker-build.sh -f Dockerfile-jdk9 -t fnproject/fn-java-fdk-build:jdk9-$(cat ../release.version) .
- - run:
- name: Build fn-java-fdk Docker runtime image
- command: |
- cd runtime
- docker build -t fnproject/fn-java-fdk:$(cat ../release.version) .
-
- - run:
- name: Build fn-java-fdk-jdk9 Docker runtime image
- command: |
- cd runtime
- docker build -f Dockerfile-jdk9 -t fnproject/fn-java-fdk:jdk9-$(cat ../release.version) .
-
- - run:
- name: Install fn binary (as it is needed for the integration tests)
- command: ./.circleci/install-fn.sh
-
- - run:
- name: Run integration tests
- command: REPOSITORY_LOCATION="$STAGING_DIR" FN_JAVA_FDK_VERSION=$(cat release.version) ./integration-tests/run-local.sh
- timeout: 1200
-
- - run:
- name: Copy integration test results to test artifact dir
- command: |
- mkdir -p "$TEST_ARTIFACT_DIR"
- for test_dir in ./integration-tests/main/test-*; do
- test_name="$(basename "$test_dir")"
- mkdir "${TEST_ARTIFACT_DIR}/${test_name}"
- cp -a $(find "$test_dir" -maxdepth 1 -type f) "${TEST_ARTIFACT_DIR}/${test_name}"
- done
- find "${TEST_ARTIFACT_DIR}"
- when: always
- - store_artifacts:
- name: Upload integration test results to artifacts
- path: /tmp/artifacts/tests
- - deploy:
- name: Release new version
- command: |
- if [[ "${CIRCLE_BRANCH}" == "master" && -z "${CIRCLE_PR_REPONAME}" ]]; then
- git config --global user.email "ci@fnproject.com"
- git config --global user.name "CI"
- git branch --set-upstream-to=origin/${CIRCLE_BRANCH} ${CIRCLE_BRANCH}
- ./.circleci/release.sh
- fi
-
diff --git a/.circleci/install-docker.sh b/.circleci/install-docker.sh
deleted file mode 100755
index 688d53a1..00000000
--- a/.circleci/install-docker.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env bash
-
-set -ex
-
-docker version || true
-sudo service docker stop || true
-curl -fsSL https://get.docker.com/ | sudo sh
-docker version
diff --git a/.circleci/install-fn.sh b/.circleci/install-fn.sh
deleted file mode 100755
index 615a4ff7..00000000
--- a/.circleci/install-fn.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-
-source "$(dirname $0)/lib.sh"
-
-check_command_exists jq
-check_command_exists git
-
-: "${FN_CLI_BINARY:=fn_linux}"
-: "${INSTALL_DIR:=/usr/local/bin}"
-
-FN_BINARY_LOCATION="$(\
- curl -s https://api.github.com/repos/fnproject/cli/releases/latest \
- | jq -r ".assets[] \
- | select(.name | test(\"${FN_CLI_BINARY}\")) \
- | .browser_download_url"\
-)"
-
-echo "Download fn from $FN_BINARY_LOCATION"
-# --location = follow redirects
-curl -f --location "$FN_BINARY_LOCATION" --output fn
-chmod +x fn
-./fn || true # show fn version
-sudo cp fn "${INSTALL_DIR}/fn"
diff --git a/.circleci/install-go.sh b/.circleci/install-go.sh
deleted file mode 100755
index b61512dc..00000000
--- a/.circleci/install-go.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env bash
-
-: ${GOVERSION:=1.8.3}
-: ${OS:=linux}
-: ${ARCH:=amd64}
-
-set -ex
-
-go version
-go env GOROOT
-
-# Remove previous Go version
-sudo rm -rf /usr/local/go
-
-# Install Go
-BUILD_DIR="/tmp/go-${GOVERSION}.${OS}.${ARCH}"
-mkdir -p "$BUILD_DIR"
-pushd "$BUILD_DIR"
- wget https://storage.googleapis.com/golang/go${GOVERSION}.${OS}-${ARCH}.tar.gz
- sudo tar -C /usr/local -xzf go${GOVERSION}.${OS}-${ARCH}.tar.gz
- go get -u github.com/golang/dep/...
-popd
-
-mkdir -p "${GOPATH}/bin"
-
-# Install Glide
-if ! type glide 2>&1 > /dev/null; then
- curl https://glide.sh/get | sh
-fi
-
-go version
-go env GOROOT
diff --git a/.circleci/lib.sh b/.circleci/lib.sh
deleted file mode 100644
index d6c3e1fe..00000000
--- a/.circleci/lib.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-check_command_exists() {
- local command="$1"; shift
- if ! command -v "$command" 2>&1 > /dev/null; then
- echo "'$command' is not installed, please install it" >&2
- fi
-}
diff --git a/.circleci/release.sh b/.circleci/release.sh
deleted file mode 100755
index f4a3983e..00000000
--- a/.circleci/release.sh
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/bin/bash
-
-set -e
-USER=fnproject
-
-SERVICE=fn-java-fdk
-RUNTIME_IMAGE=${SERVICE}
-BUILD_IMAGE=${SERVICE}-build
-
-release_version=$(cat release.version)
-if [[ $release_version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] ; then
- echo "Deploying version $release_version"
-else
- echo Invalid version $release_version
- exit 1
-fi
-
-
-# Calculate new version
-version_parts=(${release_version//./ })
-new_minor=$((${version_parts[2]}+1))
-new_version="${version_parts[0]}.${version_parts[1]}.$new_minor"
-
-if [[ $new_version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] ; then
- echo "Next version $new_version"
-else
- echo Invalid new version $new_version
- exit 1
-fi
-
-
-# Deploy to bintray
-mvn -s ./settings-deploy.xml \
- -DskipTests \
- -DaltDeploymentRepository="fnproject-release-repo::default::$MVN_RELEASE_REPO" \
- -Dfnproject-release-repo.username="$MVN_RELEASE_USER" \
- -Dfnproject-release-repo.password="$MVN_RELEASE_PASSWORD" \
- -DdeployAtEnd=true \
- clean deploy
-
-
-# Regenerate runtime and build images and push them
-(
- moving_version=${release_version%.*}-latest
-
- ## jdk8 runtime
- docker tag $USER/$RUNTIME_IMAGE:${release_version} $USER/$RUNTIME_IMAGE:latest
- docker tag $USER/$RUNTIME_IMAGE:${release_version} $USER/$RUNTIME_IMAGE:${moving_version}
- docker push $USER/$RUNTIME_IMAGE:latest
- docker push $USER/$RUNTIME_IMAGE:${release_version}
- docker push $USER/$RUNTIME_IMAGE:${moving_version}
-
- ## jdk8 build
- docker tag $USER/$BUILD_IMAGE:${release_version} $USER/$BUILD_IMAGE:latest
- docker tag $USER/$BUILD_IMAGE:${release_version} $USER/$BUILD_IMAGE:${moving_version}
- docker push $USER/$BUILD_IMAGE:latest
- docker push $USER/$BUILD_IMAGE:${release_version}
- docker push $USER/$BUILD_IMAGE:${moving_version}
-
- ## jdk9 runtime
- docker tag $USER/$RUNTIME_IMAGE:jdk9-${release_version} $USER/$RUNTIME_IMAGE:jdk9-latest
- docker tag $USER/$RUNTIME_IMAGE:jdk9-${release_version} $USER/$RUNTIME_IMAGE:jdk9-${moving_version}
- docker push $USER/$RUNTIME_IMAGE:jdk9-latest
- docker push $USER/$RUNTIME_IMAGE:jdk9-${release_version}
- docker push $USER/$RUNTIME_IMAGE:jdk9-${moving_version}
-
- ## jdk9 build
- docker tag $USER/$BUILD_IMAGE:jdk9-${release_version} $USER/$BUILD_IMAGE:jdk9-latest
- docker tag $USER/$BUILD_IMAGE:jdk9-${release_version} $USER/$BUILD_IMAGE:jdk9-${moving_version}
- docker push $USER/$BUILD_IMAGE:jdk9-latest
- docker push $USER/$BUILD_IMAGE:jdk9-${release_version}
- docker push $USER/$BUILD_IMAGE:jdk9-${moving_version}
-)
-
-
-# Push result to git
-
-echo $new_version > release.version
-git tag -a "$release_version" -m "version $release_version"
-git add release.version
-git commit -m "$SERVICE: post-$release_version version bump [skip ci]"
-git push
-git push origin "$release_version"
diff --git a/.circleci/update-versions.sh b/.circleci/update-versions.sh
deleted file mode 100755
index 1490bd2e..00000000
--- a/.circleci/update-versions.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-
-release_version=$(cat release.version)
-if [[ $release_version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] ; then
- echo "Deploying version $release_version"
-else
- echo Invalid version $release_version
- exit 1
-fi
-
-mvn versions:set -D newVersion=${release_version} versions:update-child-modules
-
-
-# We need to replace the example dependency versions also
-# (sed syntax for portability between MacOS and gnu)
-find . -name pom.xml |
- xargs -n 1 sed -i.bak -e "s|.*|${release_version}|"
-find . -name pom.xml.bak -delete
-
diff --git a/.gitignore b/.gitignore
index f93517a1..cb830517 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,10 +3,18 @@
.DS_Store
*/dependency-reduced-pom.xml
target/
+rpm-package/
*.log
\#*
.\#*
logs/
/headrevtag.txt
*.bak
-*.versionsBackup
\ No newline at end of file
+*.versionsBackup
+.gradle
+examples/gradle-build/build
+**/*.classpath
+**/*.project
+**/*.settings
+.oca/
+.csis
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9fce1768..619755d2 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -16,4 +16,9 @@ We welcome all contributions!
* Don't make breaking changes to the public APIs.
* Write tests - especially for public APIs.
* Make sure that changes to `api` are backwards compatible with `runtime` and vice-versa.
+
+## Note for mac users
+ #### If you run into build failures, try the steps below:
+ * Install `cmake`, if you don't have it already by running `brew install cmake`
+ * Go to `fdk-java/runtime/src/main/c` folder and run `./buildit.sh`
diff --git a/LICENSE b/LICENSE
index 97ee2c36..151b7eab 100644
--- a/LICENSE
+++ b/LICENSE
@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright 2017 Oracle Corporation
+ Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/NOTICE.txt b/NOTICE.txt
new file mode 100644
index 00000000..4b371e6c
--- /dev/null
+++ b/NOTICE.txt
@@ -0,0 +1,23 @@
+ Fn Project
+ ============
+
+Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+==========================================================================
+Third Party Dependencies
+==========================================================================
+
+This project includes or depends on code from third party projects.
+Attributions are contained in THIRD_PARTY_LICENSES.txt
\ No newline at end of file
diff --git a/README.md b/README.md
index 81eb9874..25d2776b 100644
--- a/README.md
+++ b/README.md
@@ -1,331 +1,29 @@
-# Fn Java Functions Developer Kit (FDK)
[](https://circleci.com/gh/fnproject/fdk-java)
-This project adds support for writing functions in Java on the [Fn
-platform](https://github.com/fnproject/fn), with full support for Java 9
-as the default out of the box.
+# Function Development Kit for Java (FDK for Java)
-# FAQ
-Some common questions are answered in [our FAQ](docs/FAQ.md).
+The Function Development Kit for Java makes it easy to build and deploy Java functions to Fn with full support for Java 11+ as the default out of the box.
-# Quick Start Tutorial
+Some of the FDK for Java features include:
-By following this step-by-step guide you will learn to create, run and deploy
-a simple app written in Java on Fn.
+- Parsing input and writing output
+- Flexible data binding to Java objects
+- Function testing using JUnit rules
+- And more!
-## Pre-requisites
+## Learn about the Fn Project
-Before you get started you will need the following things:
+New to Fn Project? If you want to learn more about using the Fn Project to power your next project, start with the [official documentation](https://github.com/fnproject/docs).
-* The [Fn CLI](https://github.com/fnproject/cli) tool
-* [Docker-ce 17.06+ installed locally](https://docs.docker.com/engine/installation/)
+## Using the Function Development Kit for Java
-### Install the Fn CLI tool
+For detailed instructions on using the FDK to build and deploy Java functions to Fn, please see the official FDK developer guide in our docs repo here: https://github.com/fnproject/docs/blob/master/fdks/fdk-java/README.md.
-To install the Fn CLI tool, just run the following:
+## Integrating with OCI Services
-```
-curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh
-```
+Use the fn-events library to easily integrate your Function with OCI services; Connector Hub, API Gateway and Notifications. Start with the [README.md](fn-events/README.md)
-This will download a shell script and execute it. If the script asks for
-a password, that is because it invokes sudo.
-
-## Your first Function
-
-### 1. Create your first Java Function:
-
-```bash
-$ mkdir hello-java-function && cd hello-java-function
-$ fn init --runtime=java --name your_dockerhub_account/hello
-Runtime: java
-function boilerplate generated.
-func.yaml created
-```
-
-This creates the boilerplate for a new Java Function based on Maven and Oracle
-Java 9. The `pom.xml` includes a dependency on the latest version of the Fn
-Java FDK that is useful for developing your Java functions.
-
-You can now import this project into your favourite IDE as normal.
-
-### 2. Deep dive into your first Java Function:
-We'll now take a look at what makes up our new Java Function. First, lets take
-a look at the `func.yaml`:
-
-```bash
-$ cat func.yaml
-name: your_dockerhub_account/hello
-version: 0.0.1
-runtime: java
-cmd: com.example.fn.HelloFunction::handleRequest
-```
-
-The `cmd` field determines which method is called when your funciton is
-invoked. In the generated Function, the `func.yaml` references
-`com.example.fn.HelloFunction::handleRequest`. Your functions will likely live
-in different classes, and this field should always point to the method to
-execute, with the following syntax:
-
-```text
-cmd: ::
-```
-
-For more information about the fields in `func.yaml`, refer to the [Fn platform
-documentation](https://github.com/fnproject/fn/blob/master/docs/function-file.md)
-about it.
-
-Let's also have a brief look at the source:
-`src/main/java/com/example/fn/HelloFunction.java`:
-
-```java
-package com.example.fn;
-
-public class HelloFunction {
-
- public String handleRequest(String input) {
- String name = (input == null || input.isEmpty()) ? "world" : input;
-
- return "Hello, " + name + "!";
- }
-
-}
-```
-
-The function takes some optional input and returns a greeting dependent on it.
-
-### 3. Run your first Java Function:
-You are now ready to run your Function locally using the Fn CLI tool.
-
-```bash
-$ fn build
-Building image your_dockerhub_account/hello:0.0.1
-Sending build context to Docker daemon 14.34kB
-Step 1/11 : FROM fnproject/fn-java-fdk-build:jdk9-latest as build-stage
- ---> 5435658a63ac
-Step 2/11 : WORKDIR /function
- ---> 37340c5aa451
-
-...
-
-Step 5/11 : RUN mvn package dependency:copy-dependencies -DincludeScope=runtime -DskipTests=true -Dmdep.prependGroupId=true -DoutputDirectory=target --fail-never
----> Running in 58b3b1397ba2
-[INFO] Scanning for projects...
-Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-compiler-plugin/3.3/maven-compiler-plugin-3.3.pom
-Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-compiler-plugin/3.3/maven-compiler-plugin-3.3.pom (11 kB at 21 kB/s)
-
-...
-
-[INFO] ------------------------------------------------------------------------
-[INFO] BUILD SUCCESS
-[INFO] ------------------------------------------------------------------------
-[INFO] Total time: 2.228 s
-[INFO] Finished at: 2017-06-27T12:06:59Z
-[INFO] Final Memory: 18M/143M
-[INFO] ------------------------------------------------------------------------
-
-...
-
-Function your_dockerhub_account/hello:0.0.1 built successfully.
-
-$ fn run
-Hello, world!
-```
-
-The next time you run this, it will execute much quicker as your dependencies
-are cached. Try passing in some input this time:
-
-```bash
-$ echo -n "Universe" | fn run
-...
-Hello, Universe!
-```
-
-### 4. Testing your function
-The Fn Java FDK includes a testing library providing useful [JUnit
-4](http://junit.org/junit4/) rules to test functions. Look at the test in
-`src/test/java/com/example/fn/HelloFunctionTest.java`:
-
-```java
-package com.example.fn;
-
-import com.fnproject.fn.testing.*;
-import org.junit.*;
-
-import static org.junit.Assert.*;
-
-public class HelloFunctionTest {
-
- @Rule
- public final FnTestingRule testing = FnTestingRule.createDefault();
-
- @Test
- public void shouldReturnGreeting() {
- testing.givenEvent().enqueue();
- testing.thenRun(HelloFunction.class, "handleRequest");
-
- FnResult result = testing.getOnlyResult();
- assertEquals("Hello, world!", result.getBodyAsString());
- }
-
-}
-```
-
-This test is very simple: it just enqueues an event with empty input and then
-runs the function, checking its output. Under the hood, the `FnTestingRule` is
-actually instantiating the same runtime wrapping function invocations, so that
-during the test your function will be invoked in exactly the same way that it
-would when deployed.
-
-There is much more functionality to construct tests in the testing library.
-Testing functions is covered in more detail in [Testing
-Functions](docs/TestingFunctions.md).
-
-### 5. Run using HTTP and the local Fn server
-The previous example used `fn run` to run a function directly via docker, you
-can also use the Fn server locally to test the deployment of your function and
-the HTTP calls to your functions.
-
-Open another terminal and start the Fn server:
-
-```bash
-$ fn start
-```
-
-Then in your original terminal create an app:
-
-```bash
-$ fn apps create java-app
-Successfully created app: java-app
-```
-
-Now deploy your Function using the `fn deploy` command. This will bump the
-function's version up, rebuild it, and push the image to the Docker registry,
-ready to be used in the function deployment. Finally it will create a route on
-the local Fn server, corresponding to your function.
-
-We are using the `--local` flag to tell fn to skip pushing the image anywhere
-as we are just going to run this on our local fn server that we started with
-`fn start` above.
-
-```bash
-$ fn deploy --app java-app --local
-...
-Bumped to version 0.0.2
-Building image hello:0.0.2
-Sending build context to Docker daemon 14.34kB
-
-...
-
-Successfully built bf2b7fa55520
-Successfully tagged your_dockerhub_account/hello:0.0.2
-Updating route /hello-java-function using image your_dockerhub_account/hello:0.0.2...
-```
-
-Call the Function via the Fn CLI:
-
-```bash
-$ fn call java-app /hello-java-function
-Hello, world!
-```
-
-You can also call the Function via curl:
-
-```bash
-$ curl http://localhost:8080/r/java-app/hello-java-function
-Hello, world!
-```
-
-### 6. Something more interesting
-The Fn Java FDK supports [flexible data binding](docs/DataBinding.md) to make
-it easier for you to map function input and output data to Java objects.
-
-Below is an example to of a Function that returns a POJO which will be
-serialized to JSON using Jackson:
-
-```java
-package com.example.fn;
-
-public class PojoFunction {
-
- public static class Greeting {
- public final String name;
- public final String salutation;
-
- public Greeting(String salutation, String name) {
- this.salutation = salutation;
- this.name = name;
- }
- }
-
- public Greeting greet(String name) {
- if (name == null || name.isEmpty())
- name = "World";
-
- return new Greeting("Hello", name);
- }
-
-}
-```
-
-Update your `func.yaml` to reference the new method:
-
-```yaml
-cmd: com.example.fn.PojoFunction::greet
-```
-
-Now run your new function:
-
-```bash
-$ fn run
-...
-{"name":"World","salutation":"Hello"}
-
-$ echo -n Michael | fn run
-...
-{"name":"Michael","salutation":"Hello"}
-```
-
-## 7. Where do I go from here?
-
-Learn more about the Fn Java FDK by reading the next tutorials in the series.
-Also check out the examples in the [`examples` directory](examples) for some
-functions demonstrating different features of the Fn Java FDK.
-
-### Configuring your function
-
-If you want to set up the state of your function object before the function is
-invoked, and to use external configuration variables that you can set up with
-the Fn tool, have a look at the [Function
-Configuration](docs/FunctionConfiguration.md) tutorial.
-
-### Input and output bindings
-
-You have the option of taking more control of how serialization and
-deserialization is performed by defining your own bindings.
-
-See the [Data Binding](docs/DataBinding.md) tutorial for other out-of-the-box
-options and the [Extending Data Binding](docs/ExtendingDataBinding.md) tutorial
-for how to define and use your own bindings.
-
-### Asynchronous workflows
-
-Suppose you want to call out to some other function from yours - perhaps
-a function written in a different language, or even one maintained by
-a different team. Maybe you then want to do some processing on the result. Or
-even have your function interact asynchronously with a completely different
-system. Perhaps you also need to maintain some state for the duration of your
-function, but you don't want to pay for execution time while you're waiting for
-someone else to do their work.
-
-If this sounds like you, then have a look at the [Fn Flow
-quickstart](docs/FnFlowsUserGuide.md).
-
-# Get help
-
- * Come over and chat to us on the [fnproject Slack](https://join.slack.com/t/fnproject/shared_invite/enQtMjIwNzc5MTE4ODg3LTdlYjE2YzU1MjAxODNhNGUzOGNhMmU2OTNhZmEwOTcxZDQxNGJiZmFiMzNiMTk0NjU2NTIxZGEyNjI0YmY4NTA).
- * Raise an issue in [our github](https://github.com/fnproject/fn-java-fdk/).
-
-# Contributing
+## Contributing to the Function Development Kit for Java
Please see [CONTRIBUTING.md](CONTRIBUTING.md).
+
diff --git a/THIRD_PARTY_LICENSES.txt b/THIRD_PARTY_LICENSES.txt
new file mode 100644
index 00000000..12003c06
--- /dev/null
+++ b/THIRD_PARTY_LICENSES.txt
@@ -0,0 +1,999 @@
+Third Party Attributions
+
+The following software (or subsets of the software) are dependencies
+of this product. They are identified by the Fn Project Java FDK module(s) that use
+them.
+
+The first section ("Third Party Runtime Dependencies") contains dependencies
+that might be used at runtime by an Fn Project Java FDK application.
+
+The second section ("Third Party Attributions for Examples, Tests, Builds, etc")
+contains dependencies that are used in examples and to test and build Fn Project Java FDK.
+They are likely not needed at runtime by an Fn Project Java FDK application.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+Third Party Runtime Dependencies
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+oci-java-sdk is dual-licensed to you under the Universal Permissive License (UPL) 1.0 or Apache License 2.0. See below for license terms. You may choose either license.
+Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+License: Copies of the Apache License V2 and the Universal Permissive License 1.0 are included at the end of this file.
+
+--------------------------------------------------------------------------------
+
+fdk-java is licensed under the Apache License, Version 2.0.
+License: A copy of the Apache License V2 is included at the end of this file.
+
+--------------------------------------------------------------------------------
+
+Apache Commons Logging
+Copyright 2003-2016 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+License: Apache License 2.0, a copy of the license is included at the end of this file.
+
+--------------------------------------------------------------------------------
+
+Apache Commons IO
+Copyright 2002-2017 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+License: Apache License 2.0, a copy of the license is included at the end of this file.
+
+--------------------------------------------------------------------------------
+
+Apache HttpComponents Core
+Copyright 2005-2020 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+License: Apache License 2.0, a copy of the license is included at the end of this file.
+
+--------------------------------------------------------------------------------
+
+Apache HttpComponents Client (including HttpMime)
+Copyright 1999-2018 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+License: Apache License 2.0, a copy of the license is included at the end of this file.
+
+--------------------------------------------------------------------------------
+
+Jackson Databind
+Copyright (c) 2019 Tatu Saloranta
+
+License: Apache License 2.0, a copy of the license is included at the end of this file.
+
+NOTICE FILE:
+===============
+# Jackson JSON processor
+
+Jackson is a high-performance, Free/Open Source JSON processing library.
+It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has
+been in development since 2007.
+It is currently developed by a community of developers, as well as supported
+commercially by FasterXML.com.
+
+## Licensing
+
+Jackson core and extension components may be licensed under different licenses.
+To find the details that apply to this artifact see the accompanying LICENSE file.
+For more information, including possible other licensing options, contact
+FasterXML.com (http://fasterxml.com).
+
+## Credits
+
+A list of contributors may be found from CREDITS file, which is included
+in some artifacts (usually source distributions); but is always available
+from the source code management (SCM) system project uses.
+
+--------------------------------------------------------------------------------
+
+Typetools
+Copyright 2010-2019 Jonathan Halterman and friends. Released under the Apache 2.0 license.
+License: Apache License 2.0, a copy of the license is included at the end of this file.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+Third Party Attributions for Examples, Tests, Builds, etc
+
+The following software (or subsets of the software) is used when building the
+Fn Project Java FDK, or in the examples and tests. They are generally not required by
+users of the Fn Project Java FDK and not required during runtime.
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+Mockito Core
+Copyright (c) 2007 Mockito contributors
+The MIT License
+
+License: The MIT License, a copy of the license is included at the end of this file.
+
+................................................................................
+
+Fourth Party Dependencies
+
+Byte Buddy (without dependencies)
+Copyright 2014 - 2020 Rafael Winterhalter
+Apache License Version 2.0
+
+
+Byte Buddy Java agent
+Copyright 2014 - 2020 Rafael Winterhalter
+Apache License Version 2.0
+
+Objenesis
+Copyright 2006-2020 the original author or authors.
+Apache License Version 2.0
+
+License: Apache License 2.0, a copy of the license is included at the end of this file.
+
+--------------------------------------------------------------------------------
+
+AssertJ Core
+
+Copyright 2012-2020 the original author or authors.
+Apache License Version 2.0
+
+License: Apache License 2.0, a copy of the license is included at the end of this file.
+
+................................................................................
+
+Fourth Party Dependencies
+
+Byte Buddy (without dependencies)
+Copyright 2014 - 2020 Rafael Winterhalter
+Apache License Version 2.0
+
+Byte Buddy Java agent
+Copyright 2014 - 2020 Rafael Winterhalter
+Apache License Version 2.0
+
+opentest4j
+Copyright 2015-2018 the original author or authors.
+Apache License Version 2.0
+
+License: Apache License 2.0, a copy of the license is included at the end of this file.
+
+
+Hamcrest
+Copyright (c) 2000-2015 www.hamcrest.org
+BSD License
+
+License: BSD License, a copy of the license is included at the end of this file.
+
+
+JUnit 4
+Copyright (c) JUnit. All Rights Reserved.
+Eclipse Public License - v 1.0
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE
+TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR
+DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS
+AGREEMENT.
+
+License: Eclipse Public License - v 1.0, a copy of the license is included at the end of this file.
+
+
+JUnit Jupiter API
+Copyright 2015-2020 the original author or authors.
+Eclipse Public License - v 2.0
+
+License: Eclipse Public License - v 2.0, a copy of the license is included at the end of this file.
+
+--------------------------------------------------------------------------------
+
+JUnit 4
+Copyright (c) JUnit. All Rights Reserved.
+Eclipse Public License - v 1.0
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE
+TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR
+DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS
+AGREEMENT.
+
+License: Eclipse Public License - v 1.0, a copy of the license is included at the end of this file.
+
+................................................................................
+
+Fourth Party Dependencies
+
+Hamcrest
+Copyright (c) 2000-2015 www.hamcrest.org
+BSD License
+
+License: BSD License, a copy of the license is included at the end of this file.
+
+================================================================================
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+
+================================================================================
+The Universal Permissive License (UPL), Version 1.0
+
+Subject to the condition set forth below, permission is hereby granted to any
+person obtaining a copy of this software, associated documentation and/or data
+(collectively the "Software"), free of charge and under any and all copyright
+rights in the Software, and any and all patent rights owned or freely
+licensable by each licensor hereunder covering either (i) the unmodified
+Software as contributed to or provided by such licensor, or (ii) the Larger
+Works (as defined below), to deal in both
+
+(a) the Software, and
+(b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+one is included with the Software (each a "Larger Work" to which the Software
+is contributed by such licensors),
+
+without restriction, including without limitation the rights to copy, create
+derivative works of, display, perform, and distribute the Software and make,
+use, sell, offer for sale, import, export, have made, and have sold the
+Software and the Larger Work(s), and to sublicense the foregoing rights on
+either these or other terms.
+
+This license is subject to the following condition:
+The above copyright notice and either this complete permission notice or at
+a minimum a reference to the UPL must be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+================================================================================
+The MIT License
+
+Copyright (c)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+================================================================================
+BSD License
+
+Copyright (c)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of
+conditions and the following disclaimer. Redistributions in binary form must reproduce
+the above copyright notice, this list of conditions and the following disclaimer in
+the documentation and/or other materials provided with the distribution.
+
+Neither the name of Hamcrest nor the names of its contributors may be used to endorse
+or promote products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+================================================================================
+Common Public License Version 1.0 (CPL)
+(NOTE: This license has been superseded by the Eclipse Public License)
+
+Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC
+LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
+CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and documentation
+distributed under this Agreement, and
+
+b) in the case of each subsequent Contributor:
+
+i) changes to the Program, and
+
+ii) additions to the Program;
+
+where such changes and/or additions to the Program originate from and are
+distributed by that particular Contributor. A Contribution 'originates' from
+a Contributor if it was added to the Program by such Contributor itself or
+anyone acting on such Contributor's behalf. Contributions do not include
+additions to the Program which: (i) are separate modules of software
+distributed in conjunction with the Program under their own license agreement,
+and (ii) are not derivative works of the Program.
+
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents " mean patent claims licensable by a Contributor which are
+necessarily infringed by the use or sale of its Contribution alone or when
+combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this
+Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement,
+including all Contributors.
+
+2. GRANT OF RIGHTS
+
+a) Subject to the terms of this Agreement, each Contributor hereby grants
+Recipient a non-exclusive, worldwide, royalty-free copyright license to
+reproduce, prepare derivative works of, publicly display, publicly perform,
+distribute and sublicense the Contribution of such Contributor, if any, and
+such derivative works, in source code and object code form.
+
+b) Subject to the terms of this Agreement, each Contributor hereby grants
+Recipient a non-exclusive, worldwide, royalty-free patent license under
+Licensed Patents to make, use, sell, offer to sell, import and otherwise
+transfer the Contribution of such Contributor, if any, in source code and
+object code form. This patent license shall apply to the combination of the
+Contribution and the Program if, at the time the Contribution is added by the
+Contributor, such addition of the Contribution causes such combination to be
+covered by the Licensed Patents. The patent license shall not apply to any
+other combinations which include the Contribution. No hardware per se is
+licensed hereunder.
+
+c) Recipient understands that although each Contributor grants the licenses to
+its Contributions set forth herein, no assurances are provided by any
+Contributor that the Program does not infringe the patent or other intellectual
+property rights of any other entity. Each Contributor disclaims any liability
+to Recipient for claims brought by any other entity based on infringement of
+intellectual property rights or otherwise. As a condition to exercising the
+rights and licenses granted hereunder, each Recipient hereby assumes sole
+responsibility to secure any other intellectual property rights needed, if any.
+For example, if a third party patent license is required to allow Recipient to
+distribute the Program, it is Recipient's responsibility to acquire that
+license before distributing the Program.
+
+d) Each Contributor represents that to its knowledge it has sufficient
+copyright rights in its Contribution, if any, to grant the copyright license
+set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form under
+its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+
+b) its license agreement:
+
+i) effectively disclaims on behalf of all Contributors all warranties and
+conditions, express and implied, including warranties or conditions of title
+and non-infringement, and implied warranties or conditions of merchantability
+and fitness for a particular purpose;
+
+ii) effectively excludes on behalf of all Contributors all liability for
+damages, including direct, indirect, special, incidental and consequential
+damages, such as lost profits;
+
+iii) states that any provisions which differ from this Agreement are offered by
+that Contributor alone and not by any other party; and
+
+iv) states that source code for the Program is available from such Contributor,
+and informs licensees how to obtain it in a reasonable manner on or through a
+medium customarily used for software exchange.
+
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+
+b) a copy of this Agreement must be included with each copy of the Program.
+
+Contributors may not remove or alter any copyright notices contained within the
+Program.
+
+Each Contributor must identify itself as the originator of its Contribution, if
+any, in a manner that reasonably allows subsequent Recipients to identify the
+originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities with
+respect to end users, business partners and the like. While this license is
+intended to facilitate the commercial use of the Program, the Contributor who
+includes the Program in a commercial product offering should do so in a manner
+which does not create potential liability for other Contributors. Therefore, if
+a Contributor includes the Program in a commercial product offering, such
+Contributor ("Commercial Contributor") hereby agrees to defend and indemnify
+every other Contributor ("Indemnified Contributor") against any losses, damages
+and costs (collectively "Losses") arising from claims, lawsuits and other legal
+actions brought by a third party against the Indemnified Contributor to the
+extent caused by the acts or omissions of such Commercial Contributor in
+connection with its distribution of the Program in a commercial product
+offering. The obligations in this section do not apply to any claims or Losses
+relating to any actual or alleged intellectual property infringement. In order
+to qualify, an Indemnified Contributor must: a) promptly notify the Commercial
+Contributor in writing of such claim, and b) allow the Commercial Contributor
+to control, and cooperate with the Commercial Contributor in, the defense and
+any related settlement negotiations. The Indemnified Contributor may
+participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial product
+offering, Product X. That Contributor is then a Commercial Contributor. If that
+Commercial Contributor then makes performance claims, or offers warranties
+related to Product X, those performance claims and warranties are such
+Commercial Contributor's responsibility alone. Under this section, the
+Commercial Contributor would have to defend claims against the other
+Contributors related to those performance claims and warranties, and if a court
+requires any other Contributor to pay any damages as a result, the Commercial
+Contributor must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
+IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each
+Recipient is solely responsible for determining the appropriateness of using
+and distributing the Program and assumes all risks associated with its
+exercise of rights under this Agreement, including but not limited to the
+risks and costs of program errors, compliance with applicable laws, damage to
+or loss of data, programs or equipment, and unavailability or interruption of
+operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
+CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION
+LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY
+RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under applicable
+law, it shall not affect the validity or enforceability of the remainder of the
+terms of this Agreement, and without further action by the parties hereto, such
+provision shall be reformed to the minimum extent necessary to make such
+provision valid and enforceable.
+
+If Recipient institutes patent litigation against a Contributor with respect to
+a patent applicable to software (including a cross-claim or counterclaim in a
+lawsuit), then any patent licenses granted by that Contributor to such
+Recipient under this Agreement shall terminate as of the date such litigation
+is filed. In addition, if Recipient institutes patent litigation against any
+entity (including a cross-claim or counterclaim in a lawsuit) alleging that the
+Program itself (excluding combinations of the Program with other software or
+hardware) infringes such Recipient's patent(s), then such Recipient's rights
+granted under Section 2(b) shall terminate as of the date such litigation is
+filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails to
+comply with any of the material terms or conditions of this Agreement and does
+not cure such failure in a reasonable period of time after becoming aware of
+such noncompliance. If all Recipient's rights under this Agreement terminate,
+Recipient agrees to cease use and distribution of the Program as soon as
+reasonably practicable. However, Recipient's obligations under this Agreement
+and any licenses granted by Recipient relating to the Program shall continue
+and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement, but in
+order to avoid inconsistency the Agreement is copyrighted and may only be
+modified in the following manner. The Agreement Steward reserves the right to
+publish new versions (including revisions) of this Agreement from time to time.
+No one other than the Agreement Steward has the right to modify this Agreement.
+IBM is the initial Agreement Steward. IBM may assign the responsibility to
+serve as the Agreement Steward to a suitable separate entity. Each new version
+of the Agreement will be given a distinguishing version number. The Program
+(including Contributions) may always be distributed subject to the version of
+the Agreement under which it was received. In addition, after a new version of
+the Agreement is published, Contributor may elect to distribute the Program
+(including its Contributions) under the new version. Except as expressly stated
+in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to
+the intellectual property of any Contributor under this Agreement, whether
+expressly, by implication, estoppel or otherwise. All rights in the Program not
+expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the
+intellectual property laws of the United States of America. No party to this
+Agreement will bring a legal action under this Agreement more than one year
+after the cause of action arose. Each party waives its rights to a jury trial
+in any resulting litigation.
+
+================================================================================
+Eclipse Public License - v 2.0
+
+ THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+ PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
+ OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+ a) in the case of the initial Contributor, the initial content
+ Distributed under this Agreement, and
+
+ b) in the case of each subsequent Contributor:
+ i) changes to the Program, and
+ ii) additions to the Program;
+ where such changes and/or additions to the Program originate from
+ and are Distributed by that particular Contributor. A Contribution
+ "originates" from a Contributor if it was added to the Program by
+ such Contributor itself or anyone acting on such Contributor's behalf.
+ Contributions do not include changes or additions to the Program that
+ are not Modified Works.
+
+"Contributor" means any person or entity that Distributes the Program.
+
+"Licensed Patents" mean patent claims licensable by a Contributor which
+are necessarily infringed by the use or sale of its Contribution alone
+or when combined with the Program.
+
+"Program" means the Contributions Distributed in accordance with this
+Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement
+or any Secondary License (as applicable), including Contributors.
+
+"Derivative Works" shall mean any work, whether in Source Code or other
+form, that is based on (or derived from) the Program and for which the
+editorial revisions, annotations, elaborations, or other modifications
+represent, as a whole, an original work of authorship.
+
+"Modified Works" shall mean any work in Source Code or other form that
+results from an addition to, deletion from, or modification of the
+contents of the Program, including, for purposes of clarity any new file
+in Source Code form that contains any contents of the Program. Modified
+Works shall not include works that contain only declarations,
+interfaces, types, classes, structures, or files of the Program solely
+in each case in order to link to, bind by name, or subclass the Program
+or Modified Works thereof.
+
+"Distribute" means the acts of a) distributing or b) making available
+in any manner that enables the transfer of a copy.
+
+"Source Code" means the form of a Program preferred for making
+modifications, including but not limited to software source code,
+documentation source, and configuration files.
+
+"Secondary License" means either the GNU General Public License,
+Version 2.0, or any later versions of that license, including any
+exceptions or additional permissions as identified by the initial
+Contributor.
+
+2. GRANT OF RIGHTS
+
+ a) Subject to the terms of this Agreement, each Contributor hereby
+ grants Recipient a non-exclusive, worldwide, royalty-free copyright
+ license to reproduce, prepare Derivative Works of, publicly display,
+ publicly perform, Distribute and sublicense the Contribution of such
+ Contributor, if any, and such Derivative Works.
+
+ b) Subject to the terms of this Agreement, each Contributor hereby
+ grants Recipient a non-exclusive, worldwide, royalty-free patent
+ license under Licensed Patents to make, use, sell, offer to sell,
+ import and otherwise transfer the Contribution of such Contributor,
+ if any, in Source Code or other form. This patent license shall
+ apply to the combination of the Contribution and the Program if, at
+ the time the Contribution is added by the Contributor, such addition
+ of the Contribution causes such combination to be covered by the
+ Licensed Patents. The patent license shall not apply to any other
+ combinations which include the Contribution. No hardware per se is
+ licensed hereunder.
+
+ c) Recipient understands that although each Contributor grants the
+ licenses to its Contributions set forth herein, no assurances are
+ provided by any Contributor that the Program does not infringe the
+ patent or other intellectual property rights of any other entity.
+ Each Contributor disclaims any liability to Recipient for claims
+ brought by any other entity based on infringement of intellectual
+ property rights or otherwise. As a condition to exercising the
+ rights and licenses granted hereunder, each Recipient hereby
+ assumes sole responsibility to secure any other intellectual
+ property rights needed, if any. For example, if a third party
+ patent license is required to allow Recipient to Distribute the
+ Program, it is Recipient's responsibility to acquire that license
+ before distributing the Program.
+
+ d) Each Contributor represents that to its knowledge it has
+ sufficient copyright rights in its Contribution, if any, to grant
+ the copyright license set forth in this Agreement.
+
+ e) Notwithstanding the terms of any Secondary License, no
+ Contributor makes additional grants to any Recipient (other than
+ those set forth in this Agreement) as a result of such Recipient's
+ receipt of the Program under the terms of a Secondary License
+ (if permitted under the terms of Section 3).
+
+3. REQUIREMENTS
+
+3.1 If a Contributor Distributes the Program in any form, then:
+
+ a) the Program must also be made available as Source Code, in
+ accordance with section 3.2, and the Contributor must accompany
+ the Program with a statement that the Source Code for the Program
+ is available under this Agreement, and informs Recipients how to
+ obtain it in a reasonable manner on or through a medium customarily
+ used for software exchange; and
+
+ b) the Contributor may Distribute the Program under a license
+ different than this Agreement, provided that such license:
+ i) effectively disclaims on behalf of all other Contributors all
+ warranties and conditions, express and implied, including
+ warranties or conditions of title and non-infringement, and
+ implied warranties or conditions of merchantability and fitness
+ for a particular purpose;
+
+ ii) effectively excludes on behalf of all other Contributors all
+ liability for damages, including direct, indirect, special,
+ incidental and consequential damages, such as lost profits;
+
+ iii) does not attempt to limit or alter the recipients' rights
+ in the Source Code under section 3.2; and
+
+ iv) requires any subsequent distribution of the Program by any
+ party to be under a license that satisfies the requirements
+ of this section 3.
+
+3.2 When the Program is Distributed as Source Code:
+
+ a) it must be made available under this Agreement, or if the
+ Program (i) is combined with other material in a separate file or
+ files made available under a Secondary License, and (ii) the initial
+ Contributor attached to the Source Code the notice described in
+ Exhibit A of this Agreement, then the Program may be made available
+ under the terms of such Secondary Licenses, and
+
+ b) a copy of this Agreement must be included with each copy of
+ the Program.
+
+3.3 Contributors may not remove or alter any copyright, patent,
+trademark, attribution notices, disclaimers of warranty, or limitations
+of liability ("notices") contained within the Program from any copy of
+the Program which they Distribute, provided that Contributors may add
+their own appropriate notices.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities
+with respect to end users, business partners and the like. While this
+license is intended to facilitate the commercial use of the Program,
+the Contributor who includes the Program in a commercial product
+offering should do so in a manner which does not create potential
+liability for other Contributors. Therefore, if a Contributor includes
+the Program in a commercial product offering, such Contributor
+("Commercial Contributor") hereby agrees to defend and indemnify every
+other Contributor ("Indemnified Contributor") against any losses,
+damages and costs (collectively "Losses") arising from claims, lawsuits
+and other legal actions brought by a third party against the Indemnified
+Contributor to the extent caused by the acts or omissions of such
+Commercial Contributor in connection with its distribution of the Program
+in a commercial product offering. The obligations in this section do not
+apply to any claims or Losses relating to any actual or alleged
+intellectual property infringement. In order to qualify, an Indemnified
+Contributor must: a) promptly notify the Commercial Contributor in
+writing of such claim, and b) allow the Commercial Contributor to control,
+and cooperate with the Commercial Contributor in, the defense and any
+related settlement negotiations. The Indemnified Contributor may
+participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial
+product offering, Product X. That Contributor is then a Commercial
+Contributor. If that Commercial Contributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Contributor's responsibility
+alone. Under this section, the Commercial Contributor would have to
+defend claims against the other Contributors related to those performance
+claims and warranties, and if a court requires any other Contributor to
+pay any damages as a result, the Commercial Contributor must pay
+those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
+PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"
+BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
+IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF
+TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
+PURPOSE. Each Recipient is solely responsible for determining the
+appropriateness of using and distributing the Program and assumes all
+risks associated with its exercise of rights under this Agreement,
+including but not limited to the risks and costs of program errors,
+compliance with applicable laws, damage to or loss of data, programs
+or equipment, and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
+PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
+SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
+PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
+EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further
+action by the parties hereto, such provision shall be reformed to the
+minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity
+(including a cross-claim or counterclaim in a lawsuit) alleging that the
+Program itself (excluding combinations of the Program with other software
+or hardware) infringes such Recipient's patent(s), then such Recipient's
+rights granted under Section 2(b) shall terminate as of the date such
+litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it
+fails to comply with any of the material terms or conditions of this
+Agreement and does not cure such failure in a reasonable period of
+time after becoming aware of such noncompliance. If all Recipient's
+rights under this Agreement terminate, Recipient agrees to cease use
+and distribution of the Program as soon as reasonably practicable.
+However, Recipient's obligations under this Agreement and any licenses
+granted by Recipient relating to the Program shall continue and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement,
+but in order to avoid inconsistency the Agreement is copyrighted and
+may only be modified in the following manner. The Agreement Steward
+reserves the right to publish new versions (including revisions) of
+this Agreement from time to time. No one other than the Agreement
+Steward has the right to modify this Agreement. The Eclipse Foundation
+is the initial Agreement Steward. The Eclipse Foundation may assign the
+responsibility to serve as the Agreement Steward to a suitable separate
+entity. Each new version of the Agreement will be given a distinguishing
+version number. The Program (including Contributions) may always be
+Distributed subject to the version of the Agreement under which it was
+received. In addition, after a new version of the Agreement is published,
+Contributor may elect to Distribute the Program (including its
+Contributions) under the new version.
+
+Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
+receives no rights or licenses to the intellectual property of any
+Contributor under this Agreement, whether expressly, by implication,
+estoppel or otherwise. All rights in the Program not expressly granted
+under this Agreement are reserved. Nothing in this Agreement is intended
+to be enforceable by any entity that is not a Contributor or Recipient.
+No third-party beneficiary rights are created under this Agreement.
+
+Exhibit A - Form of Secondary Licenses Notice
+
+"This Source Code may also be made available under the following
+Secondary Licenses when the conditions for such availability set forth
+in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
+version(s), and exceptions or additional permissions here}."
+
+ Simply including a copy of this Agreement, including this Exhibit A
+ is not sufficient to license the Source Code under Secondary Licenses.
+
+ If it is not possible or desirable to put the notice in a particular
+ file, then You may include the notice in a location (such as a LICENSE
+ file in a relevant directory) where a recipient would be likely to
+ look for such a notice.
+
+ You may add additional accurate notices of copyright ownership.
diff --git a/api/pom.xml b/api/pom.xml
index 71190839..eef37b0c 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -1,4 +1,22 @@
+
+
@@ -9,18 +27,26 @@
4.0.0
-
- UTF-8
-
-
+ apiapi
+
+
+ junit
+ junit
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+ org.apache.maven.pluginsmaven-javadoc-plugin
- 3.0.0-M1attach-javadocs
@@ -30,6 +56,22 @@
+
+ org.netbeans.tools
+ sigtest-maven-plugin
+
+
+
+ check
+
+
+
+
+ src/main/api/snapshot.sigfile
+ strictcheck
+ com.fnproject.fn.api,com.fnproject.fn.api.exception
+
+
diff --git a/api/src/main/api/snapshot.sigfile b/api/src/main/api/snapshot.sigfile
new file mode 100644
index 00000000..096b2dee
--- /dev/null
+++ b/api/src/main/api/snapshot.sigfile
@@ -0,0 +1,320 @@
+#Signature file v4.1
+#Version 1.0.0-SNAPSHOT
+
+CLSS public abstract interface !annotation com.fnproject.fn.api.FnConfiguration
+ anno 0 java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy value=RUNTIME)
+ anno 0 java.lang.annotation.Target(java.lang.annotation.ElementType[] value=[METHOD])
+intf java.lang.annotation.Annotation
+
+CLSS public abstract interface !annotation com.fnproject.fn.api.FnFeature
+ anno 0 java.lang.annotation.Repeatable(java.lang.Class extends java.lang.annotation.Annotation> value=class com.fnproject.fn.api.FnFeatures)
+ anno 0 java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy value=RUNTIME)
+ anno 0 java.lang.annotation.Target(java.lang.annotation.ElementType[] value=[TYPE])
+intf java.lang.annotation.Annotation
+meth public abstract java.lang.Class extends com.fnproject.fn.api.RuntimeFeature> value()
+
+CLSS public abstract interface !annotation com.fnproject.fn.api.FnFeatures
+ anno 0 java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy value=RUNTIME)
+ anno 0 java.lang.annotation.Target(java.lang.annotation.ElementType[] value=[TYPE])
+intf java.lang.annotation.Annotation
+meth public abstract com.fnproject.fn.api.FnFeature[] value()
+
+CLSS public abstract interface com.fnproject.fn.api.FunctionInvoker
+innr public final static !enum Phase
+meth public abstract java.util.Optional tryInvoke(com.fnproject.fn.api.InvocationContext,com.fnproject.fn.api.InputEvent)
+
+CLSS public final static !enum com.fnproject.fn.api.FunctionInvoker$Phase
+ outer com.fnproject.fn.api.FunctionInvoker
+fld public final static com.fnproject.fn.api.FunctionInvoker$Phase Call
+fld public final static com.fnproject.fn.api.FunctionInvoker$Phase PreCall
+meth public static com.fnproject.fn.api.FunctionInvoker$Phase valueOf(java.lang.String)
+meth public static com.fnproject.fn.api.FunctionInvoker$Phase[] values()
+supr java.lang.Enum
+
+CLSS public final com.fnproject.fn.api.Headers
+intf java.io.Serializable
+meth public java.util.Map getAll()
+meth public !varargs com.fnproject.fn.api.Headers addHeader(java.lang.String,java.lang.String,java.lang.String[])
+meth public !varargs com.fnproject.fn.api.Headers setHeader(java.lang.String,java.lang.String,java.lang.String[])
+meth public boolean equals(java.lang.Object)
+meth public com.fnproject.fn.api.Headers removeHeader(java.lang.String)
+meth public com.fnproject.fn.api.Headers setHeader(java.lang.String,java.util.Collection)
+meth public com.fnproject.fn.api.Headers setHeaders(java.util.Map>)
+meth public int hashCode()
+meth public java.lang.String toString()
+meth public java.util.Collection keys()
+meth public java.util.List getAllValues(java.lang.String)
+meth public java.util.Map> asMap()
+meth public java.util.Optional get(java.lang.String)
+meth public static com.fnproject.fn.api.Headers emptyHeaders()
+meth public static com.fnproject.fn.api.Headers fromMap(java.util.Map)
+meth public static com.fnproject.fn.api.Headers fromMultiHeaderMap(java.util.Map>)
+meth public static java.lang.String canonicalKey(java.lang.String)
+supr java.lang.Object
+hfds emptyHeaders,headerName,headers
+
+CLSS public abstract interface !annotation com.fnproject.fn.api.InputBinding
+ anno 0 java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy value=RUNTIME)
+ anno 0 java.lang.annotation.Target(java.lang.annotation.ElementType[] value=[PARAMETER])
+intf java.lang.annotation.Annotation
+meth public abstract java.lang.Class extends com.fnproject.fn.api.InputCoercion> coercion()
+
+CLSS public abstract interface com.fnproject.fn.api.InputCoercion<%0 extends java.lang.Object>
+meth public abstract java.util.Optional<{com.fnproject.fn.api.InputCoercion%0}> tryCoerceParam(com.fnproject.fn.api.InvocationContext,int,com.fnproject.fn.api.InputEvent,com.fnproject.fn.api.MethodWrapper)
+
+CLSS public abstract interface com.fnproject.fn.api.InputEvent
+intf java.io.Closeable
+meth public abstract <%0 extends java.lang.Object> {%%0} consumeBody(java.util.function.Function)
+meth public abstract com.fnproject.fn.api.Headers getHeaders()
+meth public abstract java.lang.String getCallID()
+meth public abstract java.time.Instant getDeadline()
+
+CLSS public abstract interface com.fnproject.fn.api.InvocationContext
+meth public abstract !varargs void setResponseHeader(java.lang.String,java.lang.String,java.lang.String[])
+meth public abstract com.fnproject.fn.api.Headers getRequestHeaders()
+meth public abstract com.fnproject.fn.api.RuntimeContext getRuntimeContext()
+meth public abstract void addListener(com.fnproject.fn.api.InvocationListener)
+meth public abstract void addResponseHeader(java.lang.String,java.lang.String)
+meth public void setResponseContentType(java.lang.String)
+
+CLSS public abstract interface com.fnproject.fn.api.InvocationListener
+intf java.util.EventListener
+meth public abstract void onFailure()
+meth public abstract void onSuccess()
+
+CLSS public abstract interface com.fnproject.fn.api.MethodWrapper
+meth public abstract com.fnproject.fn.api.TypeWrapper getParamType(int)
+meth public abstract com.fnproject.fn.api.TypeWrapper getReturnType()
+meth public abstract java.lang.Class> getTargetClass()
+meth public abstract java.lang.reflect.Method getTargetMethod()
+meth public int getParameterCount()
+meth public java.lang.String getLongName()
+
+CLSS public abstract interface !annotation com.fnproject.fn.api.OutputBinding
+ anno 0 java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy value=RUNTIME)
+ anno 0 java.lang.annotation.Target(java.lang.annotation.ElementType[] value=[METHOD])
+intf java.lang.annotation.Annotation
+meth public abstract java.lang.Class extends com.fnproject.fn.api.OutputCoercion> coercion()
+
+CLSS public abstract interface com.fnproject.fn.api.OutputCoercion
+meth public abstract java.util.Optional wrapFunctionResult(com.fnproject.fn.api.InvocationContext,com.fnproject.fn.api.MethodWrapper,java.lang.Object)
+
+CLSS public abstract interface com.fnproject.fn.api.OutputEvent
+fld public final static java.lang.String CONTENT_TYPE_HEADER = "Content-Type"
+innr public final static !enum Status
+meth public abstract com.fnproject.fn.api.Headers getHeaders()
+meth public abstract com.fnproject.fn.api.OutputEvent$Status getStatus()
+meth public abstract void writeToOutput(java.io.OutputStream) throws java.io.IOException
+meth public boolean isSuccess()
+meth public com.fnproject.fn.api.OutputEvent withHeaders(com.fnproject.fn.api.Headers)
+meth public java.util.Optional getContentType()
+meth public static com.fnproject.fn.api.OutputEvent emptyResult(com.fnproject.fn.api.OutputEvent$Status)
+meth public static com.fnproject.fn.api.OutputEvent fromBytes(byte[],com.fnproject.fn.api.OutputEvent$Status,java.lang.String)
+meth public static com.fnproject.fn.api.OutputEvent fromBytes(byte[],com.fnproject.fn.api.OutputEvent$Status,java.lang.String,com.fnproject.fn.api.Headers)
+
+CLSS public final static !enum com.fnproject.fn.api.OutputEvent$Status
+ outer com.fnproject.fn.api.OutputEvent
+fld public final static com.fnproject.fn.api.OutputEvent$Status FunctionError
+fld public final static com.fnproject.fn.api.OutputEvent$Status FunctionTimeout
+fld public final static com.fnproject.fn.api.OutputEvent$Status InternalError
+fld public final static com.fnproject.fn.api.OutputEvent$Status Success
+meth public int getCode()
+meth public static com.fnproject.fn.api.OutputEvent$Status valueOf(java.lang.String)
+meth public static com.fnproject.fn.api.OutputEvent$Status[] values()
+supr java.lang.Enum
+hfds code
+
+CLSS public abstract interface com.fnproject.fn.api.QueryParameters
+meth public abstract java.util.List getValues(java.lang.String)
+meth public abstract java.util.Map> getAll()
+meth public abstract java.util.Optional get(java.lang.String)
+
+CLSS public abstract interface com.fnproject.fn.api.RuntimeContext
+meth public abstract <%0 extends java.lang.Object> java.util.Optional<{%%0}> getAttribute(java.lang.String,java.lang.Class<{%%0}>)
+meth public abstract com.fnproject.fn.api.MethodWrapper getMethod()
+meth public abstract java.lang.String getAppID()
+meth public abstract java.lang.String getFunctionID()
+meth public java.lang.String getAppName()
+meth public java.lang.String getFunctionName()
+meth public abstract java.util.List getInputCoercions(com.fnproject.fn.api.MethodWrapper,int)
+meth public abstract java.util.List getOutputCoercions(java.lang.reflect.Method)
+meth public abstract java.util.Map getConfiguration()
+meth public abstract java.util.Optional getInvokeInstance()
+meth public abstract java.util.Optional getConfigurationByKey(java.lang.String)
+meth public abstract void addInputCoercion(com.fnproject.fn.api.InputCoercion)
+meth public abstract void addInvoker(com.fnproject.fn.api.FunctionInvoker,com.fnproject.fn.api.FunctionInvoker$Phase)
+meth public abstract void addOutputCoercion(com.fnproject.fn.api.OutputCoercion)
+meth public abstract void setAttribute(java.lang.String,java.lang.Object)
+meth public void setInvoker(com.fnproject.fn.api.FunctionInvoker)
+
+CLSS public abstract interface com.fnproject.fn.api.RuntimeFeature
+meth public abstract void initialize(com.fnproject.fn.api.RuntimeContext)
+
+CLSS public abstract interface com.fnproject.fn.api.TypeWrapper
+meth public abstract java.lang.Class> getParameterClass()
+
+CLSS public com.fnproject.fn.api.exception.FunctionConfigurationException
+cons public init(java.lang.String)
+cons public init(java.lang.String,java.lang.Throwable)
+supr com.fnproject.fn.api.exception.FunctionLoadException
+
+CLSS public com.fnproject.fn.api.exception.FunctionInputHandlingException
+cons public init(java.lang.String)
+cons public init(java.lang.String,java.lang.Throwable)
+supr java.lang.RuntimeException
+
+CLSS public abstract com.fnproject.fn.api.exception.FunctionLoadException
+cons public init(java.lang.String)
+cons public init(java.lang.String,java.lang.Throwable)
+supr java.lang.RuntimeException
+
+CLSS public com.fnproject.fn.api.exception.FunctionOutputHandlingException
+cons public init(java.lang.String)
+cons public init(java.lang.String,java.lang.Exception)
+supr java.lang.RuntimeException
+
+
+CLSS public abstract interface com.fnproject.fn.api.httpgateway.HTTPGatewayContext
+meth public abstract InvocationContext getInvocationContext()
+meth public abstract Headers getHeaders()
+meth public abstract String getRequestURL()
+meth public abstract String getMethod()
+meth public abstract QueryParameters getQueryParameters()
+meth public abstract void addResponseHeader(String key, String value)
+meth public abstract void setResponseHeader(String key, String v1, String... vs)
+meth public abstract void setStatusCode(int code)
+
+CLSS public abstract interface com.fnproject.fn.api.tracing.TracingContext
+meth public abstract InvocationContext getInvocationContext()
+meth public abstract RuntimeContext getRuntimeContext()
+meth public abstract java.lang.String getServiceName()
+meth public abstract java.lang.String getTraceCollectorURL()
+meth public abstract java.lang.String getTraceId()
+meth public abstract java.lang.String getSpanId()
+meth public abstract java.lang.String getParentSpanId()
+meth public abstract java.lang.Boolean isSampled()
+meth public abstract java.lang.String getFlags()
+meth public abstract java.lang.Boolean isTracingEnabled()
+meth public abstract java.lang.String getAppName()
+meth public abstract java.lang.String getFunctionName()
+
+CLSS public abstract interface java.io.Closeable
+intf java.lang.AutoCloseable
+meth public abstract void close() throws java.io.IOException
+
+CLSS public abstract interface java.io.Serializable
+
+CLSS public abstract interface java.lang.AutoCloseable
+meth public abstract void close() throws java.lang.Exception
+
+CLSS public abstract interface java.lang.Comparable<%0 extends java.lang.Object>
+meth public abstract int compareTo({java.lang.Comparable%0})
+
+CLSS public abstract java.lang.Enum<%0 extends java.lang.Enum<{java.lang.Enum%0}>>
+cons protected init(java.lang.String,int)
+intf java.io.Serializable
+intf java.lang.Comparable<{java.lang.Enum%0}>
+meth protected final java.lang.Object clone() throws java.lang.CloneNotSupportedException
+meth protected final void finalize()
+meth public final boolean equals(java.lang.Object)
+meth public final int compareTo({java.lang.Enum%0})
+meth public final int hashCode()
+meth public final int ordinal()
+meth public final java.lang.Class<{java.lang.Enum%0}> getDeclaringClass()
+meth public final java.lang.String name()
+meth public java.lang.String toString()
+meth public static <%0 extends java.lang.Enum<{%%0}>> {%%0} valueOf(java.lang.Class<{%%0}>,java.lang.String)
+supr java.lang.Object
+hfds name,ordinal
+
+CLSS public java.lang.Exception
+cons protected init(java.lang.String,java.lang.Throwable,boolean,boolean)
+cons public init()
+cons public init(java.lang.String)
+cons public init(java.lang.String,java.lang.Throwable)
+cons public init(java.lang.Throwable)
+supr java.lang.Throwable
+hfds serialVersionUID
+
+CLSS public java.lang.Object
+cons public init()
+meth protected java.lang.Object clone() throws java.lang.CloneNotSupportedException
+meth protected void finalize() throws java.lang.Throwable
+meth public boolean equals(java.lang.Object)
+meth public final java.lang.Class> getClass()
+meth public final void notify()
+meth public final void notifyAll()
+meth public final void wait() throws java.lang.InterruptedException
+meth public final void wait(long) throws java.lang.InterruptedException
+meth public final void wait(long,int) throws java.lang.InterruptedException
+meth public int hashCode()
+meth public java.lang.String toString()
+
+CLSS public java.lang.RuntimeException
+cons protected init(java.lang.String,java.lang.Throwable,boolean,boolean)
+cons public init()
+cons public init(java.lang.String)
+cons public init(java.lang.String,java.lang.Throwable)
+cons public init(java.lang.Throwable)
+supr java.lang.Exception
+hfds serialVersionUID
+
+CLSS public java.lang.Throwable
+cons protected init(java.lang.String,java.lang.Throwable,boolean,boolean)
+cons public init()
+cons public init(java.lang.String)
+cons public init(java.lang.String,java.lang.Throwable)
+cons public init(java.lang.Throwable)
+intf java.io.Serializable
+meth public final java.lang.Throwable[] getSuppressed()
+meth public final void addSuppressed(java.lang.Throwable)
+meth public java.lang.StackTraceElement[] getStackTrace()
+meth public java.lang.String getLocalizedMessage()
+meth public java.lang.String getMessage()
+meth public java.lang.String toString()
+meth public java.lang.Throwable fillInStackTrace()
+meth public java.lang.Throwable getCause()
+meth public java.lang.Throwable initCause(java.lang.Throwable)
+meth public void printStackTrace()
+meth public void printStackTrace(java.io.PrintStream)
+meth public void printStackTrace(java.io.PrintWriter)
+meth public void setStackTrace(java.lang.StackTraceElement[])
+supr java.lang.Object
+hfds CAUSE_CAPTION,EMPTY_THROWABLE_ARRAY,NULL_CAUSE_MESSAGE,SELF_SUPPRESSION_MESSAGE,SUPPRESSED_CAPTION,SUPPRESSED_SENTINEL,UNASSIGNED_STACK,backtrace,cause,detailMessage,serialVersionUID,stackTrace,suppressedExceptions
+hcls PrintStreamOrWriter,SentinelHolder,WrappedPrintStream,WrappedPrintWriter
+
+CLSS public abstract interface java.lang.annotation.Annotation
+meth public abstract boolean equals(java.lang.Object)
+meth public abstract int hashCode()
+meth public abstract java.lang.Class extends java.lang.annotation.Annotation> annotationType()
+meth public abstract java.lang.String toString()
+
+CLSS public abstract interface !annotation java.lang.annotation.Documented
+ anno 0 java.lang.annotation.Documented()
+ anno 0 java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy value=RUNTIME)
+ anno 0 java.lang.annotation.Target(java.lang.annotation.ElementType[] value=[ANNOTATION_TYPE])
+intf java.lang.annotation.Annotation
+
+CLSS public abstract interface !annotation java.lang.annotation.Repeatable
+ anno 0 java.lang.annotation.Documented()
+ anno 0 java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy value=RUNTIME)
+ anno 0 java.lang.annotation.Target(java.lang.annotation.ElementType[] value=[ANNOTATION_TYPE])
+intf java.lang.annotation.Annotation
+meth public abstract java.lang.Class extends java.lang.annotation.Annotation> value()
+
+CLSS public abstract interface !annotation java.lang.annotation.Retention
+ anno 0 java.lang.annotation.Documented()
+ anno 0 java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy value=RUNTIME)
+ anno 0 java.lang.annotation.Target(java.lang.annotation.ElementType[] value=[ANNOTATION_TYPE])
+intf java.lang.annotation.Annotation
+meth public abstract java.lang.annotation.RetentionPolicy value()
+
+CLSS public abstract interface !annotation java.lang.annotation.Target
+ anno 0 java.lang.annotation.Documented()
+ anno 0 java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy value=RUNTIME)
+ anno 0 java.lang.annotation.Target(java.lang.annotation.ElementType[] value=[ANNOTATION_TYPE])
+intf java.lang.annotation.Annotation
+meth public abstract java.lang.annotation.ElementType[] value()
+
+CLSS public abstract interface java.util.EventListener
+
diff --git a/api/src/main/java/com/fnproject/fn/api/FnConfiguration.java b/api/src/main/java/com/fnproject/fn/api/FnConfiguration.java
index 62a70a13..52273461 100644
--- a/api/src/main/java/com/fnproject/fn/api/FnConfiguration.java
+++ b/api/src/main/java/com/fnproject/fn/api/FnConfiguration.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api;
import java.lang.annotation.ElementType;
diff --git a/api/src/main/java/com/fnproject/fn/api/FnFeature.java b/api/src/main/java/com/fnproject/fn/api/FnFeature.java
new file mode 100644
index 00000000..6b9165be
--- /dev/null
+++ b/api/src/main/java/com/fnproject/fn/api/FnFeature.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.api;
+
+import java.lang.annotation.*;
+
+/**
+ * Annotation to be used in user function classes to enable runtime-wide feature.
+ *
+ * Runtime features are initialized at the point that the function class is loaded but prior to the call chain.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Repeatable(FnFeatures.class)
+public @interface FnFeature {
+ /**
+ * The feature class to load this must have a zero-arg public constructor
+ * @return feature class
+ */
+ Class extends RuntimeFeature> value();
+}
diff --git a/api/src/main/java/com/fnproject/fn/api/FnFeatures.java b/api/src/main/java/com/fnproject/fn/api/FnFeatures.java
new file mode 100644
index 00000000..edb021f0
--- /dev/null
+++ b/api/src/main/java/com/fnproject/fn/api/FnFeatures.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to be used in user function classes to enable runtime-wide feature.
+ *
+ * Runtime features are initialized at the point that the function class is loaded but prior to the call chain.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface FnFeatures {
+ FnFeature[] value();
+}
diff --git a/api/src/main/java/com/fnproject/fn/api/FunctionInvoker.java b/api/src/main/java/com/fnproject/fn/api/FunctionInvoker.java
index 7f1fa29a..691e6a90 100644
--- a/api/src/main/java/com/fnproject/fn/api/FunctionInvoker.java
+++ b/api/src/main/java/com/fnproject/fn/api/FunctionInvoker.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api;
import java.util.Optional;
@@ -6,6 +22,22 @@
* Handles the invocation of a given function call
*/
public interface FunctionInvoker {
+ /**
+ * Phase determines a loose ordering for invocation handler processing
+ * this should be used with {@link RuntimeContext#addInvoker(FunctionInvoker, Phase)} to add new invoke handlers to a runtime
+ */
+ enum Phase {
+ /**
+ * The Pre-Call phase runs before the main function call, all {@link FunctionInvoker} handlers added at this phase are tried prior to calling the {@link Phase#Call} phase
+ * This phase is typically used for handlers that /may/ intercept the request based on request attributes
+ */
+ PreCall,
+ /**
+ * The Call Phase indicates invokers that should handle call values - typically a given runtime will only be handled by one of these
+ */
+ Call
+ }
+
/**
* Optionally handles an invocation chain for this function
*
diff --git a/api/src/main/java/com/fnproject/fn/api/Headers.java b/api/src/main/java/com/fnproject/fn/api/Headers.java
index 2e069c03..52618d2c 100644
--- a/api/src/main/java/com/fnproject/fn/api/Headers.java
+++ b/api/src/main/java/com/fnproject/fn/api/Headers.java
@@ -1,16 +1,78 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api;
+import java.io.Serializable;
import java.util.*;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
/**
- * Represents the headers on an HTTP request or response. Multiple headers with the same key are collapsed into a single
- * entry where the values are concatenated by commas as per the HTTP spec (RFC 7230).
+ * Represents a set of String-String[] header attributes, per HTTP headers.
+ *
+ * Internally header keys are always canonicalized using HTTP header conventions
+ *
+ * Headers objects are immutable
+ *
+ * Keys are are stored and compared in a case-insensitive way and are canonicalised according to RFC 7230 conventions such that :
+ *
+ *
+ *
a-header
+ *
A-Header
+ *
A-HeaDer
+ *
+ * are all equivalent - keys are returned in the canonical form (lower cased except for leading characters)
+ * Where keys do not comply with HTTP header naming they are left as is.
*/
-public final class Headers {
- private Map headers;
+public final class Headers implements Serializable {
+ private static final Headers emptyHeaders = new Headers(Collections.emptyMap());
+ private Map> headers;
+
+ private Headers(Map> headersIn) {
+ this.headers = headersIn;
+ }
+
+ private static Pattern headerName = Pattern.compile("[A-Za-z0-9!#%&'*+-.^_`|~]+");
+
+ public Map getAll() {
+ return headers;
+ }
+
+ /**
+ * Calculates the canonical key (cf RFC 7230) for a header
+ *
+ * If the header contains invalid characters it returns the original header
+ *
+ * @param key the header key to canonicalise
+ * @return a canonical key or the original key if the input contains invalid character
+ */
+ public static String canonicalKey(String key) {
+ if (!headerName.matcher(key).matches()) {
+ return key;
+ }
+ String parts[] = key.split("-", -1);
+ for (int i = 0; i < parts.length; i++) {
+ String p = parts[i];
+ if (p.length() > 0) {
+ parts[i] = p.substring(0, 1).toUpperCase() + p.substring(1).toLowerCase();
+ }
+ }
+ return String.join("-", parts);
- private Headers(Map headers) {
- this.headers = headers;
}
/**
@@ -21,6 +83,23 @@ private Headers(Map headers) {
* @return {@code Headers} built from headers map
*/
public static Headers fromMap(Map headers) {
+ Objects.requireNonNull(headers, "headersIn");
+ Map> h = new HashMap<>();
+ headers.forEach((k, v) -> h.put(canonicalKey(k), Collections.singletonList(v)));
+ return new Headers(Collections.unmodifiableMap(new HashMap<>(h)));
+ }
+
+ /**
+ * Build a headers object from a map composed of (name, value) entries, we take a copy of the map and
+ * disallow any further modification
+ *
+ * @param headers underlying collection of header entries to copy
+ * @return {@code Headers} built from headers map
+ */
+ public static Headers fromMultiHeaderMap(Map> headers) {
+ Map> hm = new HashMap<>();
+
+ headers.forEach((k, vs) -> hm.put(canonicalKey(k), new ArrayList<>(vs)));
return new Headers(Collections.unmodifiableMap(new HashMap<>(Objects.requireNonNull(headers))));
}
@@ -30,43 +109,136 @@ public static Headers fromMap(Map headers) {
* @return empty headers
*/
public static Headers emptyHeaders() {
- return new Headers(Collections.emptyMap());
+ return emptyHeaders;
}
/**
- * Creates a new headers object with the specified header added
+ * Sets a map of headers, overwriting any headers in the current headers with the respective values
*
+ * @param vals a map of headers
+ * @return a new headers object with thos headers set
+ */
+ public Headers setHeaders(Map> vals) {
+ Objects.requireNonNull(vals, "vals");
+ Map> nm = new HashMap<>(headers);
+ vals.forEach((k, vs) -> {
+ vs.forEach(v -> Objects.requireNonNull(v, "header list contains null entries"));
+ nm.put(canonicalKey(k), vs);
+ });
+ return new Headers(nm);
+ }
+
+ /**
+ * Creates a new headers object with the specified header added - if a header with the same key existed it the new value is appended
+ *
* This will overwrite an existing header with an exact name match
+ *
* @param key new header key
- * @param value new header value
+ * @param v1 new header value
+ * @param vs additional header values to set
* @return a new headers object with the specified header added
*/
- public Headers withHeader(String key, String value){
- Map newHeaders = new HashMap<>();
- newHeaders.putAll(getAll());
- newHeaders.put(key,value);
- return new Headers(newHeaders);
+ public Headers addHeader(String key, String v1, String... vs) {
+ Objects.requireNonNull(key, "key");
+ Objects.requireNonNull(key, "value");
+
+ String canonKey = canonicalKey(key);
+
+ Map> nm = new HashMap<>(headers);
+ List current = nm.get(canonKey);
+
+ if (current == null) {
+ List s = new ArrayList<>();
+ s.add(v1);
+ s.addAll(Arrays.asList(vs));
+
+ nm.put(canonKey, Collections.unmodifiableList(s));
+ } else {
+ List s = new ArrayList<>(current);
+ s.add(v1);
+ s.addAll(Arrays.asList(vs));
+ nm.put(canonKey, Collections.unmodifiableList(s));
+ }
+ return new Headers(nm);
+
}
/**
- * Returns the header matching the specified key. This matches headers in a case-insensitive way and substitutes
- * underscore and hyphen characters such that : "CONTENT_TYPE" and "Content-type" are equivalent. If no matching
- * header is found then {@code Optional.empty} is returned.
+ * Creates a new headers object with the specified header set - this overwrites any existin values
*
- * Multiple headers are collapsed by {@code fn} into a single header entry delimited by commas (see
- * RFC7230 Sec 3.2.2 for details), for example
+ * This will overwrite an existing header with an exact name match
*
- *
- * Accept: text/html
- * Accept: text/plain
- *
+ * @param key new header key
+ * @param v1 new header value
+ * @param vs more header values to set
+ * @return a new headers object with the specified header added
+ */
+ public Headers setHeader(String key, String v1, String... vs) {
+ Objects.requireNonNull(key, "key");
+ Objects.requireNonNull(v1, "v1");
+ Stream.of(vs).forEach((v) -> Objects.requireNonNull(v, "vs"));
+
+ Map> nm = new HashMap<>(headers);
+ List s = new ArrayList<>();
+ s.add(v1);
+ s.addAll(Arrays.asList(vs));
+ nm.put(canonicalKey(key), Collections.unmodifiableList(s));
+ return new Headers(Collections.unmodifiableMap(nm));
+ }
+
+
+ /**
+ * Creates a new headers object with the specified headers set - this overwrites any existin values
+ *
+ * This will overwrite an existing header with an exact name match
*
- * is collapsed into
+ * @param key new header key
+ * @param vs header values to set
+ * @return a new headers object with the specified header added
+ */
+ public Headers setHeader(String key, Collection vs) {
+ Objects.requireNonNull(key, "key");
+ Objects.requireNonNull(vs, "vs");
+ if (vs.size() == 0) {
+ throw new IllegalArgumentException("can't set keys to an empty list");
+ }
+ vs.forEach((v) -> Objects.requireNonNull(v, "vs"));
+
+ Map> nm = new HashMap<>(headers);
+ nm.put(canonicalKey(key), Collections.unmodifiableList(new ArrayList<>(vs)));
+ return new Headers(Collections.unmodifiableMap(nm));
+
+ }
+
+ /**
+ * Creates a new headers object with the specified headers remove - this overwrites any existin values
+ *
+ * This will overwrite an existing header with an exact name match
*
- *
- * Accept: text/html, text/plain
- *
+ * @param key new header key
+ * @return a new headers object with the specified header removed
+ */
+ public Headers removeHeader(String key) {
+ Objects.requireNonNull(key, "key");
+
+ String canonKey = canonicalKey(key);
+ if (!headers.containsKey(canonKey)) {
+ return this;
+ }
+
+ Map> nm = new HashMap<>(headers);
+ nm.remove(canonKey);
+ return new Headers(Collections.unmodifiableMap(nm));
+
+ }
+
+ /**
+ * Returns the header matching the specified key. This matches headers in a case-insensitive way and substitutes
+ * underscore and hyphen characters such that : "CONTENT_TYPE_HEADER" and "Content-type" are equivalent. If no matching
+ * header is found then {@code Optional.empty} is returned.
+ *
+ * When multiple headers are present then the first value is returned- see { #getAllValues(String key)} to get all values for a header
*
* @param key match key
* @return a header matching key or empty if no header matches.
@@ -74,20 +246,60 @@ public Headers withHeader(String key, String value){
*/
public Optional get(String key) {
Objects.requireNonNull(key, "Key cannot be null");
- return getAll().entrySet().stream()
- .filter((e) -> e.getKey()
- .replaceAll("-", "_")
- .equalsIgnoreCase(key.replaceAll("-", "_")))
- .map(Map.Entry::getValue)
- .findFirst();
+ String canonKey = canonicalKey(key);
+
+ List val = headers.get(canonKey);
+ if (val == null){
+ return Optional.empty();
+ }
+ return Optional.of(val.get(0));
}
/**
- * The function invocation headers passed on the request
+ * Returns a collection of current header keys
*
- * @return a map of Invocation headers.
+ * @return a collection of keys
*/
- public Map getAll() {
+ public Collection keys() {
+ return headers.keySet();
+ }
+
+ /**
+ * Returns the headers as a map
+ *
+ * @return a map of key-values
+ */
+ public Map> asMap() {
return headers;
}
+
+ /**
+ * GetAllValues returns all values for a header or an empty list if the header has no values
+ * @param key the Header key
+ * @return a possibly empty list of values
+ */
+ public List getAllValues(String key) {
+ return headers.getOrDefault(canonicalKey(key), Collections.emptyList());
+ }
+
+ public int hashCode() {
+ return headers.hashCode();
+ }
+
+
+ public boolean equals(Object other) {
+ if (!(other instanceof Headers)) {
+ return false;
+ }
+ if (other == this) {
+ return true;
+ }
+ return headers.equals(((Headers) other).headers);
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toString(headers);
+ }
+
}
diff --git a/api/src/main/java/com/fnproject/fn/api/InputBinding.java b/api/src/main/java/com/fnproject/fn/api/InputBinding.java
index ddb2b009..a884cf88 100644
--- a/api/src/main/java/com/fnproject/fn/api/InputBinding.java
+++ b/api/src/main/java/com/fnproject/fn/api/InputBinding.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api;
import java.lang.annotation.ElementType;
diff --git a/api/src/main/java/com/fnproject/fn/api/InputCoercion.java b/api/src/main/java/com/fnproject/fn/api/InputCoercion.java
index f8e6af06..a5559105 100644
--- a/api/src/main/java/com/fnproject/fn/api/InputCoercion.java
+++ b/api/src/main/java/com/fnproject/fn/api/InputCoercion.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api;
import java.util.Optional;
diff --git a/api/src/main/java/com/fnproject/fn/api/InputEvent.java b/api/src/main/java/com/fnproject/fn/api/InputEvent.java
index 2ecab8aa..ef4e840a 100644
--- a/api/src/main/java/com/fnproject/fn/api/InputEvent.java
+++ b/api/src/main/java/com/fnproject/fn/api/InputEvent.java
@@ -1,7 +1,24 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api;
import java.io.Closeable;
import java.io.InputStream;
+import java.time.Instant;
import java.util.function.Function;
public interface InputEvent extends Closeable {
@@ -17,29 +34,21 @@ public interface InputEvent extends Closeable {
*/
T consumeBody(Function dest);
- /**
- * The application name associated with this function
- *
- * @return an application name
- */
- String getAppName();
- /**
- * @return The route (including preceding slash) of this function call
- */
- String getRoute();
/**
- * @return The full request URL of this function invocation
+ * return the current call ID for this event
+ * @return a call ID
*/
- String getRequestUrl();
+ String getCallID();
+
/**
- * The HTTP method used to invoke this function
+ * The deadline by which this event should be processed - this is information and is intended to help you determine how long you should spend processing your event - if you exceed this deadline Fn will terminate your container.
*
- * @return an UpperCase HTTP method
+ * @return a deadline relative to the current system clock that the event must be processed by
*/
- String getMethod();
+ Instant getDeadline();
/**
@@ -49,11 +58,5 @@ public interface InputEvent extends Closeable {
*/
Headers getHeaders();
- /**
- * The query parameters of the function invocation
- *
- * @return an immutable map of query parameters parsed from the request URL
- */
- QueryParameters getQueryParameters();
}
diff --git a/api/src/main/java/com/fnproject/fn/api/InvocationContext.java b/api/src/main/java/com/fnproject/fn/api/InvocationContext.java
index 3b3395a1..245d582d 100644
--- a/api/src/main/java/com/fnproject/fn/api/InvocationContext.java
+++ b/api/src/main/java/com/fnproject/fn/api/InvocationContext.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api;
@@ -9,6 +25,11 @@
*/
public interface InvocationContext {
+ /**
+ * Returns the {@link RuntimeContext} associated with this invocation context
+ *
+ * @return a runtime context
+ */
RuntimeContext getRuntimeContext();
/**
@@ -19,4 +40,39 @@ public interface InvocationContext {
*/
void addListener(InvocationListener listener);
+
+ /**
+ * Returns the current request headers for the invocation
+ *
+ * @return the headers passed into the function
+ */
+ Headers getRequestHeaders();
+
+ /**
+ * Sets the response content type, this will override the default content type of the output
+ *
+ * @param contentType a mime type for the response
+ */
+ default void setResponseContentType(String contentType) {
+ this.setResponseHeader(OutputEvent.CONTENT_TYPE_HEADER, contentType);
+ }
+
+ /**
+ * Adds a response header to the outbound event
+ *
+ * @param key header key
+ * @param value header value
+ */
+ void addResponseHeader(String key, String value);
+
+ /**
+ * Sets a response header to the outbound event, overriding a previous value.
+ *
+ * Headers set in this way override any headers returned by the function or any middleware on the function
+ *
+ * @param key header key
+ * @param v1 first value to set
+ * @param vs other values to set header to
+ */
+ void setResponseHeader(String key, String v1, String... vs);
}
diff --git a/api/src/main/java/com/fnproject/fn/api/InvocationListener.java b/api/src/main/java/com/fnproject/fn/api/InvocationListener.java
index 7c9d46fa..1e8b6b29 100644
--- a/api/src/main/java/com/fnproject/fn/api/InvocationListener.java
+++ b/api/src/main/java/com/fnproject/fn/api/InvocationListener.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api;
import java.util.EventListener;
diff --git a/api/src/main/java/com/fnproject/fn/api/MethodWrapper.java b/api/src/main/java/com/fnproject/fn/api/MethodWrapper.java
index 8ed0644f..655c28db 100644
--- a/api/src/main/java/com/fnproject/fn/api/MethodWrapper.java
+++ b/api/src/main/java/com/fnproject/fn/api/MethodWrapper.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api;
import java.lang.reflect.Method;
diff --git a/api/src/main/java/com/fnproject/fn/api/OutputBinding.java b/api/src/main/java/com/fnproject/fn/api/OutputBinding.java
index 1355e529..4bf05577 100644
--- a/api/src/main/java/com/fnproject/fn/api/OutputBinding.java
+++ b/api/src/main/java/com/fnproject/fn/api/OutputBinding.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api;
import java.lang.annotation.ElementType;
diff --git a/api/src/main/java/com/fnproject/fn/api/OutputCoercion.java b/api/src/main/java/com/fnproject/fn/api/OutputCoercion.java
index ac8e9ac4..44e3cc79 100644
--- a/api/src/main/java/com/fnproject/fn/api/OutputCoercion.java
+++ b/api/src/main/java/com/fnproject/fn/api/OutputCoercion.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api;
import java.util.Optional;
diff --git a/api/src/main/java/com/fnproject/fn/api/OutputEvent.java b/api/src/main/java/com/fnproject/fn/api/OutputEvent.java
index ef13983c..c6ab4143 100644
--- a/api/src/main/java/com/fnproject/fn/api/OutputEvent.java
+++ b/api/src/main/java/com/fnproject/fn/api/OutputEvent.java
@@ -1,47 +1,100 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api;
import java.io.IOException;
import java.io.OutputStream;
+import java.util.Objects;
import java.util.Optional;
/**
* Wrapper for an outgoing fn event
*/
public interface OutputEvent {
+
+ String CONTENT_TYPE_HEADER = "Content-Type";
+
+ /**
+ * The outcome status of this function event
+ * This determines how the platform will reflect this error to the customer and how it will treat the container after an error
+ */
+ enum Status {
+ /**
+ * The event was successfully processed
+ */
+ Success(200),
+ /**
+ * The Function code raised unhandled exception
+ */
+ FunctionError(502),
+ /**
+ * The Function code did not respond within a given timeout
+ */
+ FunctionTimeout(504),
+ /**
+ * An internal error occurred in the FDK
+ */
+ InternalError(500);
+
+ private final int code;
+
+ Status(int code) {
+ this.code = code;
+ }
+
+ public int getCode() {
+ return this.code;
+ }
+
+ }
+
+
/**
- * Report the HTTP status code of this event.
- * For default-format functions, this value is mapped into a success/failure value as follows:
- * status codes in the range [100, 400) are considered successful; anything else is a failure.
+ * Report the outcome status code of this event.
*
- * @return the status code associated with this event
+ * @return the status associated with this event
*/
- int getStatusCode();
+ Status getStatus();
- int SUCCESS = 200;
- int FAILURE = 500;
/**
* Report the boolean success of this event.
* For default-format functions, this is used to map the HTTP status code into a straight success/failure.
+ *
* @return true if the output event results from a successful invocation.
*/
default boolean isSuccess() {
- return 100 <= getStatusCode() && getStatusCode() < 400;
+ return getStatus() == Status.Success;
}
/**
- * The indicative content type of the response.
+ * The content type of the response.
*
- * This will only be used when the function format is HTTP
*
* @return The name of the content type.
*/
- Optional getContentType();
+ default Optional getContentType(){
+ return getHeaders().get(CONTENT_TYPE_HEADER);
+ }
/**
* Any additional {@link Headers} that should be supplied along with the content
- *
+ *
* These are only used when the function format is HTTP
*
* @return the headers to add
@@ -51,51 +104,79 @@ default boolean isSuccess() {
/**
* Write the body of the output to a stream
*
- * @param out an outputstream to emit the body of the event
- * @throws IOException OutputStream exceptions percolate up through this method
+ * @param out an outputstream to emit the body of the event
+ * @throws IOException OutputStream exceptions percolate up through this method
*/
void writeToOutput(OutputStream out) throws IOException;
+ /**
+ * Creates a new output event based on this one with the headers overriding
+ * @param headers the headers use in place of this event
+ * @return a new output event with these set
+ */
+ default OutputEvent withHeaders(Headers headers) {
+ Objects.requireNonNull(headers, "headers");
+
+ OutputEvent a = this;
+ return new OutputEvent() {
+
+ @Override
+ public Status getStatus() {
+ return a.getStatus();
+ }
+
+ @Override
+ public Headers getHeaders() {
+ return headers;
+ }
+
+ @Override
+ public void writeToOutput(OutputStream out) throws IOException {
+ a.writeToOutput(out);
+ }
+ };
+ }
+
/**
* Create an output event from a byte array
*
* @param bytes the byte array to write to the output
- * @param statusCode the status code to report
+ * @param status the status code to report
* @param contentType the content type to present on HTTP responses
* @return a new output event
*/
- static OutputEvent fromBytes(byte[] bytes, int statusCode, String contentType) {
- return fromBytes(bytes, statusCode, contentType, Headers.emptyHeaders());
- }
+ static OutputEvent fromBytes(byte[] bytes, Status status, String contentType) {
+ return fromBytes(bytes, status, contentType, Headers.emptyHeaders());
+ }
/**
* Create an output event from a byte array
*
* @param bytes the byte array to write to the output
- * @param statusCode the HTTP status code of this event
- * @param contentType the content type to present on HTTP responses
+ * @param status the status code of this event
+ * @param contentType the content type to present on HTTP responses or null
* @param headers any additional headers to supply with HTTP responses
* @return a new output event
*/
- static OutputEvent fromBytes(byte[] bytes, int statusCode, String contentType, Headers headers) {
- if (statusCode < 100 || 600 <= statusCode) {
- throw new IllegalArgumentException("Valid status codes must lie in the range [100, 599]");
- }
+ static OutputEvent fromBytes(final byte[] bytes, final Status status, final String contentType, final Headers headers) {
+ Objects.requireNonNull(bytes, "bytes");
+ Objects.requireNonNull(status, "status");
+ Objects.requireNonNull(headers, "headers");
+
+ final Headers newHeaders = contentType== null?Headers.emptyHeaders():headers.setHeader("Content-Type",contentType);
return new OutputEvent() {
@Override
- public int getStatusCode() {
- return statusCode;
+ public Status getStatus() {
+ return status;
}
- @Override
- public Optional getContentType() {
- return Optional.ofNullable(contentType);
- }
@Override
- public Headers getHeaders() { return headers; }
+ public Headers getHeaders() {
+ return newHeaders;
+ }
@Override
public void writeToOutput(OutputStream out) throws IOException {
@@ -104,24 +185,25 @@ public void writeToOutput(OutputStream out) throws IOException {
};
}
- static OutputEvent emptyResult(int statusCode) {
- if (statusCode < 100 || 600 <= statusCode) {
- throw new IllegalArgumentException("Valid status codes must lie in the range [100, 599]");
- }
+ /**
+ * Returns an output event with an empty body and a given status
+ * @param status the status of the event
+ * @return a new output event
+ */
+ static OutputEvent emptyResult(final Status status) {
+ Objects.requireNonNull(status, "status");
+
return new OutputEvent() {
@Override
- public int getStatusCode() {
- return statusCode;
+ public Status getStatus() {
+ return status;
}
@Override
- public Optional getContentType() {
- return Optional.empty();
+ public Headers getHeaders() {
+ return Headers.emptyHeaders();
}
- @Override
- public Headers getHeaders() { return Headers.emptyHeaders(); }
-
@Override
public void writeToOutput(OutputStream out) throws IOException {
diff --git a/api/src/main/java/com/fnproject/fn/api/QueryParameters.java b/api/src/main/java/com/fnproject/fn/api/QueryParameters.java
index 08514054..0de6ca34 100644
--- a/api/src/main/java/com/fnproject/fn/api/QueryParameters.java
+++ b/api/src/main/java/com/fnproject/fn/api/QueryParameters.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api;
import java.util.List;
@@ -7,7 +23,7 @@
/**
* Wrapper for query parameters map parsed from the URL of a function invocation.
*/
-public interface QueryParameters {
+public interface QueryParameters {
/**
* Find the first entry for {@code key} if it exists otherwise returns {@code Optional.empty}
*
diff --git a/api/src/main/java/com/fnproject/fn/api/RuntimeContext.java b/api/src/main/java/com/fnproject/fn/api/RuntimeContext.java
index 865a6d10..04936a8c 100644
--- a/api/src/main/java/com/fnproject/fn/api/RuntimeContext.java
+++ b/api/src/main/java/com/fnproject/fn/api/RuntimeContext.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api;
@@ -13,6 +29,38 @@
* of a function; they will not change between multiple invocations of a hot function.
*/
public interface RuntimeContext {
+
+
+ /**
+ * The application ID of the application associated with this function
+ * @return an application ID
+ */
+ String getAppID();
+
+ /**
+ * THe function ID of the function
+ * @return a function ID
+ */
+ String getFunctionID();
+
+ /**
+ * The user-friendly name of the application associated with this function,
+ * if present; defaulted to the application ID for backwards compatibility
+ * @return an application name
+ */
+ default public String getAppName() {
+ return getAppID();
+ }
+
+ /**
+ * The user-friendly name of the function, if present; defaulted to the
+ * function ID for backwards compatibility
+ * @return a function name
+ */
+ default public String getFunctionName() {
+ return getFunctionID();
+ }
+
/**
* Create an instance of the user specified class on which the target function to invoke is declared.
*
@@ -77,7 +125,7 @@ public interface RuntimeContext {
*
* @param targetMethod The user function method
* @param param The index of the parameter
- * @return a list of configured input coercions to apply to the given parameter
+ * @return a list of configured input coercions to apply to the given parameter
*/
List getInputCoercions(MethodWrapper targetMethod, int param);
@@ -105,7 +153,21 @@ public interface RuntimeContext {
* Set an {@link FunctionInvoker} for this function. The invoker will override
* the built in function invoker, although the cloud threads invoker will still
* have precedence so that cloud threads can be used from functions using custom invokers.
+ *
* @param invoker The {@link FunctionInvoker} to add.
+ * @deprecated this is equivalent to {@link #addInvoker(FunctionInvoker, FunctionInvoker.Phase)} with a phase of {@link FunctionInvoker.Phase#Call}
+ */
+ default void setInvoker(FunctionInvoker invoker) {
+ addInvoker(invoker, FunctionInvoker.Phase.Call);
+ }
+
+
+ /**
+ * Adds an FunctionInvoker handler to the runtime - new FunctionInvokers are added at the head of the specific phase they apply to so ordering may be important
+ *
+ *
+ * @param invoker an invoker to use to handle a given call
+ * @param phase the phase at which to add the invoke
*/
- void setInvoker(FunctionInvoker invoker);
+ void addInvoker(FunctionInvoker invoker, FunctionInvoker.Phase phase);
}
diff --git a/api/src/main/java/com/fnproject/fn/api/RuntimeFeature.java b/api/src/main/java/com/fnproject/fn/api/RuntimeFeature.java
new file mode 100644
index 00000000..f57a3c1d
--- /dev/null
+++ b/api/src/main/java/com/fnproject/fn/api/RuntimeFeature.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.api;
+
+/**
+ * RuntimeFeatures are classes that configure the Fn Runtime prior to startup and can be loaded by annotating the function class with a {@link FnFeature} annotation
+ * Created on 10/09/2018.
+ *
+ * (c) 2018 Oracle Corporation
+ */
+public interface RuntimeFeature {
+
+ /**
+ * Initialize the runtime context for this function
+ *
+ * @param context a runtime context to initalize
+ */
+ void initialize(RuntimeContext context);
+}
diff --git a/api/src/main/java/com/fnproject/fn/api/TypeWrapper.java b/api/src/main/java/com/fnproject/fn/api/TypeWrapper.java
index f453892d..b693446c 100644
--- a/api/src/main/java/com/fnproject/fn/api/TypeWrapper.java
+++ b/api/src/main/java/com/fnproject/fn/api/TypeWrapper.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api;
/**
@@ -11,11 +27,11 @@ public interface TypeWrapper {
*
* For example, take the following classes:
*
{@code
- * class GenericParent<T> {
+ * class GenericParent {
* public void someMethod(T t) { // do something with t }
* }
*
- * class ConcreteClass extends GenericParent<String> { }
+ * class ConcreteClass extends GenericParent { }
* }
*
* A {@link TypeWrapper} representing the first argument of {@code someMethod} would return {@code String.class}
diff --git a/api/src/main/java/com/fnproject/fn/api/exception/FunctionConfigurationException.java b/api/src/main/java/com/fnproject/fn/api/exception/FunctionConfigurationException.java
index e8c85889..6cb90cf9 100644
--- a/api/src/main/java/com/fnproject/fn/api/exception/FunctionConfigurationException.java
+++ b/api/src/main/java/com/fnproject/fn/api/exception/FunctionConfigurationException.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api.exception;
/**
diff --git a/api/src/main/java/com/fnproject/fn/api/exception/FunctionInputHandlingException.java b/api/src/main/java/com/fnproject/fn/api/exception/FunctionInputHandlingException.java
index 9762e6c4..984e871c 100644
--- a/api/src/main/java/com/fnproject/fn/api/exception/FunctionInputHandlingException.java
+++ b/api/src/main/java/com/fnproject/fn/api/exception/FunctionInputHandlingException.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api.exception;
/**
diff --git a/api/src/main/java/com/fnproject/fn/api/exception/FunctionLoadException.java b/api/src/main/java/com/fnproject/fn/api/exception/FunctionLoadException.java
index 095875cc..c911db0e 100644
--- a/api/src/main/java/com/fnproject/fn/api/exception/FunctionLoadException.java
+++ b/api/src/main/java/com/fnproject/fn/api/exception/FunctionLoadException.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api.exception;
/**
diff --git a/api/src/main/java/com/fnproject/fn/api/exception/FunctionOutputHandlingException.java b/api/src/main/java/com/fnproject/fn/api/exception/FunctionOutputHandlingException.java
index 9467f8ae..587b4aa3 100644
--- a/api/src/main/java/com/fnproject/fn/api/exception/FunctionOutputHandlingException.java
+++ b/api/src/main/java/com/fnproject/fn/api/exception/FunctionOutputHandlingException.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api.exception;
/**
diff --git a/api/src/main/java/com/fnproject/fn/api/flow/FunctionInvokeFailedException.java b/api/src/main/java/com/fnproject/fn/api/flow/FunctionInvokeFailedException.java
deleted file mode 100644
index db8ce8b6..00000000
--- a/api/src/main/java/com/fnproject/fn/api/flow/FunctionInvokeFailedException.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.fnproject.fn.api.flow;
-
-/**
- * Exception thrown when a function call failed within the fn platform - the function may or may not have been invoked and
- * that invocation may or may not have completed.
- */
-public class FunctionInvokeFailedException extends PlatformException {
- public FunctionInvokeFailedException(String reason) { super(reason); }
-}
diff --git a/api/src/main/java/com/fnproject/fn/api/flow/FunctionTimeoutException.java b/api/src/main/java/com/fnproject/fn/api/flow/FunctionTimeoutException.java
deleted file mode 100644
index 5cf3eec9..00000000
--- a/api/src/main/java/com/fnproject/fn/api/flow/FunctionTimeoutException.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.fnproject.fn.api.flow;
-
-/**
- * Exception thrown when a function execution exceeds its configured timeout.
- *
- * When this exception is raised the fn server has terminated the container hosting the function.
- */
-public class FunctionTimeoutException extends PlatformException {
- public FunctionTimeoutException(String reason) { super(reason); }
-}
diff --git a/api/src/main/java/com/fnproject/fn/api/flow/HttpMethod.java b/api/src/main/java/com/fnproject/fn/api/flow/HttpMethod.java
deleted file mode 100644
index 8ec66fd6..00000000
--- a/api/src/main/java/com/fnproject/fn/api/flow/HttpMethod.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.fnproject.fn.api.flow;
-
-/**
- * Enum representing the different HTTP types that can be used to invoke an external function
- *
- * @see Flow#invokeFunction
- */
-public enum HttpMethod {
- GET("GET"),
- HEAD("HEAD"),
- POST("POST"),
- PUT("PUT"),
- DELETE("DELETE"),
- OPTIONS("OPTIONS"),
- PATCH("PATCH");
-
- private final String verb;
-
- HttpMethod(String verb) {
- this.verb = verb;
- }
-
- @Override
- public String toString() {
- return this.verb;
- }
-}
diff --git a/api/src/main/java/com/fnproject/fn/api/flow/HttpRequest.java b/api/src/main/java/com/fnproject/fn/api/flow/HttpRequest.java
deleted file mode 100644
index 0f8e710a..00000000
--- a/api/src/main/java/com/fnproject/fn/api/flow/HttpRequest.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.fnproject.fn.api.flow;
-
-import com.fnproject.fn.api.Headers;
-
-/**
- * An abstract HTTP request details (without location)
- */
-public interface HttpRequest {
- /**
- * Return the HTTP method used to supply this value
- *
- * @return the HTTP method
- */
- HttpMethod getMethod();
-
- /**
- * Return the headers on the HTTP request
- *
- * @return the headers
- */
- Headers getHeaders();
-
- /**
- * Returns the body of the request as a byte array
- *
- * @return the function request body
- */
- byte[] getBodyAsBytes();
-}
diff --git a/api/src/main/java/com/fnproject/fn/api/flow/InvalidStageResponseException.java b/api/src/main/java/com/fnproject/fn/api/flow/InvalidStageResponseException.java
deleted file mode 100644
index e3ef054a..00000000
--- a/api/src/main/java/com/fnproject/fn/api/flow/InvalidStageResponseException.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.fnproject.fn.api.flow;
-
-/**
- * Exception thrown when a completion stage responds with an incompatible datum type for its corresponding completion
- * graph stage.
- */
-public class InvalidStageResponseException extends PlatformException {
- public InvalidStageResponseException(String reason) { super(reason); }
-}
diff --git a/api/src/main/java/com/fnproject/fn/api/flow/LambdaSerializationException.java b/api/src/main/java/com/fnproject/fn/api/flow/LambdaSerializationException.java
deleted file mode 100644
index d1085179..00000000
--- a/api/src/main/java/com/fnproject/fn/api/flow/LambdaSerializationException.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.fnproject.fn.api.flow;
-
-/**
- * Exception thrown when a lambda or any referenced objects fail to be serialized.
- * The cause will typically be a {@link java.io.NotSerializableException} or other {@link java.io.IOException} detailing what could not be serialized
- */
-public class LambdaSerializationException extends FlowCompletionException {
- public LambdaSerializationException(String message) {
- super(message);
- }
-
- public LambdaSerializationException(String message, Exception e) {
- super(message, e);
- }
-}
diff --git a/api/src/main/java/com/fnproject/fn/api/flow/ResultSerializationException.java b/api/src/main/java/com/fnproject/fn/api/flow/ResultSerializationException.java
deleted file mode 100644
index e2fad467..00000000
--- a/api/src/main/java/com/fnproject/fn/api/flow/ResultSerializationException.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.fnproject.fn.api.flow;
-
-/**
- * Exception thrown when a result returned by a completion stage fails to be serialized.
- */
-public class ResultSerializationException extends FlowCompletionException {
- public ResultSerializationException(String message, Throwable e) {
- super(message, e);
- }
-}
diff --git a/api/src/main/java/com/fnproject/fn/api/flow/StageInvokeFailedException.java b/api/src/main/java/com/fnproject/fn/api/flow/StageInvokeFailedException.java
deleted file mode 100644
index b4770bbc..00000000
--- a/api/src/main/java/com/fnproject/fn/api/flow/StageInvokeFailedException.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.fnproject.fn.api.flow;
-
-/**
- * Exception thrown when a a completion stage invocation failed within Fn - the stage may or may not have been invoked
- * and that invocation may or may not have completed.
- */
-public class StageInvokeFailedException extends PlatformException {
- public StageInvokeFailedException(String reason) { super(reason); }
-}
diff --git a/api/src/main/java/com/fnproject/fn/api/flow/StageLostException.java b/api/src/main/java/com/fnproject/fn/api/flow/StageLostException.java
deleted file mode 100644
index 5131ff5f..00000000
--- a/api/src/main/java/com/fnproject/fn/api/flow/StageLostException.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.fnproject.fn.api.flow;
-
-/**
- * Exception thrown when a stage failed after an internal error in the flow server, the stage may or may not have been
- * invoked and that invocation may or may not have completed.
- */
-public class StageLostException extends PlatformException {
- public StageLostException(String reason) { super(reason); }
-}
diff --git a/api/src/main/java/com/fnproject/fn/api/flow/StageTimeoutException.java b/api/src/main/java/com/fnproject/fn/api/flow/StageTimeoutException.java
deleted file mode 100644
index 5de3c6d3..00000000
--- a/api/src/main/java/com/fnproject/fn/api/flow/StageTimeoutException.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.fnproject.fn.api.flow;
-
-/**
- * Exception thrown when a completion stage function execution exceeds it configured timeout -
- * the stage may or may not have completed normally.
- *
- * When this exception is raised the fn server has terminated the container hosting the function.
- */
-public class StageTimeoutException extends PlatformException {
- public StageTimeoutException(String reason) { super(reason); }
-}
diff --git a/api/src/main/java/com/fnproject/fn/api/flow/package-info.java b/api/src/main/java/com/fnproject/fn/api/flow/package-info.java
deleted file mode 100644
index 0d21dcaf..00000000
--- a/api/src/main/java/com/fnproject/fn/api/flow/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * SDK for creating and running asynchronous processes from within fn for Java.
- */
-package com.fnproject.fn.api.flow;
diff --git a/api/src/main/java/com/fnproject/fn/api/httpgateway/HTTPGatewayContext.java b/api/src/main/java/com/fnproject/fn/api/httpgateway/HTTPGatewayContext.java
new file mode 100644
index 00000000..fdbde96b
--- /dev/null
+++ b/api/src/main/java/com/fnproject/fn/api/httpgateway/HTTPGatewayContext.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.api.httpgateway;
+
+import com.fnproject.fn.api.Headers;
+import com.fnproject.fn.api.InvocationContext;
+import com.fnproject.fn.api.QueryParameters;
+
+/**
+ * A context for accessing and setting HTTP Gateway atributes such aas headers and query parameters from a function call
+ *
+ * Created on 19/09/2018.
+ *
+ * (c) 2018 Oracle Corporation
+ */
+public interface HTTPGatewayContext {
+
+ /**
+ * Returns the underlying invocation context behind this HTTP context
+ *
+ * @return an invocation context related to this function
+ */
+ InvocationContext getInvocationContext();
+
+
+ /**
+ * Returns the HTTP headers for the request associated with this function call
+ * If no headers were set this will return an empty headers object
+ *
+ * @return the incoming HTTP headers sent in the gateway request
+ */
+ Headers getHeaders();
+
+
+ /**
+ * Returns the fully qualified request URI that the function was called with, including query parameters
+ *
+ * @return the request URI of the function
+ */
+ String getRequestURL();
+
+
+ /**
+ * Returns the incoming request method for the HTTP
+ *
+ * @return the HTTP method set on this call
+ */
+ String getMethod();
+
+ /**
+ * Returns the query parameters of the request
+ *
+ * @return a query parameters object
+ */
+ QueryParameters getQueryParameters();
+
+
+ /**
+ * Adds a response header to the outbound event
+ *
+ * @param key header key
+ * @param value header value
+ */
+ void addResponseHeader(String key, String value);
+
+ /**
+ * Sets a response header to the outbound event, overriding a previous value.
+ *
+ * Headers set in this way override any headers returned by the function or any middleware on the function
+ *
+ * Setting the "Content-Type" response header also sets this on the underlying Invocation context
+ *
+ * @param key header key
+ * @param v1 first value to set
+ * @param vs other values to set header to
+ */
+ void setResponseHeader(String key, String v1, String... vs);
+
+ /**
+ * Sets the HTTP status code of the response
+ *
+ * @param code an HTTP status code
+ * @throws IllegalArgumentException if the code is < 100 or >l=600
+ */
+ void setStatusCode(int code);
+}
diff --git a/api/src/main/java/com/fnproject/fn/api/tracing/TracingContext.java b/api/src/main/java/com/fnproject/fn/api/tracing/TracingContext.java
new file mode 100644
index 00000000..36ac377c
--- /dev/null
+++ b/api/src/main/java/com/fnproject/fn/api/tracing/TracingContext.java
@@ -0,0 +1,101 @@
+package com.fnproject.fn.api.tracing;
+
+import com.fnproject.fn.api.InvocationContext;
+import com.fnproject.fn.api.RuntimeContext;
+
+public interface TracingContext {
+
+ /**
+ * Returns the underlying invocation context behind this Tracing context
+ *
+ * @return an invocation context related to this function
+ */
+ InvocationContext getInvocationContext();
+
+ /**
+ * Returns the {@link RuntimeContext} associated with this invocation context
+ *
+ * @return a runtime context
+ */
+ RuntimeContext getRuntimeContext();
+
+ /**
+ * Returns true if tracing is enabled for this function invocation
+ *
+ * @return whether tracing is enabled
+ */
+ Boolean isTracingEnabled();
+
+ /**
+ * Returns the user-friendly name of the application associated with the
+ * function; shorthand for getRuntimeContext().getAppName()
+ *
+ * @return the user-friendly name of the application associated with the
+ * function
+ */
+ String getAppName();
+
+ /**
+ * Returns the user-friendly name of the function; shorthand for
+ * getRuntimeContext().getFunctionName()
+ *
+ * @return the user-friendly name of the function
+ */
+ String getFunctionName();
+
+ /**
+ * Returns a standard constructed "service name" to be used in tracing
+ * libraries to identify the function
+ *
+ * @return a standard constructed "service name"
+ */
+ String getServiceName();
+
+ /**
+ * Returns the URL to be used in tracing libraries as the destination for
+ * the tracing data
+ *
+ * @return a string containing the trace collector URL
+ */
+ String getTraceCollectorURL();
+
+ /**
+ * Returns the current trace ID as extracted from Zipkin B3 headers if they
+ * are present on the request
+ *
+ * @return the trace ID as a string
+ */
+ String getTraceId();
+
+ /**
+ * Returns the current span ID as extracted from Zipkin B3 headers if they
+ * are present on the request
+ *
+ * @return the span ID as a string
+ */
+ String getSpanId();
+
+ /**
+ * Returns the parent span ID as extracted from Zipkin B3 headers if they
+ * are present on the request
+ *
+ * @return the parent span ID as a string
+ */
+ String getParentSpanId();
+
+ /**
+ * Returns the value of the Sampled header of the Zipkin B3 headers if they
+ * are present on the request
+ *
+ * @return true if sampling is enabled for the request
+ */
+ Boolean isSampled();
+
+ /**
+ * Returns the value of the Flags header of the Zipkin B3 headers if they
+ * are present on the request
+ *
+ * @return the verbatim value of the X-B3-Flags header
+ */
+ String getFlags();
+}
diff --git a/api/src/test/java/com/fnproject/fn/api/HeadersTest.java b/api/src/test/java/com/fnproject/fn/api/HeadersTest.java
new file mode 100644
index 00000000..7143c34a
--- /dev/null
+++ b/api/src/test/java/com/fnproject/fn/api/HeadersTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.api;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Created on 10/09/2018.
+ *
+ * (c) 2018 Oracle Corporation
+ */
+public class HeadersTest {
+
+ @Test
+ public void shouldCanonicalizeHeaders(){
+ for (String[] v : new String[][] {
+ {"",""},
+ {"a","A"},
+ {"fn-ID-","Fn-Id-"},
+ {"myHeader-VaLue","Myheader-Value"},
+ {" Not a Header "," Not a Header "},
+ {"-","-"},
+ {"--","--"},
+ {"a-","A-"},
+ {"-a","-A"}
+ }){
+ assertThat(Headers.canonicalKey(v[0])).isEqualTo(v[1]);
+ }
+ }
+
+
+}
diff --git a/bin/scripts/migration/flag b/bin/scripts/migration/flag
new file mode 100644
index 00000000..4a3b60a2
--- /dev/null
+++ b/bin/scripts/migration/flag
@@ -0,0 +1 @@
+0 0
\ No newline at end of file
diff --git a/bin/scripts/migration/migrate_to_maven_central.sh b/bin/scripts/migration/migrate_to_maven_central.sh
new file mode 100644
index 00000000..5be056e0
--- /dev/null
+++ b/bin/scripts/migration/migrate_to_maven_central.sh
@@ -0,0 +1,395 @@
+#!/bin/bash
+
+set -euo pipefail
+
+destdir=flag
+POM_PLACEHOLDER=".*>"
+POM_REPLACEMENT="4.0.0
+
+
+ The Apache License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+
+
+ oracle
+ https://www.oracle.com/
+
+
+
+ fnproject/core
+ roneet.shaw@oracle.com
+ oracle
+ https://www.oracle.com/
+
+
+
+ scm:git:git://github.com/fnproject/fdk-java.git
+ scm:git:ssh://github.com:fnproject/fdk-java.git
+ https://github.com/fnproject/fdk-java
+
+
+
+ central
+ https://central.sonatype.com/api/v1/staging/deploy/maven2
+
+
+ central
+ https://central.sonatype.com/repository/maven-snapshots/
+
+
+ fdk-java
+ The Function Development Kit for Java makes it easy to build and deploy Java functions to Fn
+ https://fnproject.io/tutorials/JavaFDKIntroduction/
+ "
+
+# Constants
+MAVEN_CENTRAL_STAGINGURL="https://central.sonatype.com/api/v1/staging/deploy/maven2"
+MAVEN_CENTRAL_REPOID="central"
+OUTPUT_DIR="output"
+
+# TODO: add versions e.g. (1.0.0 1.0.1 1.0.2) 10*1+ 5+2+1
+
+VERSIONS=(1.0.107 1.0.106 1.0.105 1.0.104 1.0.103 1.0.102 1.0.101 1.0.100 1.0.99 1.0.98 1.0.97 1.0.96 1.0.95 1.0.94 1.0.93 1.0.92 1.0.91 1.0.90 1.0.89 1.0.88)
+
+#VERSIONS=(1.0.127 1.0.87 1.0.86 1.0.85 1.0.84 1.0.83 1.0.82 1.0.81 1.0.80 1.0.79 1.0.78 1.0.77 1.0.76 1.0.75 1.0.74 1.0.72 1.0.71 1.0.70 1.0.64 1.0.63 1.0.62 1.0.61 1.0.60 1.0.59 1.0.58 1.0.57 1.0.56 1.0.55 1.0.54 1.0.53 1.0.52 1.0.51 1.0.50 1.0.49 1.0.48 1.0.47 1.0.46 1.0.45 1.0.44 1.0.43 1.0.42 1.0.41 1.0.40 1.0.39 1.0.38 1.0.37 1.0.36 1.0.35 1.0.34 1.0.33 1.0.32 1.0.31 1.0.30 1.0.29 1.0.28 1.0.27 1.0.26 1.0.25 1.0.24 1.0.23 1.0.22 1.0.21 1.0.20 1.0.19 1.0.18 1.0.17 1.0.16 1.0.15 1.0.14 1.0.13 1.0.11 1.0.10 1.0.9 1.0.8 1.0.7 1.0.6 1.0.5 1.0.4 1.0.3 1.0.2 1.0.1)
+
+#VERSIONS=(1.0.127 1.0.125 1.0.124 1.0.123 1.0.122 1.0.121 1.0.120 1.0.119 1.0.118 1.0.117 1.0.116 1.0.115 1.0.114 1.0.113 1.0.112 1.0.111 1.0.110 1.0.109 1.0.108 1.0.107 1.0.106 1.0.105 1.0.104 1.0.103 1.0.102 1.0.101 1.0.100 1.0.99 1.0.98 1.0.97 1.0.96 1.0.95 1.0.94 1.0.93 1.0.92 1.0.91 1.0.90 1.0.89 1.0.88 1.0.87 1.0.86 1.0.85 1.0.84 1.0.83 1.0.82 1.0.81 1.0.80 1.0.79 1.0.78 1.0.77 1.0.76 1.0.75 1.0.74 1.0.72 1.0.71 1.0.70 1.0.64 1.0.63 1.0.62 1.0.61 1.0.60 1.0.59 1.0.58 1.0.57 1.0.56 1.0.55 1.0.54 1.0.53 1.0.52 1.0.51 1.0.50 1.0.49 1.0.48 1.0.47 1.0.46 1.0.45 1.0.44 1.0.43 1.0.42 1.0.41 1.0.40 1.0.39 1.0.38 1.0.37 1.0.36 1.0.35 1.0.34 1.0.33 1.0.32 1.0.31 1.0.30 1.0.29 1.0.28 1.0.27 1.0.26 1.0.25 1.0.24 1.0.23 1.0.22 1.0.21 1.0.20 1.0.19 1.0.18 1.0.17 1.0.16 1.0.15 1.0.14 1.0.13 1.0.11 1.0.10 1.0.9 1.0.8 1.0.7 1.0.6 1.0.5 1.0.4 1.0.3 1.0.2 1.0.1 1.0.0)
+
+# TODO: https://dl.bintray.com////
+BINTRAYURL="http://dl.bintray.com/fnproject/fnproject/com/fnproject/fn/"
+
+#IFS=$' ' read -d '' -r -a START_VER < $destdir
+
+IFS=$' ' GLOBIGNORE='*' command eval 'START_VER=($(cat flag))'
+
+# TODO : add artifact Id
+ARTIFACT_IDs=(fdk api experimental-native-image-support flow-api flow-runtime flow-testing fn-spring-cloud-function jrestless-handler runtime testing-core testing-junit4 testing)
+#ARTIFACT_IDs=(fdk api )
+
+
+#parameters $1: url to test
+function ping_url() {
+ status_code=$(curl --head --write-out '%{http_code}\n' --silent --output /dev/null $1)
+ echo $status_code
+}
+
+# Utilities
+function escape_pom() {
+ echo "$1" | sed 's#/#\\/#g' | tr '\n' '@'
+}
+
+function xml_encode() {
+ echo $1 | sed 's/&/\&/g; s/\</g; s/>/\>/g; s/"/\"/g; s/'"'"'/\'/g'
+}
+
+#parameters $1: artifact_id, $2: version
+function download_and_save(){
+ #copy javadoc
+
+ if [ $( ping_url $BINTRAYURL/$1/$2/$1-$2-javadoc.jar ) == 200 ]
+ then
+ mkdir -p $OUTPUT_DIR/$1/$2
+ curl -L $BINTRAYURL/$1/$2/$1-$2-javadoc.jar \
+ -o $OUTPUT_DIR/$1/$2/$1-$2-javadoc.jar
+ fi
+
+ if [ $( ping_url $BINTRAYURL/$1/$2/$1-$2-javadoc.jar.md5 ) == 200 ]
+ then
+ mkdir -p $OUTPUT_DIR/$1/$2
+ curl -L $BINTRAYURL/$1/$2/$1-$2-javadoc.jar.md5 \
+ -o $OUTPUT_DIR/$1/$2/$1-$2-javadoc.jar.md5
+ fi
+
+ #copy sources
+ if [ $( ping_url $BINTRAYURL/$1/$2/$1-$2-sources.jar ) == 200 ]
+ then
+ mkdir -p $OUTPUT_DIR/$1/$2
+ curl -L $BINTRAYURL/$1/$2/$1-$2-sources.jar \
+ -o $OUTPUT_DIR/$1/$2/$1-$2-sources.jar
+ fi
+
+ if [ $( ping_url $BINTRAYURL/$1/$2/$1-$2-sources.jar.md5 ) == 200 ]
+ then
+ mkdir -p $OUTPUT_DIR/$1/$2
+ curl -L $BINTRAYURL/$1/$2/$1-$2-sources.jar.md5 \
+ -o $OUTPUT_DIR/$1/$2/$1-$2-sources.jar.md5
+ fi
+
+ #copy jar
+ if [ $( ping_url $BINTRAYURL/$1/$2/$1-$2.jar ) == 200 ]
+ then
+ mkdir -p $OUTPUT_DIR/$1/$2
+ curl -L $BINTRAYURL/$1/$2/$1-$2.jar \
+ -o $OUTPUT_DIR/$1/$2/$1-$2.jar
+ fi
+
+ if [ $( ping_url $BINTRAYURL/$1/$2/$1-$2.jar.md5 ) == 200 ]
+ then
+ mkdir -p $OUTPUT_DIR/$1/$2
+ curl -L $BINTRAYURL/$1/$2/$1-$2.jar.md5 \
+ -o $OUTPUT_DIR/$1/$2/$1-$2.jar.md5
+ fi
+
+ #copy pom
+ if [ $( ping_url $BINTRAYURL/$1/$2/$1-$2.pom ) == 200 ]
+ then
+ mkdir -p $OUTPUT_DIR/$1/$2
+ curl -L $BINTRAYURL/$1/$2/$1-$2.pom \
+ -o $OUTPUT_DIR/$1/$2/$1-$2.pom
+ fi
+
+ if [ $( ping_url $BINTRAYURL/$1/$2/$1-$2.pom.md5 ) == 200 ]
+ then
+ mkdir -p $OUTPUT_DIR/$1/$2
+ curl -L $BINTRAYURL/$1/$2/$1-$2.pom.md5 \
+ -o $OUTPUT_DIR/$1/$2/$1-$2.pom.md5
+ fi
+
+}
+
+function save_flag() {
+ echo "$1 $2" > "$destdir"
+}
+
+function download_all_versions(){
+ for (( i=${START_VER[0]}; i<${#ARTIFACT_IDs[@]}; i++ )); do
+ for (( j=${START_VER[1]}; j<${#VERSIONS[@]}; j++ )); do
+ aid=${ARTIFACT_IDs[$i]}
+ ver=${VERSIONS[$j]}
+
+ echo "Downloading version $ver $aid"
+ download_and_save $aid $ver
+ save_flag $i $j
+ done
+ START_VER[1]=0
+ done
+}
+
+function prepare_pom() {
+ echo "Preparing $1"
+ EXAMPLE_MODULE="examples<\/module>"
+ EXP_MODULE="integration-tests<\/module>"
+ NEW=" "
+ PLUGINS=""
+ SCM_PLUGIN="
+
+
+ org.sonatype.central
+ central-publishing-maven-plugin
+ 0.8.0
+ true
+
+ central
+ true
+ published
+
+
+
+ "
+ for (( i=${START_VER[0]}; i<${#ARTIFACT_IDs[@]}; i++ )); do
+ for (( j=${START_VER[1]}; j<${#VERSIONS[@]}; j++ )); do
+ aid=${ARTIFACT_IDs[$i]}
+ ver=${VERSIONS[$j]}
+
+ echo $aid $ver
+ #if [ $ver = $2 ]; then
+ pom=$OUTPUT_DIR/$aid/$ver/$aid-$ver.pom
+ if [ -f "$pom" ]; then
+ if [ $aid = "fdk" ]; then
+ newPom=$1/$aid/$ver/pom.xml
+ mkdir -p $1/$aid/$ver
+ sed -e "s/$POM_PLACEHOLDER/$(escape_pom "$POM_REPLACEMENT")/g; s/$EXAMPLE_MODULE/$NEW/g; s/$EXP_MODULE/$NEW/g; s/$PLUGINS/$(escape_pom "$SCM_PLUGIN")/1;" \
+ $pom |\
+ tr '@' '\n' > temp.txt
+ mv temp.txt $newPom
+ else
+ newPom=$1/fdk/$ver/$aid/pom.xml
+ mkdir -p $1/fdk/$ver/$aid
+
+ #Copy pom
+ cp $pom $1/fdk/$ver/$aid/
+ mv $1/fdk/$ver/$aid/$aid-$ver.pom $newPom
+ fi
+ fi
+ #fi
+ done
+ done
+
+}
+
+
+function modify_group_id() {
+ echo "Changing $1"
+ OLD_GROUP_ID="com.fnproject.fn<\/groupId>"
+ NEW_GROUP_ID="$1"
+ EXAMPLE_MODULE="examples<\/module>"
+ EXP_MODULE="integration-tests<\/module>"
+ NEW=" "
+
+ PLUGINS=""
+ SCM_PLUGIN="
+
+
+ org.sonatype.central
+ central-publishing-maven-plugin
+ 0.8.0
+ true
+
+ central
+ true
+ published
+
+
+
+ "
+ for (( i=${START_VER[0]}; i<${#ARTIFACT_IDs[@]}; i++ )); do
+ for (( j=${START_VER[1]}; j<${#VERSIONS[@]}; j++ )); do
+ aid=${ARTIFACT_IDs[$i]}
+ ver=${VERSIONS[$j]}
+
+ echo $aid $ver
+ if [ $ver = $2 ]; then
+ pom=$OUTPUT_DIR/$aid/$ver/$aid-$ver.pom
+ if [ -f "$pom" ]; then
+ if [ $aid = "fdk" ]; then
+ newPom=$1/$aid/$ver/pom.xml
+ mkdir -p $1/$aid/$ver
+ sed -e "s/$OLD_GROUP_ID/$(escape_pom "$NEW_GROUP_ID")/g; s/$POM_PLACEHOLDER/$(escape_pom "$POM_REPLACEMENT")/g; s/$EXAMPLE_MODULE/$NEW/g; s/$EXP_MODULE/$NEW/g; s/$PLUGINS/$(escape_pom "$SCM_PLUGIN")/1;" \
+ $pom |\
+ tr '@' '\n' > temp.txt
+ mv temp.txt $newPom
+ else
+ newPom=$1/fdk/$ver/$aid/pom.xml
+ mkdir -p $1/fdk/$ver/$aid
+ sed -e "s/$OLD_GROUP_ID/$(escape_pom "$NEW_GROUP_ID")/g" \
+ $pom |\
+ tr '@' '\n' > temp.txt
+ mv temp.txt $newPom
+ fi
+ fi
+ fi
+ done
+ done
+
+}
+
+function deploy() {
+ for (( i=${START_VER[0]}; i<${#ARTIFACT_IDs[@]}; i++ )); do
+ for (( j=${START_VER[1]}; j<${#VERSIONS[@]}; j++ )); do
+ aid=${ARTIFACT_IDs[$i]}
+ ver=${VERSIONS[$j]}
+
+ #if [ $ver = $2 ]; then
+ # Add required metadata to pom.xml
+ if [ $aid = "fdk" ]; then
+ pom=$1/$aid/$ver/pom.xml
+ if [ -f "$pom" ]; then
+ echo "pom maven fdk"
+ echo $pom
+ mvn -s ./user-settings.xml gpg:sign-and-deploy-file \
+ -Durl=$MAVEN_CENTRAL_STAGINGURL \
+ -DrepositoryId=$MAVEN_CENTRAL_REPOID \
+ -DpomFile=$pom \
+ -Dfile=$pom
+ fi
+ else
+ pom=$1/fdk/$ver/$aid/pom.xml
+ if [ -f "$pom" ]; then
+ echo "pom maven other"
+ mvn -s ./user-settings.xml gpg:sign-and-deploy-file \
+ -Durl=$MAVEN_CENTRAL_STAGINGURL \
+ -DrepositoryId=$MAVEN_CENTRAL_REPOID \
+ -DpomFile=$pom \
+ -Dfile=$pom
+ fi
+ echo "Singing jar"
+ echo $pom
+
+ #sign jar files
+ if [ -f "$pom" ] && [ -f $OUTPUT_DIR/$aid/$ver/$aid-$ver.jar ]; then
+ echo "After jar inside if"
+ echo $pom
+ mvn -s ./user-settings.xml gpg:sign-and-deploy-file \
+ -Durl=$MAVEN_CENTRAL_STAGINGURL \
+ -DrepositoryId=$MAVEN_CENTRAL_REPOID \
+ -DpomFile=$pom \
+ -Dfile=$OUTPUT_DIR/$aid/$ver/$aid-$ver.jar
+ fi
+
+ echo "Singing sources"
+ echo $pom
+ #Sign sources.jar
+ if [ -f "$pom" ] && [ -f $OUTPUT_DIR/$aid/$ver/$aid-$ver-sources.jar ]; then
+ echo "sources"
+ mvn -s ./user-settings.xml gpg:sign-and-deploy-file \
+ -Durl=$MAVEN_CENTRAL_STAGINGURL \
+ -DrepositoryId=$MAVEN_CENTRAL_REPOID \
+ -DpomFile=$pom \
+ -Dfile=$OUTPUT_DIR/$aid/$ver/$aid-$ver-sources.jar \
+ -Dclassifier=sources
+ elif [ -d $OUTPUT_DIR/$aid/$ver ] && [ $aid != "fdk" ]; then
+ if [ -f fallback/$aid-sources.jar ]; then
+ echo "Fallback sourcess"
+ echo $OUTPUT_DIR/$aid/$ver
+ cp fallback/$aid-sources.jar $1/fdk/$ver/$aid/
+ mv $1/fdk/$ver/$aid/$aid-sources.jar $1/fdk/$ver/$aid/$aid-$ver-sources.jar
+ mvn -s ./user-settings.xml gpg:sign-and-deploy-file \
+ -Durl=$MAVEN_CENTRAL_STAGINGURL \
+ -DrepositoryId=$MAVEN_CENTRAL_REPOID \
+ -DpomFile=$pom \
+ -Dfile=$1/fdk/$ver/$aid/$aid-$ver-sources.jar \
+ -Dclassifier=sources
+ fi
+ fi
+ echo "Singing Javadocs"
+ echo $pom
+
+ #sign javadoc.jar
+ if [ -f "$pom" ] && [ -f $OUTPUT_DIR/$aid/$ver/$aid-$ver-javadoc.jar ]; then
+ echo "javadocs"
+ mvn -s ./user-settings.xml gpg:sign-and-deploy-file \
+ -Durl=$MAVEN_CENTRAL_STAGINGURL \
+ -DrepositoryId=$MAVEN_CENTRAL_REPOID \
+ -DpomFile=$pom \
+ -Dfile=$OUTPUT_DIR/$aid/$ver/$aid-$ver-javadoc.jar \
+ -Dclassifier=javadoc
+ elif [ -d $OUTPUT_DIR/$aid/$ver ] && [ $aid != "fdk" ]; then
+ if [ -f fallback/$aid-javadoc.jar ]; then
+ echo "Fallback javadocs2"
+ echo $OUTPUT_DIR/$aid/$ver
+ cp fallback/$aid-javadoc.jar $1/fdk/$ver/$aid/
+ mv $1/fdk/$ver/$aid/$aid-javadoc.jar $1/fdk/$ver/$aid/$aid-$ver-javadoc.jar
+ mvn -s ./user-settings.xml gpg:sign-and-deploy-file \
+ -Durl=$MAVEN_CENTRAL_STAGINGURL \
+ -DrepositoryId=$MAVEN_CENTRAL_REPOID \
+ -DpomFile=$pom \
+ -Dfile=$1/fdk/$ver/$aid/$aid-$ver-javadoc.jar \
+ -Dclassifier=javadoc
+ fi
+ fi
+ fi
+ #fi
+ #save_flag $i $j
+ done
+ START_VER[1]=0
+ done
+}
+
+#main
+#Script execution will start from here
+if [[ $1 = "-d" ]]; then
+ echo "Downloading fdk-java version from Bintray"
+ download_all_versions
+ save_flag $i $j
+elif [[ $1 = "-c" ]]; then
+ echo "Changing groupId to $2"
+ modify_group_id $2 $3
+ deploy $2 $3
+else
+ echo "Sign and Deploy to Maven Central"
+ echo $1
+ prepare_pom $1
+ deploy $1
+fi
+exit 0
diff --git a/build-image/Dockerfile b/build-image/Dockerfile
deleted file mode 100644
index 003c4107..00000000
--- a/build-image/Dockerfile
+++ /dev/null
@@ -1,7 +0,0 @@
-FROM maven:3-jdk-8-slim
-
-ADD pom.xml /tmp/cache-deps/pom.xml
-ADD cache-deps.sh /tmp/cache-deps/cache-deps.sh
-ADD src /tmp/cache-deps/src
-
-RUN /tmp/cache-deps/cache-deps.sh
diff --git a/build-image/Dockerfile-jdk9 b/build-image/Dockerfile-jdk9
deleted file mode 100644
index db4b8917..00000000
--- a/build-image/Dockerfile-jdk9
+++ /dev/null
@@ -1,7 +0,0 @@
-FROM maven:3-jdk-9-slim
-
-ADD pom.xml /tmp/cache-deps/pom.xml
-ADD cache-deps.sh /tmp/cache-deps/cache-deps.sh
-ADD src /tmp/cache-deps/src
-
-RUN /tmp/cache-deps/cache-deps.sh
diff --git a/build-image/cache-deps.sh b/build-image/cache-deps.sh
deleted file mode 100755
index c3a31654..00000000
--- a/build-image/cache-deps.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/bash -ex
-
-cd /tmp/cache-deps && mvn test package dependency:copy-dependencies -Dmaven.repo.local=/usr/share/maven/ref/repository -Dmdep.prependGroupId=true -DoutputDirectory=target
-cd / && rm -fr /tmp/cache-deps
diff --git a/build-image/docker-build.sh b/build-image/docker-build.sh
deleted file mode 100755
index 494b2cd7..00000000
--- a/build-image/docker-build.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash -ex
-
-cd /tmp/staging-repository && python -mSimpleHTTPServer 18080 1>>/tmp/http-logs 2>&1 &
-SRV_PROCESS=$!
-
-docker build $*
-kill $SRV_PROCESS
diff --git a/build-image/pom.xml b/build-image/pom.xml
deleted file mode 100644
index 6ae2415e..00000000
--- a/build-image/pom.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-
-
- 4.0.0
-
-
-
- UTF-8
- 1.0.0-SNAPSHOT
-
-
-
-
- com.fnproject.fn
- build-image
- 1.0.0
-
-
-
- com.fnproject.fn
- api
- ${fnproject.version}
-
-
- com.fnproject.fn
- testing
- ${fnproject.version}
- test
-
-
- junit
- junit
- 4.12
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 3.3
-
- 1.8
- 1.8
-
-
-
- org.apache.maven.plugins
- maven-deploy-plugin
- 2.8.2
-
- true
-
-
-
-
-
-
-
-
- fn-maven-releases
- http://172.17.0.1:18080
-
-
-
diff --git a/build-image/src/main/java/com/example/fn/HelloFunction.java b/build-image/src/main/java/com/example/fn/HelloFunction.java
deleted file mode 100644
index 8c581e76..00000000
--- a/build-image/src/main/java/com/example/fn/HelloFunction.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.example.fn;
-
-public class HelloFunction {
-
- public String handleRequest(String input) {
- String name = (input == null || input.isEmpty()) ? "world" : input;
-
- return "Hello, " + name + "!";
- }
-
-}
\ No newline at end of file
diff --git a/build-image/src/test/java/com/example/fn/HelloFunctionTest.java b/build-image/src/test/java/com/example/fn/HelloFunctionTest.java
deleted file mode 100644
index e6b7a5e3..00000000
--- a/build-image/src/test/java/com/example/fn/HelloFunctionTest.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.example.fn;
-
-import com.fnproject.fn.testing.*;
-import org.junit.*;
-
-import static org.junit.Assert.*;
-
-public class HelloFunctionTest {
-
- @Rule
- public final FnTestingRule testing = FnTestingRule.createDefault();
-
- @Test
- public void shouldReturnGreeting() {
- testing.givenEvent().enqueue();
- testing.thenRun(HelloFunction.class, "handleRequest");
-
- FnResult result = testing.getOnlyResult();
- assertEquals("Hello, world!", result.getBodyAsString());
- }
-
-}
\ No newline at end of file
diff --git a/build_in_docker.sh b/build_in_docker.sh
new file mode 100644
index 00000000..2081b9fc
--- /dev/null
+++ b/build_in_docker.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+docker run --rm --name compile -v "$(pwd)":/usr/src/mymaven -w /usr/src/mymaven svenruppert/maven-3.5-jdk-08 mvn clean install
diff --git a/docs/ExtendingDataBinding.md b/docs/ExtendingDataBinding.md
index bc8195c9..26a8a2d2 100644
--- a/docs/ExtendingDataBinding.md
+++ b/docs/ExtendingDataBinding.md
@@ -19,7 +19,7 @@ First of all, let's create a new function project. If you haven't done it alread
```shell
$ fn start &
-$ fn apps create java-app
+$ fn create app java-app
Successfully created app: java-app
```
diff --git a/docs/FAQ.md b/docs/FAQ.md
index 375650b5..4dfcb4aa 100644
--- a/docs/FAQ.md
+++ b/docs/FAQ.md
@@ -12,33 +12,16 @@ The FDK is comprised of:
- a build-time Docker image for repeatable builds.
### Is the FDK required in order to run Java on Fn?
-No. You can still write Java functions on Fn without using the FDK. However using the FDK will make several things easier for you:
- 1. A curated base image for Java 8 and Java 9 means that you don't have to build and maintain your own image. These images contain optimizations for quick JVM startup times.
- 1. Accessing configuration from Fn is easy through FDK APIs.
- 1. Input and output type coercion reduces the amount of serialization and formatting boilerplate that you have to write.
- 1. A JUnit rule provides a realistic test harness for you to test your function in isolation.
+Yes - The FDK implements the IO/contract with the FN service and is required to receive events from the platform
### What is Fn Flow?
Fn Flow is a [Java API](https://github.com/fnproject/fn-java-fdk/blob/master/docs/FnFlowsUserGuide.md) and [corresponding service](https://github.com/fnproject/flow) that helps you create complex, long-running, fault-tolerant functions using a promises-style asynchronous API. Check out the [Fn Flow docs](https://github.com/fnproject/fn-java-fdk/blob/master/docs/FnFlowsUserGuide.md) for more information.
### How do I get the FDK?
-The FDK is automatically added to your project if you built your function using `fn init --runtime=java`. The `api` and `testing` JARs are published on [our bintray](https://bintray.com/fnproject/fnproject) and the `runtime` is published in our [Docker hub repository](https://hub.docker.com/r/fnproject/fn-java-fdk/).
-
+The FDK is automatically added to your project if you built your function using `fn init --runtime=java`. The `api` and `testing` JARs are published on [our maven central repository](https://search.maven.org/search?q=g:com.fnproject.fn) and the `runtime` is published in our [Docker hub repository](https://hub.docker.com/r/fnproject/fn-java-fdk/).
### How do I add the FDK to an existing project?
- 1. Find the latest release from the [releases page](https://github.com/fnproject/fn-java-fdk/releases). For example `1.0.32`.
- 1. The FDK JAR is published on [Bintray](https://bintray.com/fnproject/fnproject). Add the repository to your`pom.xml` `repositories` section:
- ```xml
-
- fn-release-repo
- https://dl.bintray.com/fnproject/fnproject
-
- true
-
-
- false
-
-
- ```
+ 1. Find the latest release from the [releases page](https://github.com/fnproject/fn-java-fdk/releases). For example `1.0.149`.
+ 1. The FDK JAR is published on [Maven Central](https://search.maven.org/search?q=g:com.fnproject.fn). Add the repository to your`pom.xml` `repositories` section:
1. Add the dependency to your `dependency` section. Make sure that the `version` tag matches the latest release that you looked up above.
```xml
diff --git a/docs/FnFlowsAdvancedTopics.md b/docs/FnFlowsAdvancedTopics.md
index ac2f75a5..efd40e6d 100644
--- a/docs/FnFlowsAdvancedTopics.md
+++ b/docs/FnFlowsAdvancedTopics.md
@@ -61,6 +61,7 @@ An important consideration is that, if your lambda captures fields from your
function class, then that class must also be Serializable:
```java
+@FnFeature(FlowFeature.class)
public class MyFunction{
private String config = "foo";
@@ -82,6 +83,7 @@ E.g. making `MyFunction` serializable will work as the function instance object
will be captured alongside the lambda:
```java
+@FnFeature(FlowFeature.class)
public class MyFunction implements Serializable{
private String config = "foo";
@@ -104,6 +106,7 @@ prior to passing them, removing the need to make the function class
serializable. For example:
```java
+@FnFeature(FlowFeature.class)
public class MyFunction{
private final Database db; // non-serializable object
private final String config = "foo";
@@ -128,6 +131,7 @@ Alternatively, you can make non-serializable fields `transient` and construct
them on the fly:
```java
+@FnFeature(FlowFeature.class)
public class MyFunction implements Serialiable{
private final transient Database db; // non-serializable object
private final String config = "foo";
@@ -298,6 +302,7 @@ exception.
E.g.:
```java
+@FnFeature(FlowFeature.class)
public class MyFunction{
public static class MyException extends RuntimeException{
public MyException(String message){
diff --git a/docs/FnFlowsUserGuide.md b/docs/FnFlowsUserGuide.md
index 91ed4ca4..7834fe0c 100644
--- a/docs/FnFlowsUserGuide.md
+++ b/docs/FnFlowsUserGuide.md
@@ -60,7 +60,7 @@ $ fn start
Similarly, start the Flows server server and point it at the functions server API URL:
```
-$ DOCKER_LOCALHOST=$(docker inspect --type container -f '{{.NetworkSettings.Gateway}}' functions)
+$ DOCKER_LOCALHOST=$(docker inspect --type container -f '{{.NetworkSettings.Gateway}}' fnserver)
$ docker run --rm \
-p 8081:8081 \
@@ -99,6 +99,19 @@ func.yaml created
```
+### Add the Flow runtime to your function
+
+In your `pom.xml` add a depdendency on `flow-runtime` :
+
+```$ml
+
+ com.fnproject.fn
+ flow-runtime
+ ${fdk.version}
+
+
+```
+
### Create a Flow within your Function
You will create a function that produces the nth prime number and then returns
@@ -117,7 +130,10 @@ package com.example.fn;
import com.fnproject.fn.api.flow.Flow;
import com.fnproject.fn.api.flow.Flows;
+import com.fnproject.fn.runtime.flow.FlowFeature;
+import com.fnproject.fn.api.FnFeature;
+@FnFeature(FlowFeature.class)
public class PrimeFunction {
public String handleRequest(int nth) {
@@ -166,7 +182,7 @@ path: /primes
Create your app and deploy your function:
```
-$ fn apps create flows-example
+$ fn create app flows-example
Successfully created app: flows-example
$ fn deploy --app flows-example
@@ -178,20 +194,27 @@ Configure your function to talk to the local flow service endpoint:
```
$ DOCKER_LOCALHOST=$(docker inspect --type container -f '{{.NetworkSettings.Gateway}}' functions)
-$ fn apps config set flows-example COMPLETER_BASE_URL "http://$DOCKER_LOCALHOST:8081"
+$ fn config app flows-example COMPLETER_BASE_URL "http://$DOCKER_LOCALHOST:8081"
```
### Run your Flow function
-You can now run your function using `fn call` or HTTP and curl:
+You can now run your function using `fn invoke` or HTTP.
```
-$ echo 10 | fn call flows-example /primes
+$ echo 10 | fn invoke flows-example primes
The 10th prime number is 29
```
+To invoke your function via HTTP, you need to know its invocation endpoint (or the function needs to have an HTTP trigger defined).
+
+```
+$ fn inspect fn flows-examples primes
```
-$ curl -XPOST -d "10" http://localhost:8080/r/flows-example/primes
+
+Take note of the `fnproject.io/fn/invokeEndpoint` URL and invoke it (ex. using curl).
+
+$ curl -X POST -d "10" http://localhost:8080/invoke/...
The 10th prime number is 29
```
diff --git a/docs/HTTPGatewayFunctions.md b/docs/HTTPGatewayFunctions.md
new file mode 100644
index 00000000..86afd87a
--- /dev/null
+++ b/docs/HTTPGatewayFunctions.md
@@ -0,0 +1,36 @@
+# Accessing HTTP Information From Functions
+
+Functions can be used to handle events, RPC calls or HTTP requests. When you are writing a function that handles an HTTP request you frequently need access to the HTTP headers of the incoming request or need to set HTTP headers or the status code on the outbound respsonse.
+
+
+In Fn for Java, when your function is being served by an HTTP trigger (or another compatible HTTP gateway) you can get access to both the incoming request headers for your function by adding a 'com.fnproject.fn.api.httpgateway.HTTPGatewayContext' parameter to your function's parameters.
+
+
+ Using this allows you to :
+
+ * Read incoming headers
+ * Access the method and request URL for the trigger
+ * Write outbound headers to the response
+ * Set the status code of the response
+
+
+ For example this function reads a request header the method and request URL, sets an response header and sets the response status code to perform an HTTP redirect.
+
+```java
+package com.fnproject.fn.examples;
+import com.fnproject.fn.api.httpgateway.HTTPGatewayContext;
+
+
+public class RedirectFunction {
+
+ public void redirect(HTTPGatewayContext hctx) {
+ System.err.println("Request URL is:" + hctx.getRequestURL());
+ System.err.println("Trace ID" + hctx.getHeaders().get("My-Trace-ID").orElse("N/A"));
+
+ hctx.setResponseHeader("Location","http://example.com");
+ hctx.setStatusCode(302);
+
+ }
+}
+
+```
diff --git a/docs/TestingFunctions.md b/docs/TestingFunctions.md
index ffabd74d..9127b16e 100644
--- a/docs/TestingFunctions.md
+++ b/docs/TestingFunctions.md
@@ -16,7 +16,7 @@ To import the testing library add the following dependency to your Maven project
com.fnproject.fntesting
- 1.0.0-SNAPSHOT
+ ${fdk.version}test
```
@@ -155,9 +155,33 @@ You can test that this is all handled correctly as follows:
# Testing Fn Flows
-You can use `FnTestingRule` to test [Fn Flows](FnFlowsUserGuide.md) within your functions. If flow stages are started by functions within `thenRun` then the testing rule will execute the stages of those flows locally, returning when all spawned flows are complete.
+You can use `FlowTesting` to test [Fn Flows](FnFlowsUserGuide.md) within your functions. If flow stages are started by functions within `thenRun` then the testing rule will execute the stages of those flows locally, returning when all spawned flows are complete.
-`FnTestingRule` supports mocking the behaviour of Fn functions invoked by the `invokeFunction()` API within flows.
+Start by importing the `flow-testing` library into your functino in `test` scope:
+
+```xml
+
+ com.fnproject.fn
+ flow-testing
+ ${fdk.version}
+ test
+
+```
+
+Then create a `FlowTesting` field in your test class, passing the `FnTesting` rule as a parameter:
+
+```java
+import com.fnproject.fn.testing.FnTestingRule;
+import com.fnproject.fn.testing.flow.FlowTesting;
+
+public class FunctionTest {
+ @Rule
+ public final FnTestingRule testing = FnTestingRule.createDefault();
+
+ private final FlowTesting flowTesting = FlowTesting.create(testing);
+```
+
+`FlowTesting` supports mocking the behaviour of Fn functions invoked by the `invokeFunction()` API within flows.
You can specify that the invocation a function returns a valid value (as a byte array):
@@ -165,7 +189,7 @@ You can specify that the invocation a function returns a valid value (as a byte
@Test
public void callsRemoteFunctionWhichSucceeds() {
- testing.givenFn("example/other-function").withResult("blah".getBytes());
+ flowTesting.givenFn("example/other-function").withResult("blah".getBytes());
// ...
@@ -178,8 +202,8 @@ Or you can specify that the invocation a function will cause a user error or a p
@Test
public void callsRemoteFunctionWhichCausesAnError() {
- testing.givenFn("example/other-function").withFunctionError();
- testing.givenFn("example/other-function-2").withPlatformError();
+ flowTesting.givenFn("example/other-function").withFunctionError();
+ flowTesting.givenFn("example/other-function-2").withPlatformError();
// ...
@@ -196,7 +220,7 @@ used to check some behavior:
@Test
public void callsRemoteFunction() {
- testing.givenFn("example/other-function").withAction( (data) -> { called.set(true); return data; } );
+ flowTesting.givenFn("example/other-function").withAction( (data) -> { called.set(true); return data; } );
called.set(false);
@@ -221,7 +245,7 @@ If you need to share objects or static data between your test classes and your f
```java
testing.addSharedClass(MyClassWithStaticState.class); // Shares only the specific class
testing.addSharedPrefix("com.example.MyClassWithStaticState"); // Shares the class and anything under it
- testing.addSharedPrefix("com.example.mysubpackage."); // Shares anyhting under a package
+ testing.addSharedPrefix("com.example.mysubpackage."); // Shares anything under a package
```
While it is possible, it is not generally correct to share the function class itself with the test Class Loader - doing so may result in unexpected (not representative of the real fn platform) initialisation of static fields on the class. With Flows sharing the test class may also result in concurrent access to static data (via `@FnConfiguration` methods).
\ No newline at end of file
diff --git a/examples/README.md b/examples/README.md
index 4600bd90..620fbee6 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -1,36 +1,48 @@
-# `fn` Java FDK Example Projects
+# Fn Java FDK Example Projects
In this directory you will find some example projects demonstrating different
-features of the `fn` Java FDK:
+features of the Fn Java FDK:
* Plain java code support (`string-reverse`)
* Functional testing of your functions (`regex-query` and `qr-code`)
* Built-in JSON coercion (`regex-query`)
* [InputEvent and OutputEvent](/docs/DataBinding.md) handling (`qr-code`)
-## 1. String reverse
+## (1) [String reverse](string-reverse/README.md)
This function takes a string and returns the reverse of the string.
-The `fn` Java FDK runtime will handle marshalling data into your
-functions without the function having to have any knowledge of the FDK API.
+The Fn Java FDK handles marshalling data into your
+functions without the function having any knowledge of the FDK API.
-## 2. Regex query
+## (2) Regex query
This function takes a JSON object containing a `text` field and a `regex`
field and return a JSON object with a list of matches in the `matches`
field. It demonstrates the builtin JSON support of the fn Java
-wrapper (provided through Jackson) and how the platform handles serialisation
+wrapper (provided through Jackson) and how the platform handles serialization
of POJO return values.
-## 3. QR Code gen
+## (3) QR Code gen
This function parses the query parameters of a GET request (through the
`InputEvent` passed into the function) to generate a QR code. It demonstrates
the `InputEvent` and `OutputEvent` interfaces which provide low level
access to data entering the `fn` Java FDK.
-## 4. Asynchronous thumbnails generation
+## (4) Asynchronous thumbnails generation
This example showcases the Fn Flow asynchronous execution API, by
creating a workflow that takes an image and asynchronously generates three
thumbnails for it, then uploads them to an object storage.
+
+## (5) Gradle build
+This shows how to use Gradle to build functions using the Java FDK.
+
+## (6) Fn Events
+This shows how to use fn-events library for:
+- OCI API Gateway Function - [README.md](../examples/apigateway-event/README.md)
+- OCI Service Connector Hub: Monitoring - [README.md](../examples/connectorhub-monitoring/README.md)
+- OCI Service Connector Hub: Logging - [README.md](../examples/connectorhub-logging/README.md)
+- OCI Service Connector Hub: Streaming - [README.md](../examples/connectorhub-streaming/README.md)
+- OCI Service Connector Hub: Queue - [connectorhub-queue](../examples/connectorhub-queue)
+- OCI Notifications - [README.md](../examples/notifications/README.md)
\ No newline at end of file
diff --git a/examples/apigateway-event/README.md b/examples/apigateway-event/README.md
new file mode 100644
index 00000000..0d3277e2
--- /dev/null
+++ b/examples/apigateway-event/README.md
@@ -0,0 +1,135 @@
+# Example Fn Java FDK : API Gateway
+
+This example provides a Function to use as an API Gateway backend.
+The function accepts a typed request for easy object handling and returns
+http response.
+
+## Dependencies
+
+* [fn-events] for APIGatewayFunction classes.
+* [fn-events-testing] for APIGatewayFunction testing library.
+
+## Demonstrated FDK features
+
+This example showcases how to use the fn-event APIGatewayFunction to
+use a Function as the backend.
+
+## Step by step
+
+Set the API Gateway and Function
+backend [Adding a Function in OCI Functions as an API Gateway Back End](https://docs.oracle.com/en-us/iaas/Content/APIGateway/Tasks/apigatewayusingfunctionsbackend.htm)
+
+The Function entrypoint extends the `APIGatewayFunction` abstract class.
+Note: the [func.yaml](func.yaml) entrypoint remains the class which extends `APIGatewayFunction`
+e.g. `cmd: com.fnproject.fn.examples.Function::handler`
+
+
+[Function.java](src/main/java/com/fnproject/fn/examples/Function.java)
+```java
+import com.fnproject.events.APIGatewayFunction;
+import com.fnproject.events.input.APIGatewayRequestEvent;
+import com.fnproject.events.output.APIGatewayResponseEvent;
+import com.fnproject.fn.api.Headers;
+import org.apache.http.HttpStatus;
+
+public class Function extends APIGatewayFunction {
+
+ @Override
+ public APIGatewayResponseEvent handler(APIGatewayRequestEvent requestEvent) {
+ ResponseEmployee employee = new ResponseEmployee();
+ Optional id = requestEvent.getQueryParameters().get("id");
+ id.ifPresent(s -> employee.setId(Integer.parseInt(s)));
+
+ if (requestEvent.getBody() != null) {
+ employee.setName(requestEvent.getBody().getName());
+ }
+
+ return new APIGatewayResponseEvent.Builder()
+ .statusCode(HttpStatus.SC_CREATED)
+ .headers(Headers.emptyHeaders()
+ .addHeader("X-Custom-Header", "HeaderValue")
+ .addHeader("X-Custom-Header-2", "HeaderValue2"))
+ .body(employee)
+ .build();
+ }
+}
+```
+The APIGatewayRequestEvent.class `requestUrl` is relative to the deployment
+path prefix [see API Gateway using HTTP backend](https://docs.oracle.com/en-us/iaas/Content/APIGateway/Tasks/apigatewayusinghttpbackend.htm#usingjson)
+
+
+The class [RequestEmployee.java](src/main/java/com/fnproject/fn/examples/RequestEmployee.java) is the request body type and
+[ResponseEmployee.java](src/main/java/com/fnproject/fn/examples/ResponseEmployee.java) is the response body type.
+These are passed in position 1 and 2 of abstract class
+[Function.java](src/main/java/com/fnproject/fn/examples/Function.java)
+`public class Function extends APIGatewayFunction {`.
+
+To return an error response, throw RuntimeException.class.
+
+## Test walkthrough
+
+Unit testing `APIGatewayFunction` is supported with the `APIGatewayTestFeature` and `FnTestingRule`.
+
+First of all, the class initializes the `FnTestingRule` harness, as explained
+in [Testing Functions](../../docs/TestingFunctions.md).
+
+[FunctionTest.java](src/test/java/com/fnproject/fn/examples/FunctionTest.java)
+```java
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import com.fnproject.events.input.APIGatewayRequestEvent;
+import com.fnproject.events.output.APIGatewayResponseEvent;
+import com.fnproject.events.testing.APIGatewayTestFeature;
+import com.fnproject.fn.testing.FnTestingRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class FunctionTest {
+
+ @Rule
+ public FnTestingRule fn = FnTestingRule.createDefault();
+
+ private final APIGatewayTestFeature apiGatewayFeature = APIGatewayTestFeature.createDefault(fn);
+
+ @Test
+ public void testHttpAttributes() throws IOException {
+ RequestEmployee requestEmployee = new RequestEmployee();
+ requestEmployee.setName("John");
+
+ APIGatewayRequestEvent event = mock(APIGatewayRequestEvent.class);
+
+ when(event.getBody()).thenReturn(requestEmployee);
+ when(event.getMethod()).thenReturn("POST");
+ when(event.getRequestUrl()).thenReturn("/v2?id=123");
+ when(event.getQueryParameters()).thenReturn(new QueryParametersImpl(Collections.singletonMap("id", Collections.singletonList("123"))));
+ when(event.getHeaders()).thenReturn(Collections.unmodifiableMap(new HashMap>() {{
+ put("myHeader", Collections.singletonList("headerValue"));
+ }}));
+
+ apiGatewayFeature.givenEvent(event)
+ .enqueue();
+
+ fn.thenRun(Function.class, "handler");
+
+ APIGatewayResponseEvent responseEvent = apiGatewayFeature.getResult(ResponseEmployee.class);
+
+ ResponseEmployee responseEventBody = responseEvent.getBody();
+ assertEquals(123, responseEventBody.getId());
+ assertEquals("John", responseEventBody.getName());
+ assertEquals(Integer.valueOf(201), responseEvent.getStatus());
+ assertEquals("HeaderValue", responseEvent.getHeaders().get("X-Custom-Header").get(0));
+ assertEquals("HeaderValue2", responseEvent.getHeaders().get("X-Custom-Header-2").get(0));
+ }
+}
+```
+
+Use `apiGatewayFeature.givenEvent(event).enqueue();` to queue the request event
+and invoke the Function with `fn.thenRun(Function.class, "handler");`.
+
+And get the Function response using
+`APIGatewayResponseEvent responseEvent = apiGatewayFeature.getResult(ResponseEmployee.class);`
diff --git a/examples/apigateway-event/func.yaml b/examples/apigateway-event/func.yaml
new file mode 100644
index 00000000..1f72ed08
--- /dev/null
+++ b/examples/apigateway-event/func.yaml
@@ -0,0 +1,5 @@
+schema_version: 20180708
+name: apigateway-event
+version: 0.0.1
+runtime: java
+cmd: com.fnproject.fn.examples.Function::handler
diff --git a/examples/apigateway-event/pom.xml b/examples/apigateway-event/pom.xml
new file mode 100644
index 00000000..e1b8de57
--- /dev/null
+++ b/examples/apigateway-event/pom.xml
@@ -0,0 +1,100 @@
+
+
+
+
+ 4.0.0
+
+
+ UTF-8
+ UTF-8
+ 1.0.0-SNAPSHOT
+ 2.16.1
+ 4.0.0
+
+
+ com.fnproject.fn.examples
+ apigateway-event
+ 1.0.0-SNAPSHOT
+
+
+
+ com.fnproject.fn
+ api
+ ${fdk.version}
+
+
+ com.fnproject.fn
+ fn-events
+ ${fdk.version}
+
+
+ com.fnproject.fn
+ fn-events-testing
+ ${fdk.version}
+ test
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
+
+ org.mockito
+ mockito-core
+ ${mockito.version}
+ test
+
+
+ com.fnproject.fn
+ testing-core
+ ${fdk.version}
+ test
+
+
+ com.fnproject.fn
+ testing-junit4
+ ${fdk.version}
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.0
+
+ 1.8
+ 1.8
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.8.2
+
+ true
+
+
+
+
+
diff --git a/examples/apigateway-event/src/main/java/com/fnproject/fn/examples/EmployeeService.java b/examples/apigateway-event/src/main/java/com/fnproject/fn/examples/EmployeeService.java
new file mode 100644
index 00000000..8b9357cb
--- /dev/null
+++ b/examples/apigateway-event/src/main/java/com/fnproject/fn/examples/EmployeeService.java
@@ -0,0 +1,19 @@
+package com.fnproject.fn.examples;
+
+import java.util.Optional;
+
+public class EmployeeService {
+
+ public ResponseEmployee createEmployee(RequestEmployee requestEmployee, Optional id) {
+ if (requestEmployee == null) {
+ throw new IllegalArgumentException("requestEmployee must not be null");
+ }
+ if (!id.isPresent()) {
+ throw new IllegalArgumentException("id must not be null");
+ }
+ ResponseEmployee employee = new ResponseEmployee();
+ employee.setId(Integer.parseInt(id.get()));
+ employee.setName(requestEmployee.getName());
+ return employee;
+ }
+}
diff --git a/examples/apigateway-event/src/main/java/com/fnproject/fn/examples/Function.java b/examples/apigateway-event/src/main/java/com/fnproject/fn/examples/Function.java
new file mode 100644
index 00000000..bf276d34
--- /dev/null
+++ b/examples/apigateway-event/src/main/java/com/fnproject/fn/examples/Function.java
@@ -0,0 +1,34 @@
+package com.fnproject.fn.examples;
+
+import java.util.Optional;
+import com.fnproject.events.APIGatewayFunction;
+import com.fnproject.events.input.APIGatewayRequestEvent;
+import com.fnproject.events.output.APIGatewayResponseEvent;
+import com.fnproject.fn.api.Headers;
+import org.apache.http.HttpStatus;
+
+
+public class Function extends APIGatewayFunction {
+
+ private final EmployeeService employeeService;
+
+ public Function() {
+ this.employeeService = new EmployeeService();
+ }
+
+ @Override
+ public APIGatewayResponseEvent handler(APIGatewayRequestEvent requestEvent) {
+ Optional id = requestEvent.getQueryParameters().get("id");
+ RequestEmployee requestEmployee = requestEvent.getBody();
+
+ ResponseEmployee responseEmployee = employeeService.createEmployee(requestEmployee, id);
+
+ return new APIGatewayResponseEvent.Builder()
+ .statusCode(HttpStatus.SC_CREATED)
+ .headers(Headers.emptyHeaders()
+ .addHeader("X-Custom-Header", "HeaderValue")
+ .addHeader("X-Custom-Header-2", "HeaderValue2"))
+ .body(responseEmployee)
+ .build();
+ }
+}
diff --git a/examples/apigateway-event/src/main/java/com/fnproject/fn/examples/RequestEmployee.java b/examples/apigateway-event/src/main/java/com/fnproject/fn/examples/RequestEmployee.java
new file mode 100644
index 00000000..7e1cc74c
--- /dev/null
+++ b/examples/apigateway-event/src/main/java/com/fnproject/fn/examples/RequestEmployee.java
@@ -0,0 +1,16 @@
+package com.fnproject.fn.examples;
+
+public class RequestEmployee {
+ private String name;
+
+ public RequestEmployee() {}
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
diff --git a/examples/apigateway-event/src/main/java/com/fnproject/fn/examples/ResponseEmployee.java b/examples/apigateway-event/src/main/java/com/fnproject/fn/examples/ResponseEmployee.java
new file mode 100644
index 00000000..9d056c57
--- /dev/null
+++ b/examples/apigateway-event/src/main/java/com/fnproject/fn/examples/ResponseEmployee.java
@@ -0,0 +1,40 @@
+package com.fnproject.fn.examples;
+
+import java.util.Objects;
+
+public class ResponseEmployee {
+ private Integer id;
+ private String name;
+
+ public ResponseEmployee() {}
+
+ public Integer getId() {
+ return this.id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ResponseEmployee employee = (ResponseEmployee) o;
+ return id.equals(employee.id) &&
+ Objects.equals(name, employee.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, name);
+ }
+}
\ No newline at end of file
diff --git a/examples/apigateway-event/src/test/java/com/fnproject/fn/examples/FunctionTest.java b/examples/apigateway-event/src/test/java/com/fnproject/fn/examples/FunctionTest.java
new file mode 100644
index 00000000..417ec880
--- /dev/null
+++ b/examples/apigateway-event/src/test/java/com/fnproject/fn/examples/FunctionTest.java
@@ -0,0 +1,96 @@
+package com.fnproject.fn.examples;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import java.io.IOException;
+import java.util.Collections;
+import com.fnproject.events.input.APIGatewayRequestEvent;
+import com.fnproject.events.output.APIGatewayResponseEvent;
+import com.fnproject.events.testing.APIGatewayTestFeature;
+import com.fnproject.fn.api.Headers;
+import com.fnproject.fn.runtime.httpgateway.QueryParametersImpl;
+import com.fnproject.fn.testing.FnResult;
+import com.fnproject.fn.testing.FnTestingRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class FunctionTest {
+
+ @Rule
+ public FnTestingRule fn = FnTestingRule.createDefault();
+
+ private final APIGatewayTestFeature apiGatewayFeature = APIGatewayTestFeature.createDefault(fn);
+
+ @Test
+ public void testGetResponseBody() throws IOException {
+ APIGatewayRequestEvent event = createMinimalRequest();
+
+ apiGatewayFeature.givenEvent(event)
+ .enqueue();
+
+ fn.thenRun(Function.class, "handler");
+
+ APIGatewayResponseEvent responseEvent = apiGatewayFeature.getResult(ResponseEmployee.class);
+
+ ResponseEmployee responseEventBody = responseEvent.getBody();
+ assertEquals(Integer.valueOf(123), responseEventBody.getId());
+ assertEquals("John", responseEventBody.getName());
+ }
+
+ @Test
+ public void testGetResponseHeaders() throws IOException {
+ APIGatewayRequestEvent event = createMinimalRequest();
+
+ when(event.getHeaders()).thenReturn(Headers.emptyHeaders().addHeader("myHeader", "headerValue"));
+
+ apiGatewayFeature.givenEvent(event)
+ .enqueue();
+
+ fn.thenRun(Function.class, "handler");
+
+ APIGatewayResponseEvent responseEvent = apiGatewayFeature.getResult(ResponseEmployee.class);
+ assertEquals("HeaderValue", responseEvent.getHeaders().getAllValues("X-Custom-Header").get(0));
+ assertEquals("HeaderValue2", responseEvent.getHeaders().get("X-Custom-Header-2").get());
+ }
+
+ @Test
+ public void testGetResponseStatus() throws IOException {
+ APIGatewayRequestEvent event = createMinimalRequest();
+
+ apiGatewayFeature.givenEvent(event)
+ .enqueue();
+
+ fn.thenRun(Function.class, "handler");
+
+ APIGatewayResponseEvent responseEvent = apiGatewayFeature.getResult(ResponseEmployee.class);
+
+ assertEquals(Integer.valueOf(201), responseEvent.getStatus());
+ }
+
+ @Test
+ public void testErrorResponse() throws IOException {
+ APIGatewayRequestEvent event = mock(APIGatewayRequestEvent.class);
+
+ apiGatewayFeature.givenEvent(event)
+ .enqueue();
+
+ fn.thenRun(Function.class, "handler");
+
+ FnResult result = fn.getOnlyResult();
+ assertEquals(502, result.getStatus().getCode());
+ assertEquals("An error occurred in function: requestEmployee must not be null\n" +
+ "Caused by: java.lang.IllegalArgumentException: requestEmployee must not be null\n\n", fn.getStdErrAsString());
+ assertEquals(1, fn.getLastExitCode());
+ }
+
+ private static APIGatewayRequestEvent createMinimalRequest() {
+ RequestEmployee requestEmployee = new RequestEmployee();
+ requestEmployee.setName("John");
+ APIGatewayRequestEvent event = mock(APIGatewayRequestEvent.class);
+
+ when(event.getBody()).thenReturn(requestEmployee);
+ when(event.getQueryParameters()).thenReturn(new QueryParametersImpl(Collections.singletonMap("id", Collections.singletonList("123"))));
+ return event;
+ }
+}
\ No newline at end of file
diff --git a/examples/async-thumbnails/README.md b/examples/async-thumbnails/README.md
index 10616498..b3cff27d 100644
--- a/examples/async-thumbnails/README.md
+++ b/examples/async-thumbnails/README.md
@@ -32,9 +32,8 @@ this example. Run:
```
This will start a local functions service, a local flow completion
-service, and will set up a `myapp` application and three routes: `/resize128`,
-`/resize256` and `/resize512`. The routes are implemented as Fn functions
-which just invoke `imagemagick` to convert the images to the specified sizes.
+service, and will set up a `myapp` application and three functions: `resize128`,
+`resize256` and `resize512`. These functions just invoke `imagemagick` to convert the images to the specified sizes.
The setup script also starts a docker container with an object storage daemon
based on `minio` (with access key `alpha` and secret key `betabetabetabeta`).
@@ -48,14 +47,9 @@ docker container, so that you can verify when the thumbnails are uploaded.
Build the function locally:
```bash
-$ fn build
+$ fn deploy --local --app myapp
```
-Create a route to host the function:
-
-```bash
-$ fn routes create myapp /async-thumbnails
-```
Configure the app. In order to do this you must determine the IP address of the
storage server docker container:
@@ -68,18 +62,18 @@ $ docker inspect --type container -f '{{range .NetworkSettings.Networks}}{{.IPAd
and then use it as the storage host:
```bash
-$ fn routes config set myapp /async-thumbnails OBJECT_STORAGE_URL http://172.17.0.4:9000
+$ fn config app myapp OBJECT_STORAGE_URL http://172.17.0.4:9000
myapp /async-thumbnails updated OBJECT_STORAGE_URL with http://172.17.0.4:9000
-$ fn routes config set myapp /async-thumbnails OBJECT_STORAGE_ACCESS alpha
+$ fn config app myapp OBJECT_STORAGE_ACCESS alpha
myapp /async-thumbnails updated OBJECT_STORAGE_ACCESS with alpha
-$ fn routes config set myapp /async-thumbnails OBJECT_STORAGE_SECRET betabetabetabeta
+$ fn config app myapp OBJECT_STORAGE_SECRET betabetabetabeta
myapp /async-thumbnails updated OBJECT_STORAGE_SECRET with betabetabetabeta
```
Invoke the function by passing the provided test image:
```bash
-$ curl -X POST --data-binary @test-image.png -H "Content-type: application/octet-stream" "http://localhost:8080/r/myapp/async-thumbnails"
+$ curl -X POST --data-binary @test-image.png -H "Content-type: application/octet-stream" "http://localhost:8080/t/myapp/async-thumbnails"
{"imageId":"bd74fff4-0388-4c6f-82f2-8cde9ba9b6fc"}
```
@@ -116,6 +110,13 @@ public class ThumbnailsFunction {
.orElseThrow(() -> new RuntimeException("Missing configuration: OBJECT_STORAGE_ACCESS"));
storageSecretKey = ctx.getConfigurationByKey("OBJECT_STORAGE_SECRET")
.orElseThrow(() -> new RuntimeException("Missing configuration: OBJECT_STORAGE_SECRET"));
+
+ resize128ID = ctx.getConfigurationByKey("RESIZE_128_FN_ID")
+ .orElseThrow(() -> new RuntimeException("Missing configuration: RESIZE_128_FN_ID"));
+ resize256ID = ctx.getConfigurationByKey("RESIZE_256_FN_ID")
+ .orElseThrow(() -> new RuntimeException("Missing configuration: RESIZE_256_FN_ID"));
+ resize512ID = ctx.getConfigurationByKey("RESIZE_512_FN_ID")
+ .orElseThrow(() -> new RuntimeException("Missing configuration: RESIZE_512_FN_ID"));
}
// ...
@@ -155,11 +156,11 @@ public class ThumbnailsFunction {
Flow runtime = Flows.currentFlow();
runtime.allOf(
- runtime.invokeFunction("myapp/resize128", HttpMethod.POST, Headers.emptyHeaders(), imageBuffer)
+ runtime.invokeFunction(resize128ID, HttpMethod.POST, Headers.emptyHeaders(), imageBuffer)
.thenAccept((img) -> objectUpload(img.getBodyAsBytes(), id + "-128.png")),
- runtime.invokeFunction("myapp/resize256", HttpMethod.POST, Headers.emptyHeaders(), imageBuffer)
+ runtime.invokeFunction(resize256ID, HttpMethod.POST, Headers.emptyHeaders(), imageBuffer)
.thenAccept((img) -> objectUpload(img.getBodyAsBytes(), id + "-256.png")),
- runtime.invokeFunction("myapp/resize512", HttpMethod.POST, Headers.emptyHeaders(), imageBuffer)
+ runtime.invokeFunction(resize512ID, HttpMethod.POST, Headers.emptyHeaders(), imageBuffer)
.thenAccept((img) -> objectUpload(img.getBodyAsBytes(), id + "-512.png")),
runtime.supply(() -> objectUpload(imageBuffer, id + ".png"))
);
@@ -218,8 +219,9 @@ in [Testing Functions](../../docs/TestingFunctions.md).
```java
public class ThumbnailsFunctionTest {
- @Rule
- public final FnTestingRule testing = FnTestingRule.createDefault();
+ @Rule
+ public final FnTestingRule fn = FnTestingRule.createDefault();
+ private final FlowTesting flow = FlowTesting.create(fn);
// ...
}
@@ -259,20 +261,22 @@ public class ThumbnailsFunctionTest {
@Test
public void testThumbnail() {
- testing
- .setConfig("OBJECT_STORAGE_URL", "http://localhost:" + mockServer.port())
- .setConfig("OBJECT_STORAGE_ACCESS", "alpha")
- .setConfig("OBJECT_STORAGE_SECRET", "betabetabetabeta")
+ fn.setConfig("OBJECT_STORAGE_URL", "http://localhost:" + mockServer.port())
+ .setConfig("OBJECT_STORAGE_ACCESS", "alpha")
+ .setConfig("OBJECT_STORAGE_SECRET", "betabetabetabeta")
+ .setConfig("RESIZE_128_FN_ID","myapp/resize128")
+ .setConfig("RESIZE_256_FN_ID","myapp/resize256")
+ .setConfig("RESIZE_512_FN_ID","myapp/resize512");
- .givenFn("myapp/resize128")
+ flow.givenFn("myapp/resize128")
.withAction((data) -> "128".getBytes())
.givenFn("myapp/resize256")
.withAction((data) -> "256".getBytes())
.givenFn("myapp/resize512")
.withAction((data) -> "512".getBytes())
- .givenEvent()
+ fn.givenEvent()
.withBody("testing".getBytes())
.enqueue();
@@ -301,21 +305,23 @@ public class ThumbnailsFunctionTest {
@Test
public void anExternalFunctionFailure() {
- testing
- .setConfig("OBJECT_STORAGE_URL", "http://localhost:" + mockServer.port())
- .setConfig("OBJECT_STORAGE_ACCESS", "alpha")
- .setConfig("OBJECT_STORAGE_SECRET", "betabetabetabeta")
-
- .givenFn("myapp/resize128")
- .withResult("128".getBytes())
- .givenFn("myapp/resize256")
- .withResult("256".getBytes())
- .givenFn("myapp/resize512")
- .withFunctionError()
-
- .givenEvent()
- .withBody("testing".getBytes())
- .enqueue();
+ fn.setConfig("OBJECT_STORAGE_URL", "http://localhost:" + mockServer.port())
+ .setConfig("OBJECT_STORAGE_ACCESS", "alpha")
+ .setConfig("OBJECT_STORAGE_SECRET", "betabetabetabeta")
+ .setConfig("RESIZE_128_FN_ID","myapp/resize128")
+ .setConfig("RESIZE_256_FN_ID","myapp/resize256")
+ .setConfig("RESIZE_512_FN_ID","myapp/resize512");
+
+ flow.givenFn("myapp/resize128")
+ .withResult("128".getBytes())
+ .givenFn("myapp/resize256")
+ .withResult("256".getBytes())
+ .givenFn("myapp/resize512")
+ .withFunctionError();
+
+ fn.givenEvent()
+ .withBody("testing".getBytes())
+ .enqueue();
// Mock the http endpoint
mockMinio();
diff --git a/examples/async-thumbnails/func.yaml b/examples/async-thumbnails/func.yaml
index d6003dc4..1f1f0d05 100644
--- a/examples/async-thumbnails/func.yaml
+++ b/examples/async-thumbnails/func.yaml
@@ -1,7 +1,27 @@
-name: fn-example/async-thumbnails
-version: 0.0.1
+#
+# Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+schema_version: 20180708
+name: async-thumbnails
+version: 0.0.8
runtime: java
cmd: com.fnproject.fn.examples.ThumbnailsFunction::handleRequest
-path: /async-thumbnails
-format: http
-timeout: 30
+format: http-stream
+timeout: 120
+triggers:
+- name: async-thumbnails
+ type: http
+ source: /async-thumbnails
diff --git a/examples/async-thumbnails/pom.xml b/examples/async-thumbnails/pom.xml
index e554bee8..0a28f5fb 100644
--- a/examples/async-thumbnails/pom.xml
+++ b/examples/async-thumbnails/pom.xml
@@ -1,4 +1,22 @@
+
+
@@ -6,8 +24,11 @@
UTF-8
- 1.0.0-SNAPSHOT
- 2.8.47
+ UTF-8
+
+ 1.0.0-SNAPSHOT
+ 3.3.3
+ 2.16.1com.fnproject.fn.examples
@@ -15,38 +36,137 @@
1.0.0-SNAPSHOT
+
com.fnproject.fnapi
- ${fnproject.version}
+ ${fdk.version}
+
+
+ com.fnproject.fn
+ flow-runtime
+ ${fdk.version}commons-netcommons-net
- 3.6
+ 3.8.0
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson.version}io.miniominio
- 3.0.5
+ 7.1.3
+
+
+ com.squareup.okhttp3
+ okhttp
+
+
+
+
+ com.squareup.okhttp3
+ okhttp
+ 4.9.3
+
+
+ com.fnproject.fn
+ testing-core
+ ${fdk.version}
+ test
+
+
+ com.fnproject.fn
+ flow-testing
+ ${fdk.version}
+ testcom.fnproject.fn
- testing
- ${fnproject.version}
+ testing-junit4
+ ${fdk.version}testjunitjunit
- 4.12
+ 4.13.2testcom.github.tomakehurstwiremock
- 2.7.1
+ 2.27.2
+
+
+ com.jayway.jsonpath
+ json-path
+
+
+ com.github.jknack
+ handlebars
+
+
+ org.apache.httpcomponents
+ httpclient
+
+
+
+
+
+ org.eclipse.jetty
+ jetty-server
+ 9.4.44.v20210927
+
+
+ org.eclipse.jetty
+ jetty-servlet
+ 9.4.44.v20210927
+
+
+ org.eclipse.jetty
+ jetty-servlets
+ 9.4.44.v20210927
+
+
+ org.eclipse.jetty
+ jetty-webapp
+ 9.4.44.v20210927
+
+
+
+ com.jayway.jsonpath
+ json-path
+ 2.6.0
+
+
+
+ com.github.jknack
+ handlebars
+ 4.2.1
+
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.5.13
+
+
+ commons-codec
+ commons-codec
+
+
+
+
+
+ commons-codec
+ commons-codec
+ 1.15
@@ -55,7 +175,7 @@
org.apache.maven.pluginsmaven-compiler-plugin
- 3.3
+ 3.8.01.81.8
@@ -71,11 +191,4 @@
-
-
-
- fn-maven-releases
- https://dl.bintray.com/fnproject/fnproject
-
-
diff --git a/examples/async-thumbnails/run.sh b/examples/async-thumbnails/run.sh
index 76315c0f..9744fd5c 100755
--- a/examples/async-thumbnails/run.sh
+++ b/examples/async-thumbnails/run.sh
@@ -1,15 +1,27 @@
#!/bin/bash
+#
+# Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
-fn build
+set -e
-fn routes create myapp /async-thumbnails
+fn --verbose deploy --app myapp --local
-STORAGE_SERVER_IP=`docker inspect --type container -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' example-storage-server`
-fn routes config set myapp /async-thumbnails OBJECT_STORAGE_URL http://${STORAGE_SERVER_IP}:9000
-fn routes config set myapp /async-thumbnails OBJECT_STORAGE_ACCESS alpha
-fn routes config set myapp /async-thumbnails OBJECT_STORAGE_SECRET betabetabetabeta
-curl -X POST --data-binary @test-image.png -H "Content-type: application/octet-stream" "http://localhost:8080/r/myapp/async-thumbnails"
+echo "Calling function"
+curl -v -X POST --data-binary @test-image.png -H "Content-type: application/octet-stream" "http://localhost:8080/t/myapp/async-thumbnails"
echo "Contents of bucket"
mc ls -r example-storage-server
diff --git a/examples/async-thumbnails/setup/resize128/Dockerfile b/examples/async-thumbnails/setup/resize128/Dockerfile
index 20f55eec..65442048 100644
--- a/examples/async-thumbnails/setup/resize128/Dockerfile
+++ b/examples/async-thumbnails/setup/resize128/Dockerfile
@@ -1,3 +1,19 @@
+#
+# Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
FROM debian
RUN apt-get update
RUN apt-get install -y imagemagick
diff --git a/examples/async-thumbnails/setup/resize128/func.yaml b/examples/async-thumbnails/setup/resize128/func.yaml
index b7a5071b..4f6ea681 100644
--- a/examples/async-thumbnails/setup/resize128/func.yaml
+++ b/examples/async-thumbnails/setup/resize128/func.yaml
@@ -1,4 +1,21 @@
-name: example/resize128
-version: 0.0.1
+#
+# Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+schema_version: 20180708
+name: resize128
+version: 0.0.5
entrypoint: convert - -resize 128x128 -
-path: /resize128
+format: default
diff --git a/examples/async-thumbnails/setup/resize256/Dockerfile b/examples/async-thumbnails/setup/resize256/Dockerfile
index 433e17e1..4cb095ff 100644
--- a/examples/async-thumbnails/setup/resize256/Dockerfile
+++ b/examples/async-thumbnails/setup/resize256/Dockerfile
@@ -1,3 +1,19 @@
+#
+# Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
FROM debian
RUN apt-get update
RUN apt-get install -y imagemagick
diff --git a/examples/async-thumbnails/setup/resize256/func.yaml b/examples/async-thumbnails/setup/resize256/func.yaml
index 9261f2f6..2e655c82 100644
--- a/examples/async-thumbnails/setup/resize256/func.yaml
+++ b/examples/async-thumbnails/setup/resize256/func.yaml
@@ -1,4 +1,21 @@
-name: example/resize256
-version: 0.0.1
+#
+# Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+schema_version: 20180708
+name: resize256
+version: 0.0.5
entrypoint: convert - -resize 256x256 -
-path: /resize256
+format: default
diff --git a/examples/async-thumbnails/setup/resize512/Dockerfile b/examples/async-thumbnails/setup/resize512/Dockerfile
index 52a2b407..56469980 100644
--- a/examples/async-thumbnails/setup/resize512/Dockerfile
+++ b/examples/async-thumbnails/setup/resize512/Dockerfile
@@ -1,3 +1,19 @@
+#
+# Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
FROM debian
RUN apt-get update
RUN apt-get install -y imagemagick
diff --git a/examples/async-thumbnails/setup/resize512/func.yaml b/examples/async-thumbnails/setup/resize512/func.yaml
index 8ee1d02f..bf9712ab 100644
--- a/examples/async-thumbnails/setup/resize512/func.yaml
+++ b/examples/async-thumbnails/setup/resize512/func.yaml
@@ -1,4 +1,21 @@
-name: example/resize512
-version: 0.0.1
+#
+# Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+schema_version: 20180708
+name: resize512
+version: 0.0.8
entrypoint: convert - -resize 512x512 -
-path: /resize512
+format: default
diff --git a/examples/async-thumbnails/setup/setup.sh b/examples/async-thumbnails/setup/setup.sh
index 501c0e60..6902145e 100755
--- a/examples/async-thumbnails/setup/setup.sh
+++ b/examples/async-thumbnails/setup/setup.sh
@@ -1,4 +1,20 @@
#!/bin/bash
+#
+# Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
set -e
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
STORAGE_DIR="/tmp/example-storage-server-files"
@@ -51,26 +67,21 @@ fi
STORAGE_SERVER_IP=`docker inspect --type container -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' example-storage-server`
# Start functions server if not there
-if [[ -z `docker ps | grep "functions"` ]]; then
- docker run -d --name functions \
- -e NO_PROXY="$STORAGE_SERVER_IP:$NO_PROXY" \
- -p 8080:8080 \
- -v /var/run/docker.sock:/var/run/docker.sock \
- "$FUNCTIONS_IMAGE"
- # Give it time to start up
+if [[ -z `docker ps | grep "fnserver"` ]]; then
+ fn start -d
sleep 3
else
echo "Functions server is already up."
fi
# Get its IP
-FUNCTIONS_SERVER_IP=`docker inspect --type container -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' functions`
+FUNCTIONS_SERVER_IP=`docker inspect --type container -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' fnserver`
# Start flow service if not there
if [[ -z `docker ps | grep "flow-service"` ]]; then
docker run -d --name flow-service \
-e LOG_LEVEL=debug \
-e NO_PROXY="$FUNCTIONS_SERVER_IP:$NO_PROXY" \
- -e API_URL=http://$FUNCTIONS_SERVER_IP:8080/r \
+ -e API_URL=http://$FUNCTIONS_SERVER_IP:8080/invoke \
-p 8081:8081 \
"$COMPLETER_IMAGE"
# Give it time to start up
@@ -82,46 +93,41 @@ fi
COMPLETER_SERVER_IP=`docker inspect --type container -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' flow-service`
# Create app and routes
-if [[ `fn apps list` == *"myapp"* ]]; then
+if [[ `fn list apps` == *"myapp"* ]]; then
echo "App myapp is already there."
else
- fn apps create myapp
- fn apps config set myapp COMPLETER_BASE_URL http://10.167.103.193:8081
+ fn create app myapp
fi
-if [[ `fn routes list myapp` == *"/resize128"* ]]; then
- echo "Route /resize128 is already there."
-else
- # This works around proxy issues
- cd $SCRIPT_DIR/resize128 && \
- docker build -t example/resize128:0.0.1 \
- --build-arg http_proxy=$http_proxy \
- --build-arg https_proxy=$https_proxy \
- . && \
- fn routes create myapp /resize128
-fi
-if [[ `fn routes list myapp` == *"/resize256"* ]]; then
- echo "Route /resize256 is already there."
-else
- # This works around proxy issues
- cd $SCRIPT_DIR/resize256 && \
- docker build -t example/resize256:0.0.1 \
- --build-arg http_proxy=$http_proxy \
- --build-arg https_proxy=$https_proxy \
- . && \
- fn routes create myapp /resize256
-fi
-if [[ `fn routes list myapp` == *"/resize512"* ]]; then
- echo "Route /resize512 is already there."
-else
- # This works around proxy issues
- cd $SCRIPT_DIR/resize512 && \
- docker build -t example/resize512:0.0.1 \
- --build-arg http_proxy=$http_proxy \
- --build-arg https_proxy=$https_proxy \
- . && \
- fn routes create myapp /resize512
-fi
+
+fn config app myapp COMPLETER_BASE_URL http://${COMPLETER_SERVER_IP}:8081
+fn config app myapp OBJECT_STORAGE_URL http://${STORAGE_SERVER_IP}:9000
+fn config app myapp OBJECT_STORAGE_ACCESS alpha
+fn config app myapp OBJECT_STORAGE_SECRET betabetabetabeta
+
+(
+ cd ${SCRIPT_DIR}/resize128
+ fn deploy --app myapp --local
+)
+
+fn config app myapp RESIZE_128_FN_ID $(fn list functions myapp | grep resize128 | awk '{print $3}')
+
+(
+ cd ${SCRIPT_DIR}/resize256
+ fn deploy --app myapp --local
+)
+
+fn config app myapp RESIZE_256_FN_ID $(fn list functions myapp | grep resize256 | awk '{print $3}')
+
+
+(
+ cd ${SCRIPT_DIR}/resize512
+ fn deploy --app myapp --local
+)
+
+fn config app myapp RESIZE_512_FN_ID $(fn list functions myapp | grep resize512 | awk '{print $3}')
+
+
if mc config host list | grep example-storage-server &>/dev/null ; then
diff --git a/examples/async-thumbnails/src/main/java/com/fnproject/fn/examples/ThumbnailsFunction.java b/examples/async-thumbnails/src/main/java/com/fnproject/fn/examples/ThumbnailsFunction.java
index 7ad601de..04b853a8 100644
--- a/examples/async-thumbnails/src/main/java/com/fnproject/fn/examples/ThumbnailsFunction.java
+++ b/examples/async-thumbnails/src/main/java/com/fnproject/fn/examples/ThumbnailsFunction.java
@@ -1,21 +1,47 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.examples;
+import com.fnproject.fn.api.FnFeature;
import com.fnproject.fn.api.Headers;
import com.fnproject.fn.api.RuntimeContext;
import com.fnproject.fn.api.flow.Flow;
+import com.fnproject.fn.runtime.flow.FlowFeature;
import com.fnproject.fn.api.flow.Flows;
import com.fnproject.fn.api.flow.HttpMethod;
+import io.minio.BucketExistsArgs;
+import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
+import io.minio.PutObjectArgs;
import java.io.ByteArrayInputStream;
import java.io.Serializable;
+@FnFeature(FlowFeature.class)
public class ThumbnailsFunction implements Serializable {
private final String storageUrl;
private final String storageAccessKey;
private final String storageSecretKey;
+ private final String resize128ID;
+ private final String resize256ID;
+ private final String resize512ID;
+
public ThumbnailsFunction(RuntimeContext ctx) {
storageUrl = ctx.getConfigurationByKey("OBJECT_STORAGE_URL")
.orElseThrow(() -> new RuntimeException("Missing configuration: OBJECT_STORAGE_URL"));
@@ -23,6 +49,14 @@ public ThumbnailsFunction(RuntimeContext ctx) {
.orElseThrow(() -> new RuntimeException("Missing configuration: OBJECT_STORAGE_ACCESS"));
storageSecretKey = ctx.getConfigurationByKey("OBJECT_STORAGE_SECRET")
.orElseThrow(() -> new RuntimeException("Missing configuration: OBJECT_STORAGE_SECRET"));
+
+ resize128ID = ctx.getConfigurationByKey("RESIZE_128_FN_ID")
+ .orElseThrow(() -> new RuntimeException("Missing configuration: RESIZE_128_FN_ID"));
+ resize256ID = ctx.getConfigurationByKey("RESIZE_256_FN_ID")
+ .orElseThrow(() -> new RuntimeException("Missing configuration: RESIZE_256_FN_ID"));
+ resize512ID = ctx.getConfigurationByKey("RESIZE_512_FN_ID")
+ .orElseThrow(() -> new RuntimeException("Missing configuration: RESIZE_512_FN_ID"));
+
}
public class Response {
@@ -35,11 +69,11 @@ public Response handleRequest(byte[] imageBuffer) {
Flow runtime = Flows.currentFlow();
runtime.allOf(
- runtime.invokeFunction("myapp/resize128", HttpMethod.POST, Headers.emptyHeaders(), imageBuffer)
+ runtime.invokeFunction(resize128ID, HttpMethod.POST, Headers.emptyHeaders(), imageBuffer)
.thenAccept((img) -> objectUpload(img.getBodyAsBytes(), id + "-128.png")),
- runtime.invokeFunction("myapp/resize256", HttpMethod.POST, Headers.emptyHeaders(), imageBuffer)
+ runtime.invokeFunction(resize256ID, HttpMethod.POST, Headers.emptyHeaders(), imageBuffer)
.thenAccept((img) -> objectUpload(img.getBodyAsBytes(), id + "-256.png")),
- runtime.invokeFunction("myapp/resize512", HttpMethod.POST, Headers.emptyHeaders(), imageBuffer)
+ runtime.invokeFunction(resize512ID, HttpMethod.POST, Headers.emptyHeaders(), imageBuffer)
.thenAccept((img) -> objectUpload(img.getBodyAsBytes(), id + "-512.png")),
runtime.supply(() -> objectUpload(imageBuffer, id + ".png"))
);
@@ -54,15 +88,22 @@ public Response handleRequest(byte[] imageBuffer) {
*/
private void objectUpload(byte[] imageBuffer, String objectName) {
try {
- MinioClient minioClient = new MinioClient(storageUrl, storageAccessKey, storageSecretKey);
+ MinioClient minioClient = MinioClient.builder()
+ .endpoint(storageUrl).credentials(storageAccessKey, storageSecretKey).build();
// Ensure the bucket exists.
- if(!minioClient.bucketExists("alpha")) {
- minioClient.makeBucket("alpha");
+ BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder().bucket("alpha").build();
+ MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder().bucket("alpha").build();
+ if(!minioClient.bucketExists(bucketExistsArgs)) {
+ minioClient.makeBucket(makeBucketArgs);
}
+ PutObjectArgs putObjectArgs = PutObjectArgs.builder()
+ .bucket("alpha")
+ .object(objectName)
+ .stream(new ByteArrayInputStream(imageBuffer), imageBuffer.length, -1).build();
// Upload the image to the bucket with putObject
- minioClient.putObject("alpha", objectName, new ByteArrayInputStream(imageBuffer), imageBuffer.length, "application/octet-stream");
+ minioClient.putObject(putObjectArgs);
} catch(Exception e) {
System.err.println("Error occurred: " + e);
e.printStackTrace();
diff --git a/examples/async-thumbnails/src/test/java/com/fnproject/fn/examples/ThumbnailsFunctionTest.java b/examples/async-thumbnails/src/test/java/com/fnproject/fn/examples/ThumbnailsFunctionTest.java
index 675ca691..8914acc5 100644
--- a/examples/async-thumbnails/src/test/java/com/fnproject/fn/examples/ThumbnailsFunctionTest.java
+++ b/examples/async-thumbnails/src/test/java/com/fnproject/fn/examples/ThumbnailsFunctionTest.java
@@ -1,7 +1,23 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.examples;
-import com.fnproject.fn.examples.ThumbnailsFunction;
import com.fnproject.fn.testing.FnTestingRule;
+import com.fnproject.fn.testing.flow.FlowTesting;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import org.junit.Rule;
@@ -12,65 +28,76 @@
public class ThumbnailsFunctionTest {
@Rule
- public final FnTestingRule testing = FnTestingRule.createDefault();
+ public final FnTestingRule fn = FnTestingRule.createDefault();
+ private final FlowTesting flow = FlowTesting.create(fn);
@Rule
public final WireMockRule mockServer = new WireMockRule(0);
@Test
public void testThumbnail() {
- testing
-
- .setConfig("OBJECT_STORAGE_URL", "http://localhost:" + mockServer.port())
- .setConfig("OBJECT_STORAGE_ACCESS", "alpha")
- .setConfig("OBJECT_STORAGE_SECRET", "betabetabetabeta")
-
- .givenFn("myapp/resize128")
- .withAction((data) -> "128".getBytes())
- .givenFn("myapp/resize256")
- .withAction((data) -> "256".getBytes())
- .givenFn("myapp/resize512")
- .withAction((data) -> "512".getBytes())
-
- .givenEvent()
- .withBody("testing".getBytes())
- .enqueue();
+ fn
+ .setConfig("OBJECT_STORAGE_URL", "http://localhost:" + mockServer.port())
+ .setConfig("OBJECT_STORAGE_ACCESS", "alpha")
+ .setConfig("OBJECT_STORAGE_SECRET", "betabetabetabeta")
+ .setConfig("RESIZE_128_FN_ID","myapp/resize128")
+ .setConfig("RESIZE_256_FN_ID","myapp/resize256")
+ .setConfig("RESIZE_512_FN_ID","myapp/resize512");
+
+
+ flow
+ .givenFn("myapp/resize128")
+ .withAction((data) -> "128".getBytes())
+ .givenFn("myapp/resize256")
+ .withAction((data) -> "256".getBytes())
+ .givenFn("myapp/resize512")
+ .withAction((data) -> "512".getBytes());
+
+ fn
+ .givenEvent()
+ .withBody("fn".getBytes())
+ .enqueue();
// Mock the http endpoint
mockMinio();
- testing.thenRun(ThumbnailsFunction.class, "handleRequest");
+ fn.thenRun(ThumbnailsFunction.class, "handleRequest");
// Check the final image uploads were performed
- mockServer.verify(1, putRequestedFor(urlMatching("/alpha/.*\\.png")).withRequestBody(equalTo("testing")));
- mockServer.verify(1, putRequestedFor(urlMatching("/alpha/.*\\.png")).withRequestBody(equalTo("128")));
- mockServer.verify(1, putRequestedFor(urlMatching("/alpha/.*\\.png")).withRequestBody(equalTo("256")));
- mockServer.verify(1, putRequestedFor(urlMatching("/alpha/.*\\.png")).withRequestBody(equalTo("512")));
+ mockServer.verify(putRequestedFor(urlMatching("/alpha/.*\\.png")).withRequestBody(containing("fn")));
+ mockServer.verify(putRequestedFor(urlMatching("/alpha/.*\\.png")).withRequestBody(containing("128")));
+ mockServer.verify(putRequestedFor(urlMatching("/alpha/.*\\.png")).withRequestBody(containing("256")));
+ mockServer.verify(putRequestedFor(urlMatching("/alpha/.*\\.png")).withRequestBody(containing("512")));
mockServer.verify(4, putRequestedFor(urlMatching(".*")));
}
@Test
public void anExternalFunctionFailure() {
- testing
- .setConfig("OBJECT_STORAGE_URL", "http://localhost:" + mockServer.port())
- .setConfig("OBJECT_STORAGE_ACCESS", "alpha")
- .setConfig("OBJECT_STORAGE_SECRET", "betabetabetabeta")
-
- .givenFn("myapp/resize128")
- .withResult("128".getBytes())
- .givenFn("myapp/resize256")
- .withResult("256".getBytes())
- .givenFn("myapp/resize512")
- .withFunctionError()
-
- .givenEvent()
- .withBody("testing".getBytes())
- .enqueue();
+ fn
+ .setConfig("OBJECT_STORAGE_URL", "http://localhost:" + mockServer.port())
+ .setConfig("OBJECT_STORAGE_ACCESS", "alpha")
+ .setConfig("OBJECT_STORAGE_SECRET", "betabetabetabeta")
+ .setConfig("RESIZE_128_FN_ID","myapp/resize128")
+ .setConfig("RESIZE_256_FN_ID","myapp/resize256")
+ .setConfig("RESIZE_512_FN_ID","myapp/resize512");;
+
+ flow
+ .givenFn("myapp/resize128")
+ .withResult("128".getBytes())
+ .givenFn("myapp/resize256")
+ .withResult("256".getBytes())
+ .givenFn("myapp/resize512")
+ .withFunctionError();
+
+ fn
+ .givenEvent()
+ .withBody("fn".getBytes())
+ .enqueue();
// Mock the http endpoint
mockMinio();
- testing.thenRun(ThumbnailsFunction.class, "handleRequest");
+ fn.thenRun(ThumbnailsFunction.class, "handleRequest");
// Confirm that one image upload didn't happen
mockServer.verify(0, putRequestedFor(urlMatching("/alpha/.*\\.png")).withRequestBody(equalTo("512")));
@@ -82,15 +109,15 @@ public void anExternalFunctionFailure() {
private void mockMinio() {
mockServer.stubFor(get(urlMatching("/alpha.*"))
- .willReturn(aResponse().withBody(
- "\n" +
- "\n" +
- " alpha\n" +
- " \n" +
- " 0\n" +
- " 100\n" +
- " false\n" +
- "")));
+ .willReturn(aResponse().withBody(
+ "\n" +
+ "\n" +
+ " alpha\n" +
+ " \n" +
+ " 0\n" +
+ " 100\n" +
+ " false\n" +
+ "")));
mockServer.stubFor(WireMock.head(urlMatching("/alpha.*")).willReturn(aResponse().withStatus(200)));
diff --git a/examples/connectorhub-logging/README.md b/examples/connectorhub-logging/README.md
new file mode 100644
index 00000000..992474e2
--- /dev/null
+++ b/examples/connectorhub-logging/README.md
@@ -0,0 +1,93 @@
+# Example Fn Java FDK : Service Connector Hub - Logging
+
+This example provides a Function to use as a service connector hub target.
+The function accepts a typed event containing a batch of source events.
+
+## Source
+[LoggingData.java](../../fn-events/src/main/java/com/fnproject/events/input/sch/LoggingData.java)
+
+## Dependencies
+* [fn-events] for ConnectorHubFunction classes.
+* [fn-events-testing] for ConnectorHubFunction testing library.
+
+## Demonstrated FDK features
+
+This example showcases how to use the fn-event ConnectorHubFunction to
+use a Function as the target for Logging source.
+
+## Step by step
+
+Set up the connector hub with Logging source and Function target:
+* [Setup default policies](https://docs.oracle.com/en-us/iaas/Content/connector-hub/overview.htm#Authenti__default-policies)
+* [create connector hub](https://docs.oracle.com/en-us/iaas/Content/connector-hub/create-service-connector-logging-source.htm)
+
+The Function entrypoint extends the `ConnectorHubFunction` abstract class.
+Note: the [func.yaml](func.yaml) entrypoint remains the class which extends `ConnectorHubFunction`
+e.g. `cmd: com.fnproject.fn.examples.Function::handler`
+
+[Function.java](src/main/java/com/fnproject/fn/examples/Function.java)
+```java
+package com.fnproject.fn.examples;
+
+import com.fnproject.events.ConnectorHubFunction;
+import com.fnproject.events.input.ConnectorHubBatch;
+import com.fnproject.events.input.sch.LoggingData;
+
+public class Function extends ConnectorHubFunction {
+
+ public LogService logService;
+
+ public Function() {
+ this.logService = new LogService();
+ }
+
+ @Override
+ public void handler(ConnectorHubBatch batch) {
+ for (LoggingData log : batch.getBatch()) {
+ logService.readLog(log);
+ }
+ }
+}
+```
+The [ConnectorHubBatch.java](../../fn-events/src/main/java/com/fnproject/events/input/ConnectorHubBatch.java)
+`batch` contains a list of events from Logging as
+specified in [Batch Settings](https://docs.oracle.com/en-us/iaas/Content/connector-hub/overview.htm#batch-settings).
+
+The class [LoggingData.java](../../fn-events/src/main/java/com/fnproject/events/input/sch/LoggingData.java) is
+each logging event.
+
+[Function.java](src/main/java/com/fnproject/fn/examples/Function.java)
+`public class Function extends ConnectorHubFunction`.
+
+To return an error response, throw RuntimeException.class.
+Doing so will cause the Function to return a 502 [Retry policy](https://docs.oracle.com/en-us/iaas/Content/connector-hub/overview.htm#deactivate)
+
+## Test walkthrough
+
+Unit testing `ConnectorHubFunction` is supported with the `ConnectorHubTestFeature` and `FnTestingRule`.
+
+First of all, the class initializes the `FnTestingRule` harness, as explained
+in [Testing Functions](../../docs/TestingFunctions.md).
+
+[FunctionTest.java](src/test/java/com/fnproject/fn/examples/FunctionTest.java)
+```java
+@Rule
+public FnTestingRule fn = FnTestingRule.createDefault();
+
+private final ConnectorHubTestFeature connectorHubTestFeature = ConnectorHubTestFeature.createDefault(fn);
+
+@Test
+public void testInvokeFunctionWithLoggingData() throws Exception {
+
+ ConnectorHubBatch event = createMinimalRequest();
+ connectorHubTestFeature.givenEvent(event).enqueue();
+
+ fn.thenRun(Function.class, "handler");
+
+ FnResult result = fn.getOnlyResult();
+ assertEquals(200, result.getStatus().getCode());
+}
+```
+
+Use `connectorHubTestFeature.givenEvent(event).enqueue();` to queue the request event
+and invoke the Function with `fn.thenRun(Function.class, "handler");`.
diff --git a/examples/connectorhub-logging/func.yaml b/examples/connectorhub-logging/func.yaml
new file mode 100644
index 00000000..e5dface3
--- /dev/null
+++ b/examples/connectorhub-logging/func.yaml
@@ -0,0 +1,5 @@
+schema_version: 20180708
+name: connectorhub-logging
+version: 0.0.1
+runtime: java
+cmd: com.fnproject.fn.examples.Function::handler
diff --git a/examples/connectorhub-logging/pom.xml b/examples/connectorhub-logging/pom.xml
new file mode 100644
index 00000000..f768c714
--- /dev/null
+++ b/examples/connectorhub-logging/pom.xml
@@ -0,0 +1,100 @@
+
+
+
+
+ 4.0.0
+
+
+ UTF-8
+ UTF-8
+ 1.0.0-SNAPSHOT
+ 2.16.1
+ 4.0.0
+
+
+ com.fnproject.fn.examples
+ connectorhub-logging
+ 1.0.0-SNAPSHOT
+
+
+
+ com.fnproject.fn
+ api
+ ${fdk.version}
+
+
+ com.fnproject.fn
+ fn-events
+ ${fdk.version}
+
+
+ com.fnproject.fn
+ fn-events-testing
+ ${fdk.version}
+ test
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
+
+ org.mockito
+ mockito-core
+ ${mockito.version}
+ test
+
+
+ com.fnproject.fn
+ testing-core
+ ${fdk.version}
+ test
+
+
+ com.fnproject.fn
+ testing-junit4
+ ${fdk.version}
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.0
+
+ 1.8
+ 1.8
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.8.2
+
+ true
+
+
+
+
+
diff --git a/examples/connectorhub-logging/src/main/java/com/fnproject/fn/examples/Function.java b/examples/connectorhub-logging/src/main/java/com/fnproject/fn/examples/Function.java
new file mode 100644
index 00000000..9aa37965
--- /dev/null
+++ b/examples/connectorhub-logging/src/main/java/com/fnproject/fn/examples/Function.java
@@ -0,0 +1,21 @@
+package com.fnproject.fn.examples;
+
+import com.fnproject.events.ConnectorHubFunction;
+import com.fnproject.events.input.ConnectorHubBatch;
+import com.fnproject.events.input.sch.LoggingData;
+
+public class Function extends ConnectorHubFunction {
+
+ public LogService logService;
+
+ public Function() {
+ this.logService = new LogService();
+ }
+
+ @Override
+ public void handler(ConnectorHubBatch batch) {
+ for (LoggingData log : batch.getBatch()) {
+ logService.readLog(log);
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/connectorhub-logging/src/main/java/com/fnproject/fn/examples/LogService.java b/examples/connectorhub-logging/src/main/java/com/fnproject/fn/examples/LogService.java
new file mode 100644
index 00000000..f0145b2c
--- /dev/null
+++ b/examples/connectorhub-logging/src/main/java/com/fnproject/fn/examples/LogService.java
@@ -0,0 +1,19 @@
+package com.fnproject.fn.examples;
+
+
+import com.fnproject.events.input.sch.LoggingData;
+
+public class LogService {
+
+ public void readLog(LoggingData loggingData) {
+ System.out.println(loggingData);
+ assert loggingData != null;
+ assert loggingData.getData() != null && !loggingData.getData().isEmpty();
+ assert loggingData.getId() != null;
+ assert loggingData.getOracle() != null && !loggingData.getOracle().isEmpty();;
+ assert loggingData.getSource() != null;
+ assert loggingData.getSpecversion() != null;
+ assert loggingData.getTime() != null;
+ assert loggingData.getType() != null;
+ }
+}
diff --git a/examples/connectorhub-logging/src/test/java/com/fnproject/fn/examples/FunctionTest.java b/examples/connectorhub-logging/src/test/java/com/fnproject/fn/examples/FunctionTest.java
new file mode 100644
index 00000000..32ab2414
--- /dev/null
+++ b/examples/connectorhub-logging/src/test/java/com/fnproject/fn/examples/FunctionTest.java
@@ -0,0 +1,71 @@
+package com.fnproject.fn.examples;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import com.fnproject.events.input.ConnectorHubBatch;
+import com.fnproject.events.input.sch.Datapoint;
+import com.fnproject.events.input.sch.LoggingData;
+import com.fnproject.events.input.sch.MetricData;
+import com.fnproject.events.testing.ConnectorHubTestFeature;
+import com.fnproject.fn.testing.FnResult;
+import com.fnproject.fn.testing.FnTestingRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class FunctionTest {
+
+ @Rule
+ public FnTestingRule fn = FnTestingRule.createDefault();
+
+ private final ConnectorHubTestFeature connectorHubTestFeature = ConnectorHubTestFeature.createDefault(fn);
+
+ @Test
+ public void testInvokeFunctionWithLoggingData() throws Exception {
+
+ ConnectorHubBatch event = createMinimalRequest();
+ connectorHubTestFeature.givenEvent(event).enqueue();
+
+ fn.thenRun(Function.class, "handler");
+
+ FnResult result = fn.getOnlyResult();
+ assertEquals(200, result.getStatus().getCode());
+ }
+
+ private static ConnectorHubBatch createMinimalRequest() {
+ Map data = new HashMap();
+ data.put("applicationId", "ocid1.fnapp.oc1.xyz");
+ data.put("containerId", "n/a");
+ data.put("functionId", "ocid1.fnfunc.oc1.xyz");
+ data.put("message", "Received function invocation request");
+ data.put("opcRequestId", "/abc/def");
+ data.put("requestId", "/def/abc");
+ data.put("src", "STDOUT");
+
+ Map oracle = new HashMap();
+ oracle.put("compartmentid", "ocid1.tenancy.oc1.xyz");
+ oracle.put("ingestedtime", "2025-10-23T15:45:19.457Z");
+ oracle.put("loggroupid", "ocid1.loggroup.oc1.abc");
+ oracle.put("logid", "ocid1.log.oc1.abc");
+ oracle.put("tenantid", "ocid1.tenancy.oc1.xyz");
+
+ LoggingData source = new LoggingData(
+ "ecb37864-4396-4302-9575-981644949730",
+ "log-name",
+ "1.0",
+ "schedule",
+ "com.oraclecloud.functions.application.functioninvoke",
+ data,
+ oracle,
+ new Date(1764860467553L)
+ );
+ ConnectorHubBatch event = mock(ConnectorHubBatch.class);
+
+ when(event.getBatch()).thenReturn(Collections.singletonList(source));
+ return event;
+ }
+}
\ No newline at end of file
diff --git a/examples/connectorhub-monitoring/README.md b/examples/connectorhub-monitoring/README.md
new file mode 100644
index 00000000..b3790435
--- /dev/null
+++ b/examples/connectorhub-monitoring/README.md
@@ -0,0 +1,95 @@
+# Example Fn Java FDK : Service Connector Hub - Monitoring
+
+This example provides a Function to use as a service connector hub target.
+The function accepts a typed event containing a batch of source events.
+
+## Source
+[MetricData](https://docs.oracle.com/en-us/iaas/Content/connector-hub/create-service-connector-monitoring-source.htm)
+
+## Dependencies
+* [fn-events] for ConnectorHubFunction classes.
+* [fn-events-testing] for ConnectorHubFunction testing library.
+
+## Demonstrated FDK features
+
+This example showcases how to use the fn-event ConnectorHubFunction to
+use a Function as the target for Monitoring source.
+
+## Step by step
+
+Set up the connector hub with Monitoring source and Function target:
+* [Setup default policies](https://docs.oracle.com/en-us/iaas/Content/connector-hub/overview.htm#Authenti__default-policies)
+* [create connector hub](https://docs.oracle.com/en-us/iaas/Content/connector-hub/create-service-connector-monitoring-source.htm)
+
+The Function entrypoint extends the `ConnectorHubFunction` abstract class.
+Note: the [func.yaml](func.yaml) entrypoint remains the class which extends `ConnectorHubFunction`
+e.g. `cmd: com.fnproject.fn.examples.Function::handler`
+
+[Function.java](src/main/java/com/fnproject/fn/examples/Function.java)
+```java
+package com.fnproject.fn.examples;
+
+import com.fnproject.events.ConnectorHubFunction;
+import com.fnproject.events.input.ConnectorHubBatch;
+import com.fnproject.events.input.sch.MetricData;
+
+public class Function extends ConnectorHubFunction {
+
+ public MetricService metricService;
+
+ public Function() {
+ this.metricService = new MetricService();
+ }
+
+ @Override
+ public void handler(ConnectorHubBatch batch) {
+ for (MetricData metric : batch.getBatch()) {
+ metricService.readMetric(metric);
+ }
+ }
+}
+```
+The [ConnectorHubBatch.java](../../fn-events/src/main/java/com/fnproject/events/input/ConnectorHubBatch.java)
+`batch` contains a list of events from Monitoring as
+specified in [Batch Settings](https://docs.oracle.com/en-us/iaas/Content/connector-hub/overview.htm#batch-settings).
+
+The class [MetricData.java](../../fn-events/src/main/java/com/fnproject/events/input/sch/MetricData.java) is
+each Monitoring Event [Monitoring Schema](https://docs.oracle.com/en-us/iaas/Content/connector-hub/create-service-connector-monitoring-source.htm)
+
+[Function.java](src/main/java/com/fnproject/fn/examples/Function.java)
+`public class Function extends ConnectorHubFunction {`.
+
+To return an error response, throw RuntimeException.class.
+Doing so will cause the Function to return a 502 [Retry policy](https://docs.oracle.com/en-us/iaas/Content/connector-hub/overview.htm#deactivate)
+
+## Test walkthrough
+
+Unit testing `ConnectorHubFunction` is supported with the `ConnectorHubTestFeature` and `FnTestingRule`.
+
+First of all, the class initializes the `FnTestingRule` harness, as explained
+in [Testing Functions](../../docs/TestingFunctions.md).
+
+[FunctionTest.java](src/test/java/com/fnproject/fn/examples/FunctionTest.java)
+```java
+
+ @Rule
+ public FnTestingRule fn = FnTestingRule.createDefault();
+
+ private final ConnectorHubTestFeature connectorHubTestFeature = ConnectorHubTestFeature.createDefault(fn);
+
+ @Test
+ public void testMetricServiceConsumesEachMetric() throws Exception {
+
+ ConnectorHubBatch event = createMinimalRequest();
+ connectorHubTestFeature.givenEvent(event).enqueue();
+
+ fn.thenRun(Function.class, "handler");
+
+ FnResult result = fn.getOnlyResult();
+ assertEquals(200, result.getStatus().getCode());
+ }
+
+```
+
+Use `connectorHubTestFeature.givenEvent(event).enqueue();` to queue the request event
+and invoke the Function with `fn.thenRun(Function.class, "handler");`.
diff --git a/examples/connectorhub-monitoring/func.yaml b/examples/connectorhub-monitoring/func.yaml
new file mode 100644
index 00000000..c7b5de2a
--- /dev/null
+++ b/examples/connectorhub-monitoring/func.yaml
@@ -0,0 +1,5 @@
+schema_version: 20180708
+name: connectorhub-monitoring
+version: 0.0.1
+runtime: java
+cmd: com.fnproject.fn.examples.Function::handler
diff --git a/examples/connectorhub-monitoring/pom.xml b/examples/connectorhub-monitoring/pom.xml
new file mode 100644
index 00000000..b955ccfa
--- /dev/null
+++ b/examples/connectorhub-monitoring/pom.xml
@@ -0,0 +1,100 @@
+
+
+
+
+ 4.0.0
+
+
+ UTF-8
+ UTF-8
+ 1.0.0-SNAPSHOT
+ 2.16.1
+ 4.0.0
+
+
+ com.fnproject.fn.examples
+ connectorhub-monitoring
+ 1.0.0-SNAPSHOT
+
+
+
+ com.fnproject.fn
+ api
+ ${fdk.version}
+
+
+ com.fnproject.fn
+ fn-events
+ ${fdk.version}
+
+
+ com.fnproject.fn
+ fn-events-testing
+ ${fdk.version}
+ test
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
+
+ org.mockito
+ mockito-core
+ ${mockito.version}
+ test
+
+
+ com.fnproject.fn
+ testing-core
+ ${fdk.version}
+ test
+
+
+ com.fnproject.fn
+ testing-junit4
+ ${fdk.version}
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.0
+
+ 1.8
+ 1.8
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.8.2
+
+ true
+
+
+
+
+
diff --git a/examples/connectorhub-monitoring/src/main/java/com/fnproject/fn/examples/Function.java b/examples/connectorhub-monitoring/src/main/java/com/fnproject/fn/examples/Function.java
new file mode 100644
index 00000000..37e86b4a
--- /dev/null
+++ b/examples/connectorhub-monitoring/src/main/java/com/fnproject/fn/examples/Function.java
@@ -0,0 +1,21 @@
+package com.fnproject.fn.examples;
+
+import com.fnproject.events.ConnectorHubFunction;
+import com.fnproject.events.input.ConnectorHubBatch;
+import com.fnproject.events.input.sch.MetricData;
+
+public class Function extends ConnectorHubFunction {
+
+ public MetricService metricService;
+
+ public Function() {
+ this.metricService = new MetricService();
+ }
+
+ @Override
+ public void handler(ConnectorHubBatch batch) {
+ for (MetricData metric : batch.getBatch()) {
+ metricService.readMetric(metric);
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/connectorhub-monitoring/src/main/java/com/fnproject/fn/examples/MetricService.java b/examples/connectorhub-monitoring/src/main/java/com/fnproject/fn/examples/MetricService.java
new file mode 100644
index 00000000..c1750638
--- /dev/null
+++ b/examples/connectorhub-monitoring/src/main/java/com/fnproject/fn/examples/MetricService.java
@@ -0,0 +1,18 @@
+package com.fnproject.fn.examples;
+
+
+import com.fnproject.events.input.sch.MetricData;
+
+public class MetricService {
+
+ public void readMetric(MetricData metric) {
+ System.out.println(metric);
+ assert metric != null;
+ assert metric.getDatapoints() != null && !metric.getDatapoints().isEmpty();
+ assert metric.getCompartmentId() != null;
+ assert metric.getDimensions() != null && !metric.getDimensions().isEmpty();
+ assert metric.getMetadata() != null && !metric.getMetadata().isEmpty();
+ assert metric.getName() != null && !metric.getName().isEmpty();
+ assert metric.getNamespace() != null;
+ }
+}
diff --git a/examples/connectorhub-monitoring/src/test/java/com/fnproject/fn/examples/FunctionTest.java b/examples/connectorhub-monitoring/src/test/java/com/fnproject/fn/examples/FunctionTest.java
new file mode 100644
index 00000000..e056f74c
--- /dev/null
+++ b/examples/connectorhub-monitoring/src/test/java/com/fnproject/fn/examples/FunctionTest.java
@@ -0,0 +1,60 @@
+package com.fnproject.fn.examples;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import com.fnproject.events.input.ConnectorHubBatch;
+import com.fnproject.events.input.sch.Datapoint;
+import com.fnproject.events.input.sch.MetricData;
+import com.fnproject.events.testing.ConnectorHubTestFeature;
+import com.fnproject.fn.testing.FnResult;
+import com.fnproject.fn.testing.FnTestingRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class FunctionTest {
+
+ @Rule
+ public FnTestingRule fn = FnTestingRule.createDefault();
+
+ private final ConnectorHubTestFeature connectorHubTestFeature = ConnectorHubTestFeature.createDefault(fn);
+
+ @Test
+ public void testInvokeFunctionWithMetricData() throws Exception {
+
+ ConnectorHubBatch event = createMinimalRequest();
+ connectorHubTestFeature.givenEvent(event).enqueue();
+
+ fn.thenRun(Function.class, "handler");
+
+ FnResult result = fn.getOnlyResult();
+ assertEquals(200, result.getStatus().getCode());
+ }
+
+ private static ConnectorHubBatch createMinimalRequest() {
+ Map dimensions = new HashMap<>();
+ dimensions.put("resourceID", "ocid1.bucket.oc1.xyz");
+ dimensions.put("resourceDisplayName", "userName");
+ Map metadata = new HashMap<>();
+ metadata.put("displayName", "PutObject Request Count");
+ metadata.put("unit", "count");
+
+ MetricData source = new MetricData(
+ "oci_objectstorage",
+ "unknown",
+ "ocid1.tenancy.oc1..xyz",
+ "PutRequests",
+ dimensions,
+ metadata,
+ Collections.singletonList(new Datapoint(new Date(1764860467553L), Double.parseDouble("12.3"), null))
+ );
+ ConnectorHubBatch event = mock(ConnectorHubBatch.class);
+
+ when(event.getBatch()).thenReturn(Collections.singletonList(source));
+ return event;
+ }
+}
\ No newline at end of file
diff --git a/examples/connectorhub-queue/README.md b/examples/connectorhub-queue/README.md
new file mode 100644
index 00000000..215eff2c
--- /dev/null
+++ b/examples/connectorhub-queue/README.md
@@ -0,0 +1,124 @@
+# Example Fn Java FDK : Service Connector Hub - Queue
+
+This example provides a Function to use as a service connector hub target.
+The function accepts a typed event containing a batch of source messages.
+
+## Source
+[LoggingData.java](../../fn-events/src/main/java/com/fnproject/events/input/sch/LoggingData.java)
+
+## Dependencies
+* [fn-events] for ConnectorHubFunction classes.
+* [fn-events-testing] for ConnectorHubFunction testing library.
+
+## Demonstrated FDK features
+
+This example showcases how to use the fn-event ConnectorHubFunction to
+use a Function as the target for Queue source.
+
+## Step by step
+
+Set up the connector hub with Logging source and Function target:
+* [Setup default policies](https://docs.oracle.com/en-us/iaas/Content/connector-hub/overview.htm#Authenti__default-policies)
+* [create connector hub](https://docs.oracle.com/en-us/iaas/Content/connector-hub/create-service-connector-queue-source.htm)
+
+The Function entrypoint extends the `ConnectorHubFunction` abstract class.
+Note: the [func.yaml](func.yaml) entrypoint remains the class which extends `ConnectorHubFunction`
+e.g. `cmd: com.fnproject.fn.examples.Function::handler`
+
+[Function.java](src/main/java/com/fnproject/fn/examples/Function.java)
+
+```java
+package com.fnproject.fn.examples;
+
+import com.fnproject.events.ConnectorHubFunction;
+import com.fnproject.events.input.ConnectorHubBatch;
+
+public class Function extends ConnectorHubFunction {
+
+ public QueueService queueService;
+
+ public Function() {
+ this.queueService = new QueueService();
+ }
+
+ @Override
+ public void handler(ConnectorHubBatch batch) {
+ for (Employee employee : batch.getBatch()) {
+ queueService.readContent(employee);
+ }
+ }
+}
+```
+The [ConnectorHubBatch.java](../../fn-events/src/main/java/com/fnproject/events/input/ConnectorHubBatch.java)
+`batch` contains a list of messages from Queue as
+specified in [Batch Settings](https://docs.oracle.com/en-us/iaas/Content/connector-hub/overview.htm#batch-settings).
+
+The class [Employee.java](src/main/java/com/fnproject/fn/examples/Employee.java) is
+a representation of messages received from the Queue.
+
+Note the messages sent in to the Queue must be a valid String because Connector Hub forwards the message directly to
+the Function target.
+- wrong: a plain string
+- correct: "a plain string"
+
+[Function.java](src/main/java/com/fnproject/fn/examples/Function.java)
+`public class Function extends ConnectorHubFunction`.
+
+To return an error response, throw RuntimeException.class.
+Doing so will cause the Function to return a 502 [Retry policy](https://docs.oracle.com/en-us/iaas/Content/connector-hub/overview.htm#deactivate)
+
+## Test walkthrough
+
+Unit testing `ConnectorHubFunction` is supported with the `ConnectorHubTestFeature` and `FnTestingRule`.
+
+First of all, the class initializes the `FnTestingRule` harness, as explained
+in [Testing Functions](../../docs/TestingFunctions.md).
+
+[FunctionTest.java](src/test/java/com/fnproject/fn/examples/FunctionTest.java)
+```java
+package com.fnproject.fn.examples;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import java.util.Collections;
+import com.fnproject.events.input.ConnectorHubBatch;
+import com.fnproject.events.testing.ConnectorHubTestFeature;
+import com.fnproject.fn.testing.FnResult;
+import com.fnproject.fn.testing.FnTestingRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class FunctionTest {
+
+ @Rule
+ public FnTestingRule fn = FnTestingRule.createDefault();
+
+ private final ConnectorHubTestFeature connectorHubTestFeature = ConnectorHubTestFeature.createDefault(fn);
+
+ @Test
+ public void testInvokeFunctionWithLoggingData() throws Exception {
+
+ ConnectorHubBatch event = createMinimalRequest();
+ connectorHubTestFeature.givenEvent(event).enqueue();
+
+ fn.thenRun(Function.class, "handler");
+
+ FnResult result = fn.getOnlyResult();
+ assertEquals(200, result.getStatus().getCode());
+ }
+
+ private static ConnectorHubBatch createMinimalRequest() {
+ Employee employee = new Employee();
+ employee.setName("foo");
+
+ ConnectorHubBatch event = mock(ConnectorHubBatch.class);
+
+ when(event.getBatch()).thenReturn(Collections.singletonList(employee));
+ return event;
+ }
+}
+```
+
+Use `connectorHubTestFeature.givenEvent(event).enqueue();` to queue the request event
+and invoke the Function with `fn.thenRun(Function.class, "handler");`.
diff --git a/examples/connectorhub-queue/func.yaml b/examples/connectorhub-queue/func.yaml
new file mode 100644
index 00000000..6cef20c6
--- /dev/null
+++ b/examples/connectorhub-queue/func.yaml
@@ -0,0 +1,5 @@
+schema_version: 20180708
+name: connectorhub-queue
+version: 0.0.1
+runtime: java
+cmd: com.fnproject.fn.examples.Function::handler
diff --git a/examples/connectorhub-queue/pom.xml b/examples/connectorhub-queue/pom.xml
new file mode 100644
index 00000000..7f29999c
--- /dev/null
+++ b/examples/connectorhub-queue/pom.xml
@@ -0,0 +1,100 @@
+
+
+
+
+ 4.0.0
+
+
+ UTF-8
+ UTF-8
+ 1.0.0-SNAPSHOT
+ 2.16.1
+ 4.0.0
+
+
+ com.fnproject.fn.examples
+ connectorhub-queue
+ 1.0.0-SNAPSHOT
+
+
+
+ com.fnproject.fn
+ api
+ ${fdk.version}
+
+
+ com.fnproject.fn
+ fn-events
+ ${fdk.version}
+
+
+ com.fnproject.fn
+ fn-events-testing
+ ${fdk.version}
+ test
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
+
+ org.mockito
+ mockito-core
+ ${mockito.version}
+ test
+
+
+ com.fnproject.fn
+ testing-core
+ ${fdk.version}
+ test
+
+
+ com.fnproject.fn
+ testing-junit4
+ ${fdk.version}
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.0
+
+ 1.8
+ 1.8
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.8.2
+
+ true
+
+
+
+
+
diff --git a/examples/connectorhub-queue/src/main/java/com/fnproject/fn/examples/Employee.java b/examples/connectorhub-queue/src/main/java/com/fnproject/fn/examples/Employee.java
new file mode 100644
index 00000000..21079ea8
--- /dev/null
+++ b/examples/connectorhub-queue/src/main/java/com/fnproject/fn/examples/Employee.java
@@ -0,0 +1,41 @@
+package com.fnproject.fn.examples;
+
+import java.util.Objects;
+
+public class Employee {
+ private String name;
+
+ public Employee() {}
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Employee employee = (Employee) o;
+ return Objects.equals(name, employee.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(name);
+ }
+
+ @Override
+ public String toString() {
+ return "Employee{" +
+ "name='" + name + '\'' +
+ '}';
+ }
+}
diff --git a/examples/connectorhub-queue/src/main/java/com/fnproject/fn/examples/Function.java b/examples/connectorhub-queue/src/main/java/com/fnproject/fn/examples/Function.java
new file mode 100644
index 00000000..79423d49
--- /dev/null
+++ b/examples/connectorhub-queue/src/main/java/com/fnproject/fn/examples/Function.java
@@ -0,0 +1,20 @@
+package com.fnproject.fn.examples;
+
+import com.fnproject.events.ConnectorHubFunction;
+import com.fnproject.events.input.ConnectorHubBatch;
+
+public class Function extends ConnectorHubFunction {
+
+ public QueueService queueService;
+
+ public Function() {
+ this.queueService = new QueueService();
+ }
+
+ @Override
+ public void handler(ConnectorHubBatch batch) {
+ for (Employee employee : batch.getBatch()) {
+ queueService.readContent(employee);
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/connectorhub-queue/src/main/java/com/fnproject/fn/examples/QueueService.java b/examples/connectorhub-queue/src/main/java/com/fnproject/fn/examples/QueueService.java
new file mode 100644
index 00000000..748360aa
--- /dev/null
+++ b/examples/connectorhub-queue/src/main/java/com/fnproject/fn/examples/QueueService.java
@@ -0,0 +1,10 @@
+package com.fnproject.fn.examples;
+
+
+public class QueueService {
+
+ public void readContent(Employee employee) {
+ System.out.println(employee);
+ assert employee != null;
+ }
+}
diff --git a/examples/connectorhub-queue/src/test/java/com/fnproject/fn/examples/FunctionTest.java b/examples/connectorhub-queue/src/test/java/com/fnproject/fn/examples/FunctionTest.java
new file mode 100644
index 00000000..8f0324a5
--- /dev/null
+++ b/examples/connectorhub-queue/src/test/java/com/fnproject/fn/examples/FunctionTest.java
@@ -0,0 +1,42 @@
+package com.fnproject.fn.examples;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import java.util.Collections;
+import com.fnproject.events.input.ConnectorHubBatch;
+import com.fnproject.events.testing.ConnectorHubTestFeature;
+import com.fnproject.fn.testing.FnResult;
+import com.fnproject.fn.testing.FnTestingRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class FunctionTest {
+
+ @Rule
+ public FnTestingRule fn = FnTestingRule.createDefault();
+
+ private final ConnectorHubTestFeature connectorHubTestFeature = ConnectorHubTestFeature.createDefault(fn);
+
+ @Test
+ public void testInvokeFunctionWithQueueData() throws Exception {
+
+ ConnectorHubBatch event = createMinimalRequest();
+ connectorHubTestFeature.givenEvent(event).enqueue();
+
+ fn.thenRun(Function.class, "handler");
+
+ FnResult result = fn.getOnlyResult();
+ assertEquals(200, result.getStatus().getCode());
+ }
+
+ private static ConnectorHubBatch createMinimalRequest() {
+ Employee employee = new Employee();
+ employee.setName("foo");
+
+ ConnectorHubBatch event = mock(ConnectorHubBatch.class);
+
+ when(event.getBatch()).thenReturn(Collections.singletonList(employee));
+ return event;
+ }
+}
\ No newline at end of file
diff --git a/examples/connectorhub-streaming/README.md b/examples/connectorhub-streaming/README.md
new file mode 100644
index 00000000..89361024
--- /dev/null
+++ b/examples/connectorhub-streaming/README.md
@@ -0,0 +1,133 @@
+# Example Fn Java FDK : Service Connector Hub - Streaming
+
+This example provides a Function to use as a service connector hub target.
+The function accepts a typed event containing a batch of messages.
+
+## Source
+[Streaming](https://docs.oracle.com/en-us/iaas/api/#/en/streaming/20180418/Message)
+The value in each streaming event is delivered as base64 encoded. The library automatically decodes it.
+
+## Dependencies
+* [fn-events] for ConnectorHubFunction classes.
+* [fn-events-testing] for ConnectorHubFunction testing library.
+
+## Demonstrated FDK features
+
+This example showcases how to use the fn-event ConnectorHubFunction to
+use a Function as the target for Streaming source.
+
+## Step by step
+
+Set up the connector hub with Streaming source and Function target:
+* [Setup default policies](https://docs.oracle.com/en-us/iaas/Content/connector-hub/overview.htm#Authenti__default-policies)
+* [create connector hub](https://docs.oracle.com/en-us/iaas/Content/connector-hub/create-service-connector-streaming-source.htm)
+
+The Function entrypoint extends the `ConnectorHubFunction` abstract class.
+Note: the [func.yaml](func.yaml) entrypoint remains the class which extends `ConnectorHubFunction`
+e.g. `cmd: com.fnproject.fn.examples.Function::handler`
+
+[Function.java](src/main/java/com/fnproject/fn/examples/Function.java)
+
+```java
+package com.fnproject.fn.examples;
+
+import com.fnproject.events.ConnectorHubFunction;
+import com.fnproject.events.input.ConnectorHubBatch;
+import com.fnproject.events.input.sch.StreamingData;
+
+public class Function extends ConnectorHubFunction> {
+
+ public StreamService streamService;
+
+ public Function() {
+ this.streamService = new StreamService();
+ }
+
+ @Override
+ public void handler(ConnectorHubBatch> batch) {
+ for (StreamingData stream : batch.getBatch()) {
+ streamService.readStream(stream);
+ }
+ }
+}
+```
+The [ConnectorHubBatch.java](../../fn-events/src/main/java/com/fnproject/events/input/ConnectorHubBatch.java)
+`batch` contains a list of events from Streaming as
+specified in [Batch Settings](https://docs.oracle.com/en-us/iaas/Content/connector-hub/overview.htm#batch-settings).
+
+The class [StreamingData.java](../../fn-events/src/main/java/com/fnproject/events/input/sch/StreamingData.java)
+represents the batch of Streaming Events.
+
+The [Employee.java](src/main/java/com/fnproject/fn/examples/Employee.java) represents the base64 encoded JSON
+from value in each message. Note: Provide a String type if the message value is not JSON format.
+
+[Function.java](src/main/java/com/fnproject/fn/examples/Function.java)
+`public class Function extends ConnectorHubFunction>`.
+
+To return an error response, throw RuntimeException.class.
+Doing so will cause the Function to return a 502 [Retry policy](https://docs.oracle.com/en-us/iaas/Content/connector-hub/overview.htm#deactivate)
+
+## Test walkthrough
+
+Unit testing `ConnectorHubFunction` is supported with the `ConnectorHubTestFeature` and `FnTestingRule`.
+
+First of all, the class initializes the `FnTestingRule` harness, as explained
+in [Testing Functions](../../docs/TestingFunctions.md).
+
+[FunctionTest.java](src/test/java/com/fnproject/fn/examples/FunctionTest.java)
+```java
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import java.util.Collections;
+import java.util.Date;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fnproject.events.input.ConnectorHubBatch;
+import com.fnproject.events.input.sch.StreamingData;
+import com.fnproject.events.testing.ConnectorHubTestFeature;
+import com.fnproject.fn.testing.FnResult;
+import com.fnproject.fn.testing.FnTestingRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class FunctionTest {
+
+ @Rule
+ public FnTestingRule fn = FnTestingRule.createDefault();
+
+ private final ConnectorHubTestFeature connectorHubTestFeature = ConnectorHubTestFeature.createDefault(fn);
+
+ @Test
+ public void testInvokeFunctionWithStreamingData() throws Exception {
+
+ ConnectorHubBatch> event = createMinimalRequest();
+ connectorHubTestFeature.givenEvent(event).enqueue();
+
+ fn.thenRun(Function.class, "handler");
+
+ FnResult result = fn.getOnlyResult();
+ assertEquals(200, result.getStatus().getCode());
+ }
+
+ private static ConnectorHubBatch> createMinimalRequest() throws JsonProcessingException {
+ Employee employee = new Employee();
+ employee.setName("foo");
+
+ StreamingData source = new StreamingData(
+ "stream-name",
+ "0",
+ null,
+ employee,
+ "3",
+ new Date(1764860467553L)
+ );
+ ConnectorHubBatch> event = mock(ConnectorHubBatch.class);
+
+ when(event.getBatch()).thenReturn(Collections.singletonList(source));
+ return event;
+ }
+}
+```
+
+Use `connectorHubTestFeature.givenEvent(event).enqueue();` to queue the request event
+and invoke the Function with `fn.thenRun(Function.class, "handler");`.
diff --git a/examples/connectorhub-streaming/func.yaml b/examples/connectorhub-streaming/func.yaml
new file mode 100644
index 00000000..2b7cd061
--- /dev/null
+++ b/examples/connectorhub-streaming/func.yaml
@@ -0,0 +1,5 @@
+schema_version: 20180708
+name: connectorhub-streaming
+version: 0.0.1
+runtime: java
+cmd: com.fnproject.fn.examples.Function::handler
diff --git a/examples/connectorhub-streaming/pom.xml b/examples/connectorhub-streaming/pom.xml
new file mode 100644
index 00000000..afc256c8
--- /dev/null
+++ b/examples/connectorhub-streaming/pom.xml
@@ -0,0 +1,100 @@
+
+
+
+
+ 4.0.0
+
+
+ UTF-8
+ UTF-8
+ 1.0.0-SNAPSHOT
+ 2.16.1
+ 4.0.0
+
+
+ com.fnproject.fn.examples
+ connectorhub-streaming
+ 1.0.0-SNAPSHOT
+
+
+
+ com.fnproject.fn
+ api
+ ${fdk.version}
+
+
+ com.fnproject.fn
+ fn-events
+ ${fdk.version}
+
+
+ com.fnproject.fn
+ fn-events-testing
+ ${fdk.version}
+ test
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
+
+ org.mockito
+ mockito-core
+ ${mockito.version}
+ test
+
+
+ com.fnproject.fn
+ testing-core
+ ${fdk.version}
+ test
+
+
+ com.fnproject.fn
+ testing-junit4
+ ${fdk.version}
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.0
+
+ 1.8
+ 1.8
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.8.2
+
+ true
+
+
+
+
+
diff --git a/examples/connectorhub-streaming/src/main/java/com/fnproject/fn/examples/Employee.java b/examples/connectorhub-streaming/src/main/java/com/fnproject/fn/examples/Employee.java
new file mode 100644
index 00000000..21079ea8
--- /dev/null
+++ b/examples/connectorhub-streaming/src/main/java/com/fnproject/fn/examples/Employee.java
@@ -0,0 +1,41 @@
+package com.fnproject.fn.examples;
+
+import java.util.Objects;
+
+public class Employee {
+ private String name;
+
+ public Employee() {}
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Employee employee = (Employee) o;
+ return Objects.equals(name, employee.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(name);
+ }
+
+ @Override
+ public String toString() {
+ return "Employee{" +
+ "name='" + name + '\'' +
+ '}';
+ }
+}
diff --git a/examples/connectorhub-streaming/src/main/java/com/fnproject/fn/examples/Function.java b/examples/connectorhub-streaming/src/main/java/com/fnproject/fn/examples/Function.java
new file mode 100644
index 00000000..0b7b8fbd
--- /dev/null
+++ b/examples/connectorhub-streaming/src/main/java/com/fnproject/fn/examples/Function.java
@@ -0,0 +1,21 @@
+package com.fnproject.fn.examples;
+
+import com.fnproject.events.ConnectorHubFunction;
+import com.fnproject.events.input.ConnectorHubBatch;
+import com.fnproject.events.input.sch.StreamingData;
+
+public class Function extends ConnectorHubFunction> {
+
+ public StreamService streamService;
+
+ public Function() {
+ this.streamService = new StreamService();
+ }
+
+ @Override
+ public void handler(ConnectorHubBatch> batch) {
+ for (StreamingData stream : batch.getBatch()) {
+ streamService.readStream(stream);
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/connectorhub-streaming/src/main/java/com/fnproject/fn/examples/StreamService.java b/examples/connectorhub-streaming/src/main/java/com/fnproject/fn/examples/StreamService.java
new file mode 100644
index 00000000..c6870ac0
--- /dev/null
+++ b/examples/connectorhub-streaming/src/main/java/com/fnproject/fn/examples/StreamService.java
@@ -0,0 +1,19 @@
+package com.fnproject.fn.examples;
+
+import com.fnproject.events.input.sch.StreamingData;
+
+public class StreamService {
+
+ public void readStream(StreamingData streamingData) {
+ System.out.println(streamingData);
+ assert streamingData != null;
+ assert streamingData.getStream() != null;
+ assert streamingData.getPartition() != null;
+ assert streamingData.getValue() != null;
+ assert streamingData.getOffset() != null;
+ assert streamingData.getTimestamp() != null;
+
+ Employee employee = streamingData.getValue();
+ System.out.println(employee);
+ }
+}
diff --git a/examples/connectorhub-streaming/src/test/java/com/fnproject/fn/examples/FunctionTest.java b/examples/connectorhub-streaming/src/test/java/com/fnproject/fn/examples/FunctionTest.java
new file mode 100644
index 00000000..bce8a569
--- /dev/null
+++ b/examples/connectorhub-streaming/src/test/java/com/fnproject/fn/examples/FunctionTest.java
@@ -0,0 +1,53 @@
+package com.fnproject.fn.examples;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import java.util.Collections;
+import java.util.Date;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fnproject.events.input.ConnectorHubBatch;
+import com.fnproject.events.input.sch.StreamingData;
+import com.fnproject.events.testing.ConnectorHubTestFeature;
+import com.fnproject.fn.testing.FnResult;
+import com.fnproject.fn.testing.FnTestingRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class FunctionTest {
+
+ @Rule
+ public FnTestingRule fn = FnTestingRule.createDefault();
+
+ private final ConnectorHubTestFeature connectorHubTestFeature = ConnectorHubTestFeature.createDefault(fn);
+
+ @Test
+ public void testInvokeFunctionWithStreamingData() throws Exception {
+
+ ConnectorHubBatch> event = createMinimalRequest();
+ connectorHubTestFeature.givenEvent(event).enqueue();
+
+ fn.thenRun(Function.class, "handler");
+
+ FnResult result = fn.getOnlyResult();
+ assertEquals(200, result.getStatus().getCode());
+ }
+
+ private static ConnectorHubBatch> createMinimalRequest() {
+ Employee employee = new Employee();
+ employee.setName("foo");
+
+ StreamingData source = new StreamingData<>(
+ "stream-name",
+ "0",
+ null,
+ employee,
+ "3",
+ new Date(1764860467553L)
+ );
+ ConnectorHubBatch> event = mock(ConnectorHubBatch.class);
+
+ when(event.getBatch()).thenReturn(Collections.singletonList(source));
+ return event;
+ }
+}
\ No newline at end of file
diff --git a/examples/gradle-build/.dockerignore b/examples/gradle-build/.dockerignore
new file mode 100644
index 00000000..531da109
--- /dev/null
+++ b/examples/gradle-build/.dockerignore
@@ -0,0 +1,4 @@
+.gradle
+.idea
+.git
+build
\ No newline at end of file
diff --git a/examples/gradle-build/Dockerfile b/examples/gradle-build/Dockerfile
new file mode 100644
index 00000000..cca84dbb
--- /dev/null
+++ b/examples/gradle-build/Dockerfile
@@ -0,0 +1,36 @@
+#
+# Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+FROM gradle:4.5.1-jdk8 as build-stage
+WORKDIR /function
+# needed for gradle?
+USER root
+ENV GRADLE_USER_HOME /function/.gradle
+
+# Code build
+# Copy any build files into the build container and build
+COPY *.gradle /function/
+RUN ["gradle", "-s", "--no-daemon","--console","plain","cacheDeps"]
+
+# Copies build source into build container
+COPY src /function/src
+
+RUN ["gradle", "-s", "--no-daemon","--console","plain","build"]
+# Container build
+FROM fnproject/fn-java-fdk:1.0.56
+WORKDIR /function
+COPY --from=build-stage /function/build/libs/*.jar /function/build/deps/*.jar /function/app/
+CMD ["com.example.fn.HelloFunction::handleRequest"]
diff --git a/examples/gradle-build/README.md b/examples/gradle-build/README.md
new file mode 100644
index 00000000..3214e0b3
--- /dev/null
+++ b/examples/gradle-build/README.md
@@ -0,0 +1,14 @@
+# Fn Gradle + Java fdk example
+
+Fn uses Maven by default for builds. This is an example that uses Fn's `docker` runtime format to build a Java function using the [Fn Java FDK](https://github.com/fnproject/fdk-java).
+
+The example consists of a `Dockerfile` that builds the function using gradle and copies the function's dependencies to `build/deps` and a func.yaml that uses that `Dockerfile` to build the function.
+
+Note that fdk.versions are hard-coded in this example, you may need to update them manually to more recent version.
+
+Key points:
+
+* [Dockerfile](Dockerfile) - contains the containerised docker build (based on dockerhub library/gradle images) and image build - this includes the gradle invocation
+* The `cacheDeps` task in `build.gradle` is invoked within the Dockerfile - The task pulls down dependencies into the container gradle cache to speed up docker builds.
+* The `copyDeps` task in `build.gradle` copies the functions compile deps
+* This uses JDK 8 by default - you can change this to Java 11 by changing : `FROM gradle:4.5.1-jdk8 as build-stage` to `FROM gradle:4.5.1-jre11 as build-stage` and `FROM fnproject/fn-java-fdk:1.0.85` to `FROM fnproject/fn-java-fdk:jre11-1.0.85`
\ No newline at end of file
diff --git a/examples/gradle-build/build.gradle b/examples/gradle-build/build.gradle
new file mode 100644
index 00000000..6fb0925a
--- /dev/null
+++ b/examples/gradle-build/build.gradle
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply plugin: 'java'
+
+ext {
+ fdkVersion = '1.0.56'
+}
+
+repositories {
+ mavenCentral()
+ maven {
+ url 'https://dl.bintray.com/fnproject/fnproject'
+ }
+}
+
+dependencies {
+ runtime "com.fnproject.fn:api:$fdkVersion"
+ // runtime "com.fnproject.fn:runtime:$fdkVersion" // this is optional and included with its deps in the base image to reduce layer size
+
+ testCompile "junit:junit:4.12"
+ testCompile "com.fnproject.fn:testing:$fdkVersion"
+}
+
+task cacheDeps(type: Exec) {
+ configurations.testRuntime.files
+ commandLine 'echo', 'Downloaded all dependencies'
+}
+
+task copyDeps(type: Copy) {
+ from configurations.compile
+ into "${project.buildDir}/deps"
+}
+
+build.dependsOn copyDeps
diff --git a/examples/gradle-build/func.yaml b/examples/gradle-build/func.yaml
new file mode 100644
index 00000000..bc23a27a
--- /dev/null
+++ b/examples/gradle-build/func.yaml
@@ -0,0 +1,20 @@
+#
+# Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+name: gradle_build
+version: 0.0.3
+runtime: docker
+format: http
diff --git a/examples/gradle-build/src/main/java/com/example/fn/HelloFunction.java b/examples/gradle-build/src/main/java/com/example/fn/HelloFunction.java
new file mode 100644
index 00000000..c335e147
--- /dev/null
+++ b/examples/gradle-build/src/main/java/com/example/fn/HelloFunction.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.fn;
+
+public class HelloFunction {
+
+ public String handleRequest(String input) {
+ String name = (input == null || input.isEmpty()) ? "world" : input;
+ return "Hello, " + name + "!";
+ }
+
+}
\ No newline at end of file
diff --git a/examples/gradle-build/src/test/java/com/example/fn/HelloFunctionTest.java b/examples/gradle-build/src/test/java/com/example/fn/HelloFunctionTest.java
new file mode 100644
index 00000000..5a036a2a
--- /dev/null
+++ b/examples/gradle-build/src/test/java/com/example/fn/HelloFunctionTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.fn;
+
+import static org.junit.Assert.*;
+
+public class HelloFunctionTest {
+
+ @Rule
+ public final FnTestingRule testing = FnTestingRule.createDefault();
+
+ @Test
+ public void shouldReturnGreeting() {
+ testing.givenEvent().enqueue();
+ testing.thenRun(HelloFunction.class, "handleRequest");
+
+ FnResult result = testing.getOnlyResult();
+ assertEquals("Hello, world!", result.getBodyAsString());
+ }
+
+}
\ No newline at end of file
diff --git a/examples/notifications/README.md b/examples/notifications/README.md
new file mode 100644
index 00000000..2966b991
--- /dev/null
+++ b/examples/notifications/README.md
@@ -0,0 +1,116 @@
+# Example Fn Java FDK : Notifications
+
+This example provides a Function to consume a Notification.
+The function accepts a typed message event.
+
+## Source
+
+* Notifications appends metadata to the headers [Standard header metadata](https://docs.oracle.com/en-us/iaas/Content/Notification/Concepts/notificationoverview.htm#hownw)
+* The Notification message body is a user specified Schema or String.
+Note: This library currently supports JSON and String types.
+* Limits for Notifications [Notification limits](https://docs.oracle.com/en-us/iaas/Content/Notification/Concepts/notificationoverview.htm#limits)
+
+## Dependencies
+* [fn-events](../../fn-events) for NotificationFunction classes.
+* [fn-events-testing](../../fn-events-testing) for NotificationFunction testing library.
+
+## Demonstrated FDK features
+
+This example showcases how to use the fn-event NotificationFunction to
+use a Function to consume messages from a Notification topic.
+
+## Step by step
+
+Set up the Notification Topic with Function Subscription:
+* [Setup default policies](https://docs.oracle.com/en-us/iaas/Content/Security/Reference/notifications_security.htm#iam-policies__subs)
+* [create subscription function](https://docs.oracle.com/en-us/iaas/Content/Notification/Tasks/create-subscription-function.htm)
+
+The Function entrypoint extends the `NotificationFunction` abstract class.
+Note: the [func.yaml](func.yaml) entrypoint remains the class which extends `NotificationFunction`
+e.g. `cmd: com.fnproject.fn.examples.Function::handler`
+
+[Function.java](src/main/java/com/fnproject/fn/examples/Function.java)
+
+```java
+package com.fnproject.fn.examples;
+
+import com.fnproject.events.NotificationFunction;
+import com.fnproject.events.input.NotificationMessage;
+
+public class Function extends NotificationFunction {
+
+ public NotificationService notificationService;
+
+ public Function() {
+ this.notificationService = new NotificationService();
+ }
+
+ @Override
+ public void handler(NotificationMessage content) {
+ notificationService.readNotification(content);
+ }
+}
+```
+The [NotificationMessage.java](../../fn-events/src/main/java/com/fnproject/events/input/NotificationMessage.java)
+`content` contains the message body.
+
+The class [Employee.java](src/main/java/com/fnproject/fn/examples/Employee.java) is
+the schema for the message content.
+
+[Function.java](src/main/java/com/fnproject/fn/examples/Function.java)
+`public class Function extends NotificationFunction`.
+
+To return an error response, throw RuntimeException.class.
+Doing so will cause the Function to return a 502 [Retry policy](https://docs.oracle.com/en-us/iaas/Content/connector-hub/overview.htm#deactivate)
+
+## Test walkthrough
+
+Unit testing `NotificationFunction` is supported with the `NotificationTestFeature` and `FnTestingRule`.
+
+First of all, the class initializes the `FnTestingRule` harness, as explained
+in [Testing Functions](../../docs/TestingFunctions.md).
+
+[FunctionTest.java](src/test/java/com/fnproject/fn/examples/FunctionTest.java)
+```java
+package com.fnproject.fn.examples;
+
+import static org.junit.Assert.assertEquals;
+import com.fnproject.events.input.NotificationMessage;
+import com.fnproject.events.testing.NotificationTestFeature;
+import com.fnproject.fn.api.Headers;
+import com.fnproject.fn.testing.FnResult;
+import com.fnproject.fn.testing.FnTestingRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class FunctionTest {
+
+ @Rule
+ public FnTestingRule fn = FnTestingRule.createDefault();
+
+ private final NotificationTestFeature connectorHubTestFeature = NotificationTestFeature.createDefault(fn);
+
+ @Test
+ public void testInvokeFunctionWithLoggingData() throws Exception {
+ NotificationMessage event = createMinimalRequest();
+ connectorHubTestFeature.givenEvent(event).enqueue();
+
+ fn.thenRun(Function.class, "handler");
+
+ FnResult result = fn.getOnlyResult();
+ assertEquals(200, result.getStatus().getCode());
+ }
+
+ private static NotificationMessage createMinimalRequest() {
+ Employee employee = new Employee();
+ employee.setName("foo");
+
+ return new NotificationMessage<>(employee, Headers.emptyHeaders());
+ }
+}
+```
+
+Use `connectorHubTestFeature.givenEvent(event).enqueue();` to queue the request event
+and invoke the Function with `fn.thenRun(Function.class, "handler");`.
+
+To verify the Subscription is working, check [Notification metrics](https://docs.oracle.com/en-us/iaas/Content/Notification/Tasks/view-chart-resource.htm#top)
\ No newline at end of file
diff --git a/examples/notifications/func.yaml b/examples/notifications/func.yaml
new file mode 100644
index 00000000..e065909d
--- /dev/null
+++ b/examples/notifications/func.yaml
@@ -0,0 +1,5 @@
+schema_version: 20180708
+name: notifications
+version: 0.0.1
+runtime: java
+cmd: com.fnproject.fn.examples.Function::handler
diff --git a/examples/notifications/pom.xml b/examples/notifications/pom.xml
new file mode 100644
index 00000000..e470aa39
--- /dev/null
+++ b/examples/notifications/pom.xml
@@ -0,0 +1,112 @@
+
+
+
+
+ 4.0.0
+
+
+ UTF-8
+ UTF-8
+ 1.0.0-SNAPSHOT
+ 2.16.1
+ 4.0.0
+
+
+ com.fnproject.fn.examples
+ notifications
+ 1.0.0-SNAPSHOT
+
+
+
+ com.fnproject.fn
+ api
+ ${fdk.version}
+
+
+ com.fnproject.fn
+ fn-events
+ ${fdk.version}
+
+
+ com.fnproject.fn
+ fn-events-testing
+ ${fdk.version}
+ test
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
+
+ org.mockito
+ mockito-core
+ ${mockito.version}
+ test
+
+
+ com.fnproject.fn
+ testing-core
+ ${fdk.version}
+ test
+
+
+ com.fnproject.fn
+ testing-junit4
+ ${fdk.version}
+ test
+
+
+
+
+ faas-release-maven-local
+ https://artifactory-builds.oci.oraclecorp.com/faas-release-maven-local
+
+ true
+
+
+ true
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.0
+
+ 1.8
+ 1.8
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.8.2
+
+ true
+
+
+
+
+
diff --git a/examples/notifications/src/main/java/com/fnproject/fn/examples/Employee.java b/examples/notifications/src/main/java/com/fnproject/fn/examples/Employee.java
new file mode 100644
index 00000000..21079ea8
--- /dev/null
+++ b/examples/notifications/src/main/java/com/fnproject/fn/examples/Employee.java
@@ -0,0 +1,41 @@
+package com.fnproject.fn.examples;
+
+import java.util.Objects;
+
+public class Employee {
+ private String name;
+
+ public Employee() {}
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Employee employee = (Employee) o;
+ return Objects.equals(name, employee.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(name);
+ }
+
+ @Override
+ public String toString() {
+ return "Employee{" +
+ "name='" + name + '\'' +
+ '}';
+ }
+}
diff --git a/examples/notifications/src/main/java/com/fnproject/fn/examples/Function.java b/examples/notifications/src/main/java/com/fnproject/fn/examples/Function.java
new file mode 100644
index 00000000..923c650c
--- /dev/null
+++ b/examples/notifications/src/main/java/com/fnproject/fn/examples/Function.java
@@ -0,0 +1,18 @@
+package com.fnproject.fn.examples;
+
+import com.fnproject.events.NotificationFunction;
+import com.fnproject.events.input.NotificationMessage;
+
+public class Function extends NotificationFunction {
+
+ public NotificationService notificationService;
+
+ public Function() {
+ this.notificationService = new NotificationService();
+ }
+
+ @Override
+ public void handler(NotificationMessage content) {
+ notificationService.readNotification(content);
+ }
+}
\ No newline at end of file
diff --git a/examples/notifications/src/main/java/com/fnproject/fn/examples/NotificationService.java b/examples/notifications/src/main/java/com/fnproject/fn/examples/NotificationService.java
new file mode 100644
index 00000000..3e1ed461
--- /dev/null
+++ b/examples/notifications/src/main/java/com/fnproject/fn/examples/NotificationService.java
@@ -0,0 +1,10 @@
+package com.fnproject.fn.examples;
+
+import com.fnproject.events.input.NotificationMessage;
+
+public class NotificationService {
+
+ public void readNotification(NotificationMessage notification) {
+ System.out.println(notification);
+ }
+}
diff --git a/examples/notifications/src/test/java/com/fnproject/fn/examples/FunctionTest.java b/examples/notifications/src/test/java/com/fnproject/fn/examples/FunctionTest.java
new file mode 100644
index 00000000..3afca9e1
--- /dev/null
+++ b/examples/notifications/src/test/java/com/fnproject/fn/examples/FunctionTest.java
@@ -0,0 +1,36 @@
+package com.fnproject.fn.examples;
+
+import static org.junit.Assert.assertEquals;
+import com.fnproject.events.input.NotificationMessage;
+import com.fnproject.events.testing.NotificationTestFeature;
+import com.fnproject.fn.api.Headers;
+import com.fnproject.fn.testing.FnResult;
+import com.fnproject.fn.testing.FnTestingRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class FunctionTest {
+
+ @Rule
+ public FnTestingRule fn = FnTestingRule.createDefault();
+
+ private final NotificationTestFeature connectorHubTestFeature = NotificationTestFeature.createDefault(fn);
+
+ @Test
+ public void testInvokeFunctionWithLoggingData() throws Exception {
+ NotificationMessage event = createMinimalRequest();
+ connectorHubTestFeature.givenEvent(event).enqueue();
+
+ fn.thenRun(Function.class, "handler");
+
+ FnResult result = fn.getOnlyResult();
+ assertEquals(200, result.getStatus().getCode());
+ }
+
+ private static NotificationMessage createMinimalRequest() {
+ Employee employee = new Employee();
+ employee.setName("foo");
+
+ return new NotificationMessage<>(employee, Headers.emptyHeaders());
+ }
+}
\ No newline at end of file
diff --git a/examples/pom.xml b/examples/pom.xml
index 219291f4..34a63435 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -1,10 +1,29 @@
+
+
4.0.0pomfdk-examples
+ fdk-examplescom.fnproject.fn.examples
@@ -13,10 +32,6 @@
1.0.0-SNAPSHOT
-
- UTF-8
-
-
string-reverseregex-query
@@ -24,6 +39,12 @@
qr-code
-->
async-thumbnails
+ apigateway-event
+ connectorhub-monitoring
+ connectorhub-logging
+ connectorhub-streaming
+ connectorhub-queue
+ notifications
@@ -31,7 +52,6 @@
org.apache.maven.pluginsmaven-deploy-plugin
- 2.8.2true
diff --git a/examples/qr-code/README.md b/examples/qr-code/README.md
index d511c970..eda10787 100644
--- a/examples/qr-code/README.md
+++ b/examples/qr-code/README.md
@@ -31,8 +31,8 @@ $ fn build
Create an app and route to host the function
```bash
-$ fn apps create qr-app
-$ fn routes create qr-app /qr
+$ fn create app qr-app
+$ fn create route qr-app /qr
```
Invoke the function to create a QR code
@@ -54,21 +54,22 @@ of the example function. The body of the function is shown below:
```java
-public OutputEvent create(InputEvent event) throws MalformedURLException, UnsupportedEncodingException {
- String decodedUrl = URLDecoder.decode(event.getRequestUrl(), "utf-8");
- QueryParameters params = getParams(decodedUrl);
- ImageType type = getFormat(params.getFirst("format").orElse("png"));
- String contents = params.getFirst("contents").orElse("");
-
- ByteArrayOutputStream stream = QRCode.from(contents).to(type).stream();
- System.err.println("Generated QR Code for contents: " + contents);
- return OutputEvent.fromBytes(stream.toByteArray(), true, getMimeType(type));
-}
+
+ public byte[] create(HTTPGatewayContext hctx) {
+ ImageType type = getFormat(hctx.getQueryParameters().get("format").orElse(defaultFormat));
+ System.err.println("Default format: " + type.toString());
+ String contents = hctx.getQueryParameters().get("contents").orElseThrow(() -> new RuntimeException("Contents must be provided to the QR code"));
+
+ ByteArrayOutputStream stream = QRCode.from(contents).to(type).stream();
+ System.err.println("Generated QR Code for contents: " + contents);
+
+ hctx.setResponseHeader("Content-Type", getMimeType(type));
+ return stream.toByteArray();
+ }
+
```
-The fn Java FDK facilitates access to the internal events representing the
-invocation of the function, `InputEvent`, and response of the function,
-`OutputEvent`, for more fine grained control of the platform. See
+The fn Java FDK facilitates access to the HTTP context of events triggered from HTTP gateways via `HTTPGatewayContext` , and response of the function as a bye array, for more fine grained control of the platform. See
[Data Binding](/docs/DataBinding.md) for further information on the types
of input that the fn Java FDK provides.
@@ -116,17 +117,17 @@ this to handle invocations of functions and retrieving function results
```java
...
- @Test
- public void textHelloWorld() throws Exception {
- fn.givenEvent()
- .withRequestUrl("http://www.example.com/qr?contents=" + URLEncoder.encode("hello world", "utf-8"))
- .withMethod("GET")
- .enqueue();
- fn.thenRun(QRGen.class, "create");
-
- assertArrayEquals(readTestFile("qr-code-text-hello-world.png"), fn.getOnlyResult().getBodyAsBytes());
- }
-...
+ @Test
+ public void textHelloWorld() throws Exception {
+ String content = "hello world";
+ fn.givenEvent()
+ .withHeader("Fn-Http-Request-Url", "http://www.example.com/qr?contents=hello+world&format=png")
+ .withHeader("Fn-Http-Method","GET")
+ .enqueue();
+ fn.thenRun(QRGen.class, "create");
+
+ assertEquals(content, decode(fn.getOnlyResult().getBodyAsBytes()));
+ }
```
Input events are constructed using `fn.givenEvent()` providing an `FnEventBuilder`
diff --git a/examples/qr-code/example.html b/examples/qr-code/example.html
index fb7d1f6a..d4d964c2 100644
--- a/examples/qr-code/example.html
+++ b/examples/qr-code/example.html
@@ -1,3 +1,21 @@
+
+
diff --git a/examples/qr-code/func.yaml b/examples/qr-code/func.yaml
index ccff7c49..4ca1732e 100644
--- a/examples/qr-code/func.yaml
+++ b/examples/qr-code/func.yaml
@@ -1,3 +1,19 @@
+#
+# Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
name: fn-example/qr-code-gen
version: 0.0.1
runtime: java
diff --git a/examples/qr-code/pom.xml b/examples/qr-code/pom.xml
index e7941657..b9cec937 100644
--- a/examples/qr-code/pom.xml
+++ b/examples/qr-code/pom.xml
@@ -1,4 +1,22 @@
+
+
@@ -7,7 +25,7 @@
UTF-8
- 1.0.0-SNAPSHOT
+ 1.0.0-SNAPSHOTcom.fnproject.fn.examples
@@ -24,19 +42,25 @@
com.fnproject.fnapi
- ${fnproject.version}
+ ${fdk.version}junitjunit
- 4.12
+ 4.13.1testcom.fnproject.fntesting
- ${fnproject.version}
+ ${fdk.version}
+ test
+
+
+ com.fnproject.fn
+ testing-junit4
+ ${fdk.version}test
@@ -45,7 +69,7 @@
org.apache.maven.pluginsmaven-compiler-plugin
- 3.3
+ 3.8.01.81.8
diff --git a/examples/qr-code/src/main/java/com/fnproject/fn/examples/QRGen.java b/examples/qr-code/src/main/java/com/fnproject/fn/examples/QRGen.java
index a95caa3d..99b886b9 100644
--- a/examples/qr-code/src/main/java/com/fnproject/fn/examples/QRGen.java
+++ b/examples/qr-code/src/main/java/com/fnproject/fn/examples/QRGen.java
@@ -1,13 +1,27 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.examples;
-import com.fnproject.fn.api.InputEvent;
-import com.fnproject.fn.api.OutputEvent;
import com.fnproject.fn.api.RuntimeContext;
+import com.fnproject.fn.api.httpgateway.HTTPGatewayContext;
import net.glxn.qrgen.core.image.ImageType;
import net.glxn.qrgen.javase.QRCode;
-import java.io.*;
-import java.net.MalformedURLException;
+import java.io.ByteArrayOutputStream;
public class QRGen {
private final String defaultFormat;
@@ -16,19 +30,20 @@ public QRGen(RuntimeContext ctx) {
defaultFormat = ctx.getConfigurationByKey("FORMAT").orElse("png");
}
- public OutputEvent create(InputEvent event) throws MalformedURLException, UnsupportedEncodingException {
- ImageType type = getFormat(event.getQueryParameters().get("format").orElse(defaultFormat));
+ public byte[] create(HTTPGatewayContext hctx) {
+ ImageType type = getFormat(hctx.getQueryParameters().get("format").orElse(defaultFormat));
System.err.println("Default format: " + type.toString());
- String contents = event.getQueryParameters().get("contents").orElseThrow(() -> new RuntimeException("Contents must be provided to the QR code"));
+ String contents = hctx.getQueryParameters().get("contents").orElseThrow(() -> new RuntimeException("Contents must be provided to the QR code"));
ByteArrayOutputStream stream = QRCode.from(contents).to(type).stream();
System.err.println("Generated QR Code for contents: " + contents);
- return OutputEvent.fromBytes(stream.toByteArray(), OutputEvent.SUCCESS, getMimeType(type));
+ hctx.setResponseHeader("Content-Type", getMimeType(type));
+ return stream.toByteArray();
}
private ImageType getFormat(String extension) {
- switch(extension.toLowerCase()) {
+ switch (extension.toLowerCase()) {
case "png":
return ImageType.PNG;
case "jpg":
@@ -43,8 +58,8 @@ private ImageType getFormat(String extension) {
}
}
- private String getMimeType(ImageType type) {
- switch(type) {
+ private String getMimeType(ImageType type) {
+ switch (type) {
case JPG:
return "image/jpeg";
case GIF:
@@ -56,5 +71,5 @@ private String getMimeType(ImageType type) {
default:
throw new RuntimeException("Invalid ImageType: " + type);
}
- }
+ }
}
diff --git a/examples/qr-code/src/test/java/com/fnproject/fn/examples/QRGenTest.java b/examples/qr-code/src/test/java/com/fnproject/fn/examples/QRGenTest.java
index d727e941..cb376482 100644
--- a/examples/qr-code/src/test/java/com/fnproject/fn/examples/QRGenTest.java
+++ b/examples/qr-code/src/test/java/com/fnproject/fn/examples/QRGenTest.java
@@ -1,7 +1,26 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.examples;
import com.fnproject.fn.testing.FnTestingRule;
-import com.google.zxing.*;
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.ChecksumException;
+import com.google.zxing.FormatException;
+import com.google.zxing.NotFoundException;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
@@ -23,11 +42,9 @@ public class QRGenTest {
public void textHelloWorld() throws Exception {
String content = "hello world";
fn.givenEvent()
- .withRequestUrl("http://www.example.com/qr")
- .withQueryParameter("contents", content)
- .withQueryParameter("format", "png")
- .withMethod("GET")
- .enqueue();
+ .withHeader("Fn-Http-Request-Url", "http://www.example.com/qr?contents=hello+world&format=png")
+ .withHeader("Fn-Http-Method","GET")
+ .enqueue();
fn.thenRun(QRGen.class, "create");
assertEquals(content, decode(fn.getOnlyResult().getBodyAsBytes()));
@@ -37,10 +54,9 @@ public void textHelloWorld() throws Exception {
public void phoneNumber() throws Exception {
String telephoneNumber = "tel:0-12345-67890";
fn.givenEvent()
- .withRequestUrl("http://www.example.com/qr")
- .withQueryParameter("contents", telephoneNumber)
- .withMethod("GET")
- .enqueue();
+ .withHeader("Fn-Http-Request-Url", "http://www.example.com/qr?contents=tel:0-12345-67890")
+ .withHeader("Fn-Http-Method","GET")
+ .enqueue();
fn.thenRun(QRGen.class, "create");
assertEquals(telephoneNumber, decode(fn.getOnlyResult().getBodyAsBytes()));
@@ -51,14 +67,14 @@ public void formatConfigurationIsUsedIfNoFormatIsProvided() throws Exception {
String contents = "hello world";
fn.setConfig("FORMAT", "jpg");
fn.givenEvent()
- .withRequestUrl("http://www.example.com/qr")
- .withQueryParameter("contents", contents)
- .withMethod("GET")
- .enqueue();
+ .withHeader("Fn-Http-Request-Url", "http://www.example.com/qr?contents=hello+world")
+ .withHeader("Fn-Http-Method","GET")
+ .enqueue();
fn.thenRun(QRGen.class, "create");
assertEquals(contents, decode(fn.getOnlyResult().getBodyAsBytes()));
}
+
private String decode(byte[] imageBytes) throws IOException, NotFoundException, ChecksumException, FormatException {
BinaryBitmap bitmap = readToBitmap(imageBytes);
return new QRCodeReader().decode(bitmap).getText();
diff --git a/examples/regex-query/README.md b/examples/regex-query/README.md
index a9d67155..f7a21c6e 100644
--- a/examples/regex-query/README.md
+++ b/examples/regex-query/README.md
@@ -33,8 +33,8 @@ $ fn build
Create an app and route to host the function
```bash
-$ fn apps create regex-query
-$ fn routes create regex-query /query
+$ fn create app regex-query
+$ fn create route regex-query /query
```
Invoke the function to perform a regex search
diff --git a/examples/regex-query/func.yaml b/examples/regex-query/func.yaml
index b216f06e..d14a3f37 100644
--- a/examples/regex-query/func.yaml
+++ b/examples/regex-query/func.yaml
@@ -1,3 +1,19 @@
+#
+# Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
name: fn-example/regex-query
version: 0.0.1
runtime: java
diff --git a/examples/regex-query/pom.xml b/examples/regex-query/pom.xml
index 23961362..ac7bead1 100644
--- a/examples/regex-query/pom.xml
+++ b/examples/regex-query/pom.xml
@@ -1,4 +1,22 @@
+
+
@@ -6,8 +24,10 @@
UTF-8
- 1.0.0-SNAPSHOT
- 2.8.9
+ UTF-8
+
+ 1.0.0-SNAPSHOT
+ 2.16.1com.fnproject.fn.examples
@@ -23,18 +43,24 @@
com.fasterxml.jackson.corejackson-annotations
- ${jackson.version}
+ 2.13.4junitjunit
- 4.12
+ 4.13.2
+ test
+
+
+ com.fnproject.fn
+ testing-core
+ ${fdk.version}testcom.fnproject.fn
- testing
- ${fnproject.version}
+ testing-junit4
+ ${fdk.version}test
@@ -50,7 +76,7 @@
org.apache.maven.pluginsmaven-compiler-plugin
- 3.3
+ 3.8.01.81.8
@@ -66,11 +92,4 @@
-
-
-
- fn-maven-releases
- https://dl.bintray.com/fnproject/fnproject
-
-
diff --git a/examples/regex-query/src/main/java/com/fnproject/fn/examples/Match.java b/examples/regex-query/src/main/java/com/fnproject/fn/examples/Match.java
index d4b1188c..f2bb4f90 100644
--- a/examples/regex-query/src/main/java/com/fnproject/fn/examples/Match.java
+++ b/examples/regex-query/src/main/java/com/fnproject/fn/examples/Match.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.examples;
import com.fasterxml.jackson.annotation.JsonCreator;
diff --git a/examples/regex-query/src/main/java/com/fnproject/fn/examples/Query.java b/examples/regex-query/src/main/java/com/fnproject/fn/examples/Query.java
index e927400e..fe5a9256 100644
--- a/examples/regex-query/src/main/java/com/fnproject/fn/examples/Query.java
+++ b/examples/regex-query/src/main/java/com/fnproject/fn/examples/Query.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.examples;
import com.fasterxml.jackson.annotation.JsonCreator;
diff --git a/examples/regex-query/src/main/java/com/fnproject/fn/examples/RegexQuery.java b/examples/regex-query/src/main/java/com/fnproject/fn/examples/RegexQuery.java
index cbfd993e..aab1c7bb 100644
--- a/examples/regex-query/src/main/java/com/fnproject/fn/examples/RegexQuery.java
+++ b/examples/regex-query/src/main/java/com/fnproject/fn/examples/RegexQuery.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.examples;
import java.util.ArrayList;
diff --git a/examples/regex-query/src/main/java/com/fnproject/fn/examples/Response.java b/examples/regex-query/src/main/java/com/fnproject/fn/examples/Response.java
index af939b9f..0f7330a5 100644
--- a/examples/regex-query/src/main/java/com/fnproject/fn/examples/Response.java
+++ b/examples/regex-query/src/main/java/com/fnproject/fn/examples/Response.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.examples;
import com.fasterxml.jackson.annotation.JsonCreator;
diff --git a/examples/regex-query/src/test/java/com/fnproject/fn/examples/RegexQueryTests.java b/examples/regex-query/src/test/java/com/fnproject/fn/examples/RegexQueryTests.java
index e5a44626..d0e241d5 100644
--- a/examples/regex-query/src/test/java/com/fnproject/fn/examples/RegexQueryTests.java
+++ b/examples/regex-query/src/test/java/com/fnproject/fn/examples/RegexQueryTests.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.examples;
import com.fnproject.fn.testing.FnTestingRule;
@@ -15,7 +31,6 @@ public void matchingSingleCharacter() throws JSONException {
String text = "a";
String regex = ".";
fn.givenEvent()
- .withMethod("POST")
.withBody(String.format("{\"text\": \"%s\", \"regex\": \"%s\"}", text, regex))
.enqueue();
@@ -34,7 +49,6 @@ public void matchingSingleCharacterMultipleTimes() throws JSONException {
String text = "abc";
String regex = ".";
fn.givenEvent()
- .withMethod("POST")
.withBody(String.format("{\"text\": \"%s\", \"regex\": \"%s\"}", text, regex))
.enqueue();
diff --git a/examples/string-reverse/README.md b/examples/string-reverse/README.md
index 9c52dae4..97eb79a0 100644
--- a/examples/string-reverse/README.md
+++ b/examples/string-reverse/README.md
@@ -1,71 +1,75 @@
-# Example oFunctions Project: String Reverse
+# Example Java Function: String Reverse
-This example provides an HTTP endpoint for reversing strings
+This example provides an HTTP trigger endpoint for reversing strings.
```bash
-$ curl -d "Hello, World!" "http://localhost:8080/r/string-reverse-app/reverse"
-!dlroW ,olleH
+$ curl -d "Hello World" http://localhost:8080/t/string-reverse-app/string-reverse
+dlroW olleH
```
## Demonstrated FDK features
-This example uses **no** features of the fn Java FDK; in fact it doesn't have
-a dependency on the fn Java FDK, it just plain old Java code.
+This example uses **none** of the Fn Java FDK features, in fact it doesn't have
+any dependency on the Fn Java FDK. It is just plain old Java code.
## Step by step
-Ensure you have the functions server running using, this will host your
-function and provide the HTTP endpoints that invoke it:
+Ensure you have the Fn server running to host your
+function and provide the HTTP endpoint that invokes it:
-```bash
+(1) Start the server
+
+```sh
$ fn start
```
-Build the function locally
+(2) Create an app for the function
-```bash
-$ fn build
+```sh
+$ fn create app string-reverse-app
```
-Create an app and route to host the function
+(3) Deploy the function to your app from the `string-reverse` directory.
-```bash
-$ fn apps create string-reverse-app
-$ fn routes create string-reverse-app /reverse
+```sh
+fn deploy --app string-reverse-app --local
+```
+
+(4) Invoke the function and reverse the string.
+
+```sh
+echo "Hello World" | fn invoke string-reverse-app string-reverse
+dlroW olleH
```
-Invoke the function to reverse a string
+(5) Invoke the function using curl and a trigger to reverse a string.
```bash
-$ curl -d "Hello, World!" "http://localhost:8080/r/string-reverse-app/reverse"
-!dlroW ,olleH
+$ curl -d "Hello World" http://localhost:8080/t/string-reverse-app/string-reverse
+dlroW olleH
```
## Code walkthrough
The entrypoint to the function is specified in `func.yaml` in the `cmd` key.
-It is set this to `com.fnproject.fn.examples.StringReverse::reverse`. The whole class
+It is set this to `com.example.fn.StringReverse::reverse`. The whole class
`StringReverse` is shown below:
```java
-package com.fnproject.fn.examples;
+package com.example.fn;
public class StringReverse {
public String reverse(String str) {
- StringBuilder builder = new StringBuilder();
- for (int i = str.length() - 1; i >= 0; i--) {
- builder.append(str.charAt(i));
- }
- return builder.toString();
+ return new StringBuilder(str).reverse().toString();
}
}
```
-As you can see, this is plain java with no references to the fn API. The
-fn Java FDK handles the marshalling of the HTTP body into the `str`
+As you can see, this is plain java with no references to the Fn API. The
+Fn Java FDK handles the marshalling of the HTTP body into the `str`
parameter as well as the marshalling of the returned reversed string into the HTTP
response body (see [Data Binding](/docs/DataBinding.md) for more
information on how marshalling is performed).
diff --git a/examples/string-reverse/func.yaml b/examples/string-reverse/func.yaml
index 197eebe2..06045be1 100644
--- a/examples/string-reverse/func.yaml
+++ b/examples/string-reverse/func.yaml
@@ -1,7 +1,27 @@
-name: fn-example/string-reverse
-version: 0.0.1
+#
+# Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+schema_version: 20180708
+name: string-reverse
+version: 0.0.2
runtime: java
-timeout: 30
-format: http
-cmd: com.fnproject.fn.examples.StringReverse::reverse
-path: /reverse
+build_image: fnproject/fn-java-fdk-build:jdk11-1.0.87
+run_image: fnproject/fn-java-fdk:jre11-1.0.87
+cmd: com.example.fn.StringReverse::reverse
+triggers:
+- name: string-reverse
+ type: http
+ source: /string-reverse
diff --git a/examples/string-reverse/pom.xml b/examples/string-reverse/pom.xml
index 4c380ea1..472b033b 100644
--- a/examples/string-reverse/pom.xml
+++ b/examples/string-reverse/pom.xml
@@ -1,23 +1,57 @@
+
+
4.0.0
-
-
UTF-8
+ 1.0.0-SNAPSHOT
-
com.fnproject.fn.examplesstring-reverse1.0.0-SNAPSHOT
+
+ com.fnproject.fn
+ api
+ ${fdk.version}
+
+
+ com.fnproject.fn
+ testing-core
+ ${fdk.version}
+ test
+
+
+ com.fnproject.fn
+ testing-junit4
+ ${fdk.version}
+ test
+ junitjunit
- 4.12
+ 4.13.2
+ test
@@ -28,8 +62,8 @@
maven-compiler-plugin3.3
- 1.8
- 1.8
+ 8
+ 8
@@ -40,13 +74,14 @@
true
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.22.1
+
+ false
+
+
-
-
-
- fn-maven-releases
- https://dl.bintray.com/fnproject/fnproject
-
-
diff --git a/examples/string-reverse/src/main/java/com/example/fn/StringReverse.java b/examples/string-reverse/src/main/java/com/example/fn/StringReverse.java
new file mode 100644
index 00000000..6c66f8b2
--- /dev/null
+++ b/examples/string-reverse/src/main/java/com/example/fn/StringReverse.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.fn;
+
+public class StringReverse {
+ public String reverse(String str) {
+ return new StringBuilder(str).reverse().toString();
+ }
+}
diff --git a/examples/string-reverse/src/main/java/com/fnproject/fn/examples/StringReverse.java b/examples/string-reverse/src/main/java/com/fnproject/fn/examples/StringReverse.java
deleted file mode 100644
index e95ce08e..00000000
--- a/examples/string-reverse/src/main/java/com/fnproject/fn/examples/StringReverse.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.fnproject.fn.examples;
-
-public class StringReverse {
- public String reverse(String str) {
- StringBuilder builder = new StringBuilder();
- for (int i = str.length() - 1; i >= 0; i--) {
- builder.append(str.charAt(i));
- }
- return builder.toString();
- }
-}
diff --git a/examples/string-reverse/src/test/java/com/example/fn/testing/StringReverseTest.java b/examples/string-reverse/src/test/java/com/example/fn/testing/StringReverseTest.java
new file mode 100644
index 00000000..19c2af40
--- /dev/null
+++ b/examples/string-reverse/src/test/java/com/example/fn/testing/StringReverseTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.fn.testing;
+
+import com.example.fn.StringReverse;
+import org.junit.Test;
+
+import static junit.framework.TestCase.assertEquals;
+
+public class StringReverseTest {
+ private StringReverse stringReverse = new StringReverse();
+
+ @Test
+ public void reverseEmptyString() {
+ assertEquals("", reverse(""));
+ }
+
+ @Test
+ public void reverseOfSingleCharacter() {
+ assertEquals("a", reverse("a"));
+ }
+
+ @Test
+ public void reverseHelloIsOlleh() {
+ assertEquals("olleh", reverse("hello"));
+ }
+
+ private String reverse(String str) {
+ return stringReverse.reverse(str);
+ }
+}
diff --git a/examples/string-reverse/src/test/java/com/fnproject/examples/StringReverseTest.java b/examples/string-reverse/src/test/java/com/fnproject/examples/StringReverseTest.java
deleted file mode 100644
index 9f67dd2c..00000000
--- a/examples/string-reverse/src/test/java/com/fnproject/examples/StringReverseTest.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.fnproject.examples;
-
-import com.fnproject.fn.examples.StringReverse;
-import org.junit.Test;
-
-import static junit.framework.TestCase.assertEquals;
-
-public class StringReverseTest {
- private StringReverse stringReverse = new StringReverse();
-
- @Test
- public void reverseEmptyString() {
- assertEquals("", reverse(""));
- }
-
- @Test
- public void reverseOfSingleCharacter() {
- assertEquals("a", reverse("a"));
- }
-
- @Test
- public void reverseHelloIsOlleh() {
- assertEquals("olleh", reverse("hello"));
- }
-
- private String reverse(String str) {
- return stringReverse.reverse(str);
- }
-}
diff --git a/experimental-native-image-support/README.md b/experimental-native-image-support/README.md
new file mode 100644
index 00000000..e7200e08
--- /dev/null
+++ b/experimental-native-image-support/README.md
@@ -0,0 +1,50 @@
+# Experimental support tools for functions native images
+
+This is an optional module that can be added to native-image builds that resolves some common issues related to
+native-image handling in oracle functions.
+
+This library is _experimental_ - it may change in behaviour and may not work in many cases.
+
+Currently, this contains graal native-image configuration files which enable common use cases for :
+
+* Oracle Cloud Infrastructure java client support in native images
+* Graal native reflection config for general Jersey client support (specifically tested for the OCI client use case, may work in other cases)
+* Graal native reflection config for BouncyCastle crypto in native images
+
+It also includes :
+
+* A dynamic graal-native feature which automatically adds classes referenced in Jackson-databind annotations - this removes the need to have to manually add model classes that are referenced via Jackson annotations like `@JsonSubTypes.Type` `@JsonDeserlize` etc.
+
+# Enabling the feature in a native build:
+
+Generate a native build function (replace 1.0.121 with the appropriate fdk-java version):
+
+```
+fn init --init-image fnproject/fn-java-native-init:jdk11-1.0.121 graalfn
+```
+
+Edit your pom file and add this library as a dependency:
+
+```xml
+
+ com.fnproject.fn
+ experimental-native-image-support
+ ${fdk.version}
+ runtime
+
+```
+
+Edit the generated `Dockerfile` and add the following to enable the feature:
+
+```
+ --features=com.fnproject.fn.nativeimagesupport.JacksonFeature \
+```
+You may also need to add the following flags if they are not already set:
+```
+ --allow-incomplete-classpath \
+ --enable-all-security-services \
+ --enable-url-protocols=https \
+ --report-unsupported-elements-at-runtime \
+```
+
+You may see "WARNING:..." messages during the build, these are expected and should not cause issues.
diff --git a/experimental-native-image-support/pom.xml b/experimental-native-image-support/pom.xml
new file mode 100644
index 00000000..31fe7592
--- /dev/null
+++ b/experimental-native-image-support/pom.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+ fdk
+ com.fnproject.fn
+ 1.0.0-SNAPSHOT
+
+ 4.0.0
+ experimental-native-image-support
+ experimental-native-image-support
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+
+ junit
+ junit
+ test
+
+
+ org.graalvm.nativeimage
+ graal-hotspot-library
+ ${graalvm.version}
+ provided
+ true
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+
+
+
diff --git a/experimental-native-image-support/src/main/java/com/fnproject/fn/nativeimagesupport/JacksonFeature.java b/experimental-native-image-support/src/main/java/com/fnproject/fn/nativeimagesupport/JacksonFeature.java
new file mode 100644
index 00000000..a5d7e6d1
--- /dev/null
+++ b/experimental-native-image-support/src/main/java/com/fnproject/fn/nativeimagesupport/JacksonFeature.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2019, 2020, 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.nativeimagesupport;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import com.fasterxml.jackson.annotation.JacksonAnnotation;
+import com.oracle.svm.reflect.hosted.ReflectionFeature;
+import org.graalvm.nativeimage.ImageSingletons;
+import org.graalvm.nativeimage.hosted.Feature;
+import org.graalvm.nativeimage.impl.RuntimeReflectionSupport;
+
+/**
+ * This is a graal native-image feature that automatically includes any classes referenced as literals in Jackson Annotations
+ * from included classes.
+ *
+ * The process assumes that the following classes require full reflective acceess (all fields/methods/constructors) :
+ *
+ *
Classes with Jackson annotations (any jackson annotation on the class or in a member)
+ *
Any Class literals referenced from jackson annotations or their descendents (e.g. Serializers, dispatch types)
+ * (c) 2021 Oracle Corporation
+ */
+public class JacksonFeatureTest {
+
+
+ @Test
+ public void shouldWalkAnnotations() {
+ assertThat(JacksonFeature.expandClassesToMarkForReflection(AbstractBase.class)).containsExactly(AbstractBase.class,ConcreteSubType.class);
+ assertThat(JacksonFeature.expandClassesToMarkForReflection(InterfaceBase.class)).containsExactly(InterfaceImpl.class);
+ assertThat(JacksonFeature.expandClassesToMarkForReflection(UsesJacksonFeatures.class)).containsExactly(UsesJacksonFeatures.class,UsesJacksonFeatures.Builder.class);
+ assertThat(JacksonFeature.expandClassesToMarkForReflection(AnnotationsOnFields.class)).containsExactly(AnnotationsOnFields.class,BigInteger.class);
+ assertThat(JacksonFeature.expandClassesToMarkForReflection(AnnotationsOnMethods.class)).containsExactly(AnnotationsOnMethods.class,BigInteger.class);
+ }
+
+
+
+ @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
+ @JsonSubTypes({
+ @JsonSubTypes.Type(value = ConcreteSubType.class, name = "c1")
+ })
+ public abstract static class AbstractBase {
+
+ }
+
+ public static class ConcreteSubType extends AbstractBase {
+ int id;
+ }
+
+ public static class InterfaceImpl implements InterfaceBase {
+ }
+
+ /**
+ * Created on 15/02/2021.
+ *
+ * (c) 2021 Oracle Corporation
+ */
+ @JsonDeserialize(as = InterfaceImpl.class)
+ public interface InterfaceBase {
+ }
+
+
+ @JsonDeserialize(builder = UsesJacksonFeatures.Builder.class)
+ public static class UsesJacksonFeatures {
+ @JsonPOJOBuilder
+ public static class Builder {
+ }
+ }
+
+ public static class AnnotationsOnFields {
+ @JsonSerialize(as = BigInteger.class)
+ private int number;
+ }
+
+
+ public static class AnnotationsOnMethods {
+ @JsonSerialize(as = BigInteger.class)
+ public int getNumber() {
+ return 1;
+ }
+ }
+}
diff --git a/flow-api/pom.xml b/flow-api/pom.xml
new file mode 100644
index 00000000..81e25e32
--- /dev/null
+++ b/flow-api/pom.xml
@@ -0,0 +1,79 @@
+
+
+
+
+
+ fdk
+ com.fnproject.fn
+ 1.0.0-SNAPSHOT
+
+ 4.0.0
+ flow-api
+ flow-api
+
+
+ com.fnproject.fn
+ api
+
+
+ junit
+ junit
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+ org.netbeans.tools
+ sigtest-maven-plugin
+
+
+
+ check
+
+
+
+
+ src/main/api/snapshot.sigfile
+ strictcheck
+ com.fnproject.fn.api.flow
+
+
+
+
+
diff --git a/flow-api/src/main/api/snapshot.sigfile b/flow-api/src/main/api/snapshot.sigfile
new file mode 100644
index 00000000..9048991c
--- /dev/null
+++ b/flow-api/src/main/api/snapshot.sigfile
@@ -0,0 +1,343 @@
+#Signature file v4.1
+#Version 1.0.0-SNAPSHOT
+
+CLSS public abstract interface com.fnproject.fn.api.flow.Flow
+innr public final static !enum FlowState
+intf java.io.Serializable
+meth public <%0 extends java.io.Serializable, %1 extends java.lang.Object> com.fnproject.fn.api.flow.FlowFuture<{%%0}> invokeFunction(java.lang.String,{%%1},java.lang.Class<{%%0}>)
+meth public <%0 extends java.lang.Object> com.fnproject.fn.api.flow.FlowFuture invokeFunction(java.lang.String,{%%0})
+meth public abstract !varargs com.fnproject.fn.api.flow.FlowFuture anyOf(com.fnproject.fn.api.flow.FlowFuture>[])
+meth public abstract !varargs com.fnproject.fn.api.flow.FlowFuture allOf(com.fnproject.fn.api.flow.FlowFuture>[])
+meth public abstract <%0 extends java.io.Serializable, %1 extends java.lang.Object> com.fnproject.fn.api.flow.FlowFuture<{%%0}> invokeFunction(java.lang.String,com.fnproject.fn.api.flow.HttpMethod,com.fnproject.fn.api.Headers,{%%1},java.lang.Class<{%%0}>)
+meth public abstract <%0 extends java.lang.Object> com.fnproject.fn.api.flow.FlowFuture invokeFunction(java.lang.String,com.fnproject.fn.api.flow.HttpMethod,com.fnproject.fn.api.Headers,{%%0})
+meth public abstract <%0 extends java.lang.Object> com.fnproject.fn.api.flow.FlowFuture<{%%0}> completedValue({%%0})
+meth public abstract <%0 extends java.lang.Object> com.fnproject.fn.api.flow.FlowFuture<{%%0}> createFlowFuture()
+meth public abstract <%0 extends java.lang.Object> com.fnproject.fn.api.flow.FlowFuture<{%%0}> failedFuture(java.lang.Throwable)
+meth public abstract <%0 extends java.lang.Object> com.fnproject.fn.api.flow.FlowFuture<{%%0}> supply(com.fnproject.fn.api.flow.Flows$SerCallable<{%%0}>)
+meth public abstract com.fnproject.fn.api.flow.Flow addTerminationHook(com.fnproject.fn.api.flow.Flows$SerConsumer)
+meth public abstract com.fnproject.fn.api.flow.FlowFuture invokeFunction(java.lang.String,com.fnproject.fn.api.flow.HttpMethod,com.fnproject.fn.api.Headers,byte[])
+meth public abstract com.fnproject.fn.api.flow.FlowFuture delay(long,java.util.concurrent.TimeUnit)
+meth public abstract com.fnproject.fn.api.flow.FlowFuture supply(com.fnproject.fn.api.flow.Flows$SerRunnable)
+meth public com.fnproject.fn.api.flow.FlowFuture invokeFunction(java.lang.String,com.fnproject.fn.api.flow.HttpMethod)
+meth public com.fnproject.fn.api.flow.FlowFuture invokeFunction(java.lang.String,com.fnproject.fn.api.flow.HttpMethod,com.fnproject.fn.api.Headers)
+
+CLSS public final static !enum com.fnproject.fn.api.flow.Flow$FlowState
+ outer com.fnproject.fn.api.flow.Flow
+fld public final static com.fnproject.fn.api.flow.Flow$FlowState CANCELLED
+fld public final static com.fnproject.fn.api.flow.Flow$FlowState FAILED
+fld public final static com.fnproject.fn.api.flow.Flow$FlowState KILLED
+fld public final static com.fnproject.fn.api.flow.Flow$FlowState SUCCEEDED
+fld public final static com.fnproject.fn.api.flow.Flow$FlowState UNKNOWN
+meth public static com.fnproject.fn.api.flow.Flow$FlowState valueOf(java.lang.String)
+meth public static com.fnproject.fn.api.flow.Flow$FlowState[] values()
+supr java.lang.Enum
+
+CLSS public com.fnproject.fn.api.flow.FlowCompletionException
+cons public init(java.lang.String)
+cons public init(java.lang.String,java.lang.Throwable)
+cons public init(java.lang.Throwable)
+supr java.lang.RuntimeException
+
+CLSS public abstract interface com.fnproject.fn.api.flow.FlowFuture<%0 extends java.lang.Object>
+intf java.io.Serializable
+meth public abstract <%0 extends java.lang.Object, %1 extends java.lang.Object> com.fnproject.fn.api.flow.FlowFuture<{%%1}> thenCombine(com.fnproject.fn.api.flow.FlowFuture extends {%%0}>,com.fnproject.fn.api.flow.Flows$SerBiFunction super {com.fnproject.fn.api.flow.FlowFuture%0},? super {%%0},? extends {%%1}>)
+meth public abstract <%0 extends java.lang.Object> com.fnproject.fn.api.flow.FlowFuture thenAcceptBoth(com.fnproject.fn.api.flow.FlowFuture<{%%0}>,com.fnproject.fn.api.flow.Flows$SerBiConsumer<{com.fnproject.fn.api.flow.FlowFuture%0},{%%0}>)
+meth public abstract <%0 extends java.lang.Object> com.fnproject.fn.api.flow.FlowFuture<{%%0}> applyToEither(com.fnproject.fn.api.flow.FlowFuture extends {com.fnproject.fn.api.flow.FlowFuture%0}>,com.fnproject.fn.api.flow.Flows$SerFunction<{com.fnproject.fn.api.flow.FlowFuture%0},{%%0}>)
+meth public abstract <%0 extends java.lang.Object> com.fnproject.fn.api.flow.FlowFuture<{%%0}> handle(com.fnproject.fn.api.flow.Flows$SerBiFunction super {com.fnproject.fn.api.flow.FlowFuture%0},java.lang.Throwable,? extends {%%0}>)
+meth public abstract <%0 extends java.lang.Object> com.fnproject.fn.api.flow.FlowFuture<{%%0}> thenApply(com.fnproject.fn.api.flow.Flows$SerFunction<{com.fnproject.fn.api.flow.FlowFuture%0},{%%0}>)
+meth public abstract <%0 extends java.lang.Object> com.fnproject.fn.api.flow.FlowFuture<{%%0}> thenCompose(com.fnproject.fn.api.flow.Flows$SerFunction<{com.fnproject.fn.api.flow.FlowFuture%0},com.fnproject.fn.api.flow.FlowFuture<{%%0}>>)
+meth public abstract boolean cancel()
+meth public abstract boolean complete({com.fnproject.fn.api.flow.FlowFuture%0})
+meth public abstract boolean completeExceptionally(java.lang.Throwable)
+meth public abstract com.fnproject.fn.api.flow.FlowFuture acceptEither(com.fnproject.fn.api.flow.FlowFuture extends {com.fnproject.fn.api.flow.FlowFuture%0}>,com.fnproject.fn.api.flow.Flows$SerConsumer<{com.fnproject.fn.api.flow.FlowFuture%0}>)
+meth public abstract com.fnproject.fn.api.flow.FlowFuture thenAccept(com.fnproject.fn.api.flow.Flows$SerConsumer<{com.fnproject.fn.api.flow.FlowFuture%0}>)
+meth public abstract com.fnproject.fn.api.flow.FlowFuture thenRun(com.fnproject.fn.api.flow.Flows$SerRunnable)
+meth public abstract com.fnproject.fn.api.flow.FlowFuture<{com.fnproject.fn.api.flow.FlowFuture%0}> exceptionally(com.fnproject.fn.api.flow.Flows$SerFunction)
+meth public abstract com.fnproject.fn.api.flow.FlowFuture<{com.fnproject.fn.api.flow.FlowFuture%0}> exceptionallyCompose(com.fnproject.fn.api.flow.Flows$SerFunction>)
+meth public abstract com.fnproject.fn.api.flow.FlowFuture<{com.fnproject.fn.api.flow.FlowFuture%0}> whenComplete(com.fnproject.fn.api.flow.Flows$SerBiConsumer<{com.fnproject.fn.api.flow.FlowFuture%0},java.lang.Throwable>)
+meth public abstract {com.fnproject.fn.api.flow.FlowFuture%0} get()
+meth public abstract {com.fnproject.fn.api.flow.FlowFuture%0} get(long,java.util.concurrent.TimeUnit) throws java.util.concurrent.TimeoutException
+meth public abstract {com.fnproject.fn.api.flow.FlowFuture%0} getNow({com.fnproject.fn.api.flow.FlowFuture%0})
+
+CLSS public final com.fnproject.fn.api.flow.Flows
+innr public abstract interface static FlowSource
+innr public abstract interface static SerBiConsumer
+innr public abstract interface static SerBiFunction
+innr public abstract interface static SerCallable
+innr public abstract interface static SerConsumer
+innr public abstract interface static SerFunction
+innr public abstract interface static SerRunnable
+innr public abstract interface static SerSupplier
+meth public static com.fnproject.fn.api.flow.Flow currentFlow()
+meth public static com.fnproject.fn.api.flow.Flows$FlowSource getCurrentFlowSource()
+meth public static void setCurrentFlowSource(com.fnproject.fn.api.flow.Flows$FlowSource)
+supr java.lang.Object
+hfds flowSource
+
+CLSS public abstract interface static com.fnproject.fn.api.flow.Flows$FlowSource
+ outer com.fnproject.fn.api.flow.Flows
+meth public abstract com.fnproject.fn.api.flow.Flow currentFlow()
+
+CLSS public abstract interface static com.fnproject.fn.api.flow.Flows$SerBiConsumer<%0 extends java.lang.Object, %1 extends java.lang.Object>
+ outer com.fnproject.fn.api.flow.Flows
+ anno 0 java.lang.FunctionalInterface()
+intf java.io.Serializable
+intf java.util.function.BiConsumer<{com.fnproject.fn.api.flow.Flows$SerBiConsumer%0},{com.fnproject.fn.api.flow.Flows$SerBiConsumer%1}>
+
+CLSS public abstract interface static com.fnproject.fn.api.flow.Flows$SerBiFunction<%0 extends java.lang.Object, %1 extends java.lang.Object, %2 extends java.lang.Object>
+ outer com.fnproject.fn.api.flow.Flows
+ anno 0 java.lang.FunctionalInterface()
+intf java.io.Serializable
+intf java.util.function.BiFunction<{com.fnproject.fn.api.flow.Flows$SerBiFunction%0},{com.fnproject.fn.api.flow.Flows$SerBiFunction%1},{com.fnproject.fn.api.flow.Flows$SerBiFunction%2}>
+
+CLSS public abstract interface static com.fnproject.fn.api.flow.Flows$SerCallable<%0 extends java.lang.Object>
+ outer com.fnproject.fn.api.flow.Flows
+ anno 0 java.lang.FunctionalInterface()
+intf java.io.Serializable
+intf java.util.concurrent.Callable<{com.fnproject.fn.api.flow.Flows$SerCallable%0}>
+
+CLSS public abstract interface static com.fnproject.fn.api.flow.Flows$SerConsumer<%0 extends java.lang.Object>
+ outer com.fnproject.fn.api.flow.Flows
+ anno 0 java.lang.FunctionalInterface()
+intf java.io.Serializable
+intf java.util.function.Consumer<{com.fnproject.fn.api.flow.Flows$SerConsumer%0}>
+
+CLSS public abstract interface static com.fnproject.fn.api.flow.Flows$SerFunction<%0 extends java.lang.Object, %1 extends java.lang.Object>
+ outer com.fnproject.fn.api.flow.Flows
+ anno 0 java.lang.FunctionalInterface()
+intf java.io.Serializable
+intf java.util.function.Function<{com.fnproject.fn.api.flow.Flows$SerFunction%0},{com.fnproject.fn.api.flow.Flows$SerFunction%1}>
+
+CLSS public abstract interface static com.fnproject.fn.api.flow.Flows$SerRunnable
+ outer com.fnproject.fn.api.flow.Flows
+ anno 0 java.lang.FunctionalInterface()
+intf java.io.Serializable
+intf java.lang.Runnable
+
+CLSS public abstract interface static com.fnproject.fn.api.flow.Flows$SerSupplier<%0 extends java.lang.Object>
+ outer com.fnproject.fn.api.flow.Flows
+ anno 0 java.lang.FunctionalInterface()
+intf java.io.Serializable
+intf java.util.function.Supplier<{com.fnproject.fn.api.flow.Flows$SerSupplier%0}>
+
+CLSS public com.fnproject.fn.api.flow.FunctionInvocationException
+cons public init(com.fnproject.fn.api.flow.HttpResponse)
+meth public com.fnproject.fn.api.flow.HttpResponse getFunctionResponse()
+supr java.lang.RuntimeException
+hfds functionResponse
+
+CLSS public com.fnproject.fn.api.flow.FunctionInvokeFailedException
+cons public init(java.lang.String)
+supr com.fnproject.fn.api.flow.PlatformException
+
+CLSS public com.fnproject.fn.api.flow.FunctionTimeoutException
+cons public init(java.lang.String)
+supr com.fnproject.fn.api.flow.PlatformException
+
+CLSS public final !enum com.fnproject.fn.api.flow.HttpMethod
+fld public final static com.fnproject.fn.api.flow.HttpMethod DELETE
+fld public final static com.fnproject.fn.api.flow.HttpMethod GET
+fld public final static com.fnproject.fn.api.flow.HttpMethod HEAD
+fld public final static com.fnproject.fn.api.flow.HttpMethod OPTIONS
+fld public final static com.fnproject.fn.api.flow.HttpMethod PATCH
+fld public final static com.fnproject.fn.api.flow.HttpMethod POST
+fld public final static com.fnproject.fn.api.flow.HttpMethod PUT
+meth public java.lang.String toString()
+meth public static com.fnproject.fn.api.flow.HttpMethod valueOf(java.lang.String)
+meth public static com.fnproject.fn.api.flow.HttpMethod[] values()
+supr java.lang.Enum
+hfds verb
+
+CLSS public abstract interface com.fnproject.fn.api.flow.HttpRequest
+meth public abstract byte[] getBodyAsBytes()
+meth public abstract com.fnproject.fn.api.Headers getHeaders()
+meth public abstract com.fnproject.fn.api.flow.HttpMethod getMethod()
+
+CLSS public abstract interface com.fnproject.fn.api.flow.HttpResponse
+meth public abstract byte[] getBodyAsBytes()
+meth public abstract com.fnproject.fn.api.Headers getHeaders()
+meth public abstract int getStatusCode()
+
+CLSS public com.fnproject.fn.api.flow.InvalidStageResponseException
+cons public init(java.lang.String)
+supr com.fnproject.fn.api.flow.PlatformException
+
+CLSS public com.fnproject.fn.api.flow.LambdaSerializationException
+cons public init(java.lang.String)
+cons public init(java.lang.String,java.lang.Exception)
+supr com.fnproject.fn.api.flow.FlowCompletionException
+
+CLSS public com.fnproject.fn.api.flow.PlatformException
+cons public init(java.lang.String)
+cons public init(java.lang.String,java.lang.Throwable)
+cons public init(java.lang.Throwable)
+meth public java.lang.Throwable fillInStackTrace()
+supr com.fnproject.fn.api.flow.FlowCompletionException
+
+CLSS public com.fnproject.fn.api.flow.ResultSerializationException
+cons public init(java.lang.String,java.lang.Throwable)
+supr com.fnproject.fn.api.flow.FlowCompletionException
+
+CLSS public com.fnproject.fn.api.flow.StageInvokeFailedException
+cons public init(java.lang.String)
+supr com.fnproject.fn.api.flow.PlatformException
+
+CLSS public com.fnproject.fn.api.flow.StageLostException
+cons public init(java.lang.String)
+supr com.fnproject.fn.api.flow.PlatformException
+
+CLSS public com.fnproject.fn.api.flow.StageTimeoutException
+cons public init(java.lang.String)
+supr com.fnproject.fn.api.flow.PlatformException
+
+CLSS public final com.fnproject.fn.api.flow.WrappedFunctionException
+cons public init(java.lang.Throwable)
+intf java.io.Serializable
+meth public java.lang.Class> getOriginalExceptionType()
+supr java.lang.RuntimeException
+hfds originalExceptionType
+
+CLSS public abstract interface java.io.Serializable
+
+CLSS public abstract interface java.lang.Comparable<%0 extends java.lang.Object>
+meth public abstract int compareTo({java.lang.Comparable%0})
+
+CLSS public abstract java.lang.Enum<%0 extends java.lang.Enum<{java.lang.Enum%0}>>
+cons protected init(java.lang.String,int)
+intf java.io.Serializable
+intf java.lang.Comparable<{java.lang.Enum%0}>
+meth protected final java.lang.Object clone() throws java.lang.CloneNotSupportedException
+meth protected final void finalize()
+meth public final boolean equals(java.lang.Object)
+meth public final int compareTo({java.lang.Enum%0})
+meth public final int hashCode()
+meth public final int ordinal()
+meth public final java.lang.Class<{java.lang.Enum%0}> getDeclaringClass()
+meth public final java.lang.String name()
+meth public java.lang.String toString()
+meth public static <%0 extends java.lang.Enum<{%%0}>> {%%0} valueOf(java.lang.Class<{%%0}>,java.lang.String)
+supr java.lang.Object
+hfds name,ordinal
+
+CLSS public java.lang.Exception
+cons protected init(java.lang.String,java.lang.Throwable,boolean,boolean)
+cons public init()
+cons public init(java.lang.String)
+cons public init(java.lang.String,java.lang.Throwable)
+cons public init(java.lang.Throwable)
+supr java.lang.Throwable
+hfds serialVersionUID
+
+CLSS public abstract interface !annotation java.lang.FunctionalInterface
+ anno 0 java.lang.annotation.Documented()
+ anno 0 java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy value=RUNTIME)
+ anno 0 java.lang.annotation.Target(java.lang.annotation.ElementType[] value=[TYPE])
+intf java.lang.annotation.Annotation
+
+CLSS public java.lang.Object
+cons public init()
+meth protected java.lang.Object clone() throws java.lang.CloneNotSupportedException
+meth protected void finalize() throws java.lang.Throwable
+meth public boolean equals(java.lang.Object)
+meth public final java.lang.Class> getClass()
+meth public final void notify()
+meth public final void notifyAll()
+meth public final void wait() throws java.lang.InterruptedException
+meth public final void wait(long) throws java.lang.InterruptedException
+meth public final void wait(long,int) throws java.lang.InterruptedException
+meth public int hashCode()
+meth public java.lang.String toString()
+
+CLSS public abstract interface java.lang.Runnable
+ anno 0 java.lang.FunctionalInterface()
+meth public abstract void run()
+
+CLSS public java.lang.RuntimeException
+cons protected init(java.lang.String,java.lang.Throwable,boolean,boolean)
+cons public init()
+cons public init(java.lang.String)
+cons public init(java.lang.String,java.lang.Throwable)
+cons public init(java.lang.Throwable)
+supr java.lang.Exception
+hfds serialVersionUID
+
+CLSS public java.lang.Throwable
+cons protected init(java.lang.String,java.lang.Throwable,boolean,boolean)
+cons public init()
+cons public init(java.lang.String)
+cons public init(java.lang.String,java.lang.Throwable)
+cons public init(java.lang.Throwable)
+intf java.io.Serializable
+meth public final java.lang.Throwable[] getSuppressed()
+meth public final void addSuppressed(java.lang.Throwable)
+meth public java.lang.StackTraceElement[] getStackTrace()
+meth public java.lang.String getLocalizedMessage()
+meth public java.lang.String getMessage()
+meth public java.lang.String toString()
+meth public java.lang.Throwable fillInStackTrace()
+meth public java.lang.Throwable getCause()
+meth public java.lang.Throwable initCause(java.lang.Throwable)
+meth public void printStackTrace()
+meth public void printStackTrace(java.io.PrintStream)
+meth public void printStackTrace(java.io.PrintWriter)
+meth public void setStackTrace(java.lang.StackTraceElement[])
+supr java.lang.Object
+hfds CAUSE_CAPTION,EMPTY_THROWABLE_ARRAY,NULL_CAUSE_MESSAGE,SELF_SUPPRESSION_MESSAGE,SUPPRESSED_CAPTION,SUPPRESSED_SENTINEL,UNASSIGNED_STACK,backtrace,cause,detailMessage,serialVersionUID,stackTrace,suppressedExceptions
+hcls PrintStreamOrWriter,SentinelHolder,WrappedPrintStream,WrappedPrintWriter
+
+CLSS public abstract interface java.lang.annotation.Annotation
+meth public abstract boolean equals(java.lang.Object)
+meth public abstract int hashCode()
+meth public abstract java.lang.Class extends java.lang.annotation.Annotation> annotationType()
+meth public abstract java.lang.String toString()
+
+CLSS public abstract interface !annotation java.lang.annotation.Documented
+ anno 0 java.lang.annotation.Documented()
+ anno 0 java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy value=RUNTIME)
+ anno 0 java.lang.annotation.Target(java.lang.annotation.ElementType[] value=[ANNOTATION_TYPE])
+intf java.lang.annotation.Annotation
+
+CLSS public abstract interface !annotation java.lang.annotation.Retention
+ anno 0 java.lang.annotation.Documented()
+ anno 0 java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy value=RUNTIME)
+ anno 0 java.lang.annotation.Target(java.lang.annotation.ElementType[] value=[ANNOTATION_TYPE])
+intf java.lang.annotation.Annotation
+meth public abstract java.lang.annotation.RetentionPolicy value()
+
+CLSS public abstract interface !annotation java.lang.annotation.Target
+ anno 0 java.lang.annotation.Documented()
+ anno 0 java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy value=RUNTIME)
+ anno 0 java.lang.annotation.Target(java.lang.annotation.ElementType[] value=[ANNOTATION_TYPE])
+intf java.lang.annotation.Annotation
+meth public abstract java.lang.annotation.ElementType[] value()
+
+CLSS public abstract interface java.util.concurrent.Callable<%0 extends java.lang.Object>
+ anno 0 java.lang.FunctionalInterface()
+meth public abstract {java.util.concurrent.Callable%0} call() throws java.lang.Exception
+
+CLSS public abstract interface java.util.function.BiConsumer<%0 extends java.lang.Object, %1 extends java.lang.Object>
+ anno 0 java.lang.FunctionalInterface()
+meth public abstract void accept({java.util.function.BiConsumer%0},{java.util.function.BiConsumer%1})
+meth public java.util.function.BiConsumer<{java.util.function.BiConsumer%0},{java.util.function.BiConsumer%1}> andThen(java.util.function.BiConsumer super {java.util.function.BiConsumer%0},? super {java.util.function.BiConsumer%1}>)
+
+CLSS public abstract interface java.util.function.BiFunction<%0 extends java.lang.Object, %1 extends java.lang.Object, %2 extends java.lang.Object>
+ anno 0 java.lang.FunctionalInterface()
+meth public <%0 extends java.lang.Object> java.util.function.BiFunction<{java.util.function.BiFunction%0},{java.util.function.BiFunction%1},{%%0}> andThen(java.util.function.Function super {java.util.function.BiFunction%2},? extends {%%0}>)
+meth public abstract {java.util.function.BiFunction%2} apply({java.util.function.BiFunction%0},{java.util.function.BiFunction%1})
+
+CLSS public abstract interface java.util.function.Consumer<%0 extends java.lang.Object>
+ anno 0 java.lang.FunctionalInterface()
+meth public abstract void accept({java.util.function.Consumer%0})
+meth public java.util.function.Consumer<{java.util.function.Consumer%0}> andThen(java.util.function.Consumer super {java.util.function.Consumer%0}>)
+
+CLSS public abstract interface java.util.function.Function<%0 extends java.lang.Object, %1 extends java.lang.Object>
+ anno 0 java.lang.FunctionalInterface()
+meth public <%0 extends java.lang.Object> java.util.function.Function<{%%0},{java.util.function.Function%1}> compose(java.util.function.Function super {%%0},? extends {java.util.function.Function%0}>)
+meth public <%0 extends java.lang.Object> java.util.function.Function<{java.util.function.Function%0},{%%0}> andThen(java.util.function.Function super {java.util.function.Function%1},? extends {%%0}>)
+meth public abstract {java.util.function.Function%1} apply({java.util.function.Function%0})
+meth public static <%0 extends java.lang.Object> java.util.function.Function<{%%0},{%%0}> identity()
+
+CLSS public abstract interface java.util.function.Supplier<%0 extends java.lang.Object>
+ anno 0 java.lang.FunctionalInterface()
+meth public abstract {java.util.function.Supplier%0} get()
+
diff --git a/api/src/main/java/com/fnproject/fn/api/flow/Flow.java b/flow-api/src/main/java/com/fnproject/fn/api/flow/Flow.java
similarity index 92%
rename from api/src/main/java/com/fnproject/fn/api/flow/Flow.java
rename to flow-api/src/main/java/com/fnproject/fn/api/flow/Flow.java
index 22ab2ed8..11cfc9f2 100644
--- a/api/src/main/java/com/fnproject/fn/api/flow/Flow.java
+++ b/flow-api/src/main/java/com/fnproject/fn/api/flow/Flow.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api.flow;
import com.fnproject.fn.api.Headers;
@@ -33,7 +49,7 @@ public interface Flow extends Serializable {
*
* Function IDs should be of the form "APPID/path/in/app" (without leading slash) where APPID may either be a named application or ".", indicating the appID of the current (calling) function.
*
- * @param functionId Function ID of function to invoke - this should have the form APPNAME/FUNCTION_PATH (e.g. "myapp/path/to/function" or "./path/to/function").
+ * @param functionId Function ID of function to invoke - this should be the function ID returned by `fn inspect function appName fnName`
* @param method HTTP method to invoke function
* @param headers Headers to add to the HTTP request representing the function invocation
* @param data input data to function as a byte array -
@@ -45,7 +61,7 @@ public interface Flow extends Serializable {
* Invoke a function by ID with headers and an empty body
*
*
- * @param functionId Function ID of function to invoke - this should have the form APPNAME/FUNCTION_PATH (e.g. "myapp/path/to/function" or "./path/to/function").
+ * @param functionId Function ID of function to invoke - this should be the function ID returned by `fn inspect function appName fnName`
* @param method HTTP method to invoke function
* @param headers Headers to add to the HTTP request representing the function invocation
* @return a future which completes normally if the function succeeded and fails if it fails
@@ -60,7 +76,7 @@ default FlowFuture invokeFunction(String functionId, HttpMethod me
*
* This currently only maps to JSON via the default JSON mapper in the FDK
*
- * @param functionId Function ID of function to invoke - this should have the form APPNAME/FUNCTION_PATH (e.g. "myapp/path/to/function" or "./path/to/function").
+ * @param functionId Function ID of function to invoke - this should be the function ID returned by `fn inspect function appName fnName`
* @param input The input object to send to the function input
* @param responseType The expected response type of the target function
* @param The Response type
@@ -77,7 +93,7 @@ default FlowFuture invokeFunction(String function
*
* This currently only maps to JSON via the default JSON mapper in the FDK
*
- * @param functionId Function ID of function to invoke - this should have the form APPNAME/FUNCTION_PATH (e.g. "myapp/path/to/function" or "./path/to/function").
+ * @param functionId Function ID of function to invoke - this should be the function ID returned by `fn inspect function appName fnName`
* @param method the HTTP method to use for this call
* @param headers additional HTTP headers to pass to this function -
* @param input The input object to send to the function input
@@ -98,7 +114,7 @@ default FlowFuture invokeFunction(String function
*
* This currently only maps to JSON via the default JSON mapper in the FDK
*
- * @param functionId Function ID of function to invoke - this should have the form APPNAME/FUNCTION_PATH (e.g. "myapp/path/to/function" or "./path/to/function").
+ * @param functionId Function ID of function to invoke - this should be the function ID returned by `fn inspect function appName fnName`
* @param input The input object to send to the function input
* @param The Input type of the function
* @return a flow future that completes with the result of the function, or an error if the function invocation failed
@@ -116,7 +132,7 @@ default FlowFuture invokeFunction(String functionId, U input)
*
* This currently only maps to JSON via the default JSON mapper in the FDK
*
- * @param functionId Function ID of function to invoke - this should have the form APPNAME/FUNCTION_PATH (e.g. "myapp/path/to/function" or "./path/to/function").
+ * @param functionId Function ID of function to invoke - this should be the function ID returned by `fn inspect function appName fnName`
* @param method the HTTP method to use for this call
* @param headers additional HTTP headers to pass to this function -
* @param input The input object to send to the function input
@@ -130,7 +146,7 @@ default FlowFuture invokeFunction(String functionId, U input)
* Invoke a function by ID with no headers
*
*
- * @param functionId Function ID of function to invoke - this should have the form APPNAME/FUNCTION_PATH (e.g. "myapp/path/to/function" or "./path/to/function").
+ * @param functionId Function ID of function to invoke - this should be the function ID returned by `fn inspect function appName fnName`
* @param method HTTP method to invoke function
* @return a future which completes normally if the function succeeded and fails if it fails
* @see #invokeFunction(String, HttpMethod, Headers, byte[])
diff --git a/api/src/main/java/com/fnproject/fn/api/flow/FlowCompletionException.java b/flow-api/src/main/java/com/fnproject/fn/api/flow/FlowCompletionException.java
similarity index 52%
rename from api/src/main/java/com/fnproject/fn/api/flow/FlowCompletionException.java
rename to flow-api/src/main/java/com/fnproject/fn/api/flow/FlowCompletionException.java
index 3f3e017c..051a0f32 100644
--- a/api/src/main/java/com/fnproject/fn/api/flow/FlowCompletionException.java
+++ b/flow-api/src/main/java/com/fnproject/fn/api/flow/FlowCompletionException.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api.flow;
diff --git a/api/src/main/java/com/fnproject/fn/api/flow/FlowFuture.java b/flow-api/src/main/java/com/fnproject/fn/api/flow/FlowFuture.java
similarity index 97%
rename from api/src/main/java/com/fnproject/fn/api/flow/FlowFuture.java
rename to flow-api/src/main/java/com/fnproject/fn/api/flow/FlowFuture.java
index da3d5e60..1bbc36cc 100644
--- a/api/src/main/java/com/fnproject/fn/api/flow/FlowFuture.java
+++ b/flow-api/src/main/java/com/fnproject/fn/api/flow/FlowFuture.java
@@ -1,7 +1,22 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api.flow;
import java.io.Serializable;
-import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
diff --git a/api/src/main/java/com/fnproject/fn/api/flow/Flows.java b/flow-api/src/main/java/com/fnproject/fn/api/flow/Flows.java
similarity index 76%
rename from api/src/main/java/com/fnproject/fn/api/flow/Flows.java
rename to flow-api/src/main/java/com/fnproject/fn/api/flow/Flows.java
index 4df4f51a..14097465 100644
--- a/api/src/main/java/com/fnproject/fn/api/flow/Flows.java
+++ b/flow-api/src/main/java/com/fnproject/fn/api/flow/Flows.java
@@ -1,5 +1,22 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api.flow;
+
import java.io.Serializable;
import java.util.Objects;
import java.util.concurrent.Callable;
@@ -8,7 +25,9 @@
/**
* Fn Flow API entry point class - this provides access to the current {@link Flow} for the current function invocation.
*/
-public class Flows {
+public final class Flows {
+ private Flows() {
+ }
private static FlowSource flowSource;
@@ -17,7 +36,7 @@ public class Flows {
*
* @return the current supplier of the flow runtime
*/
- public static FlowSource getCurrentFlowSource() {
+ public static synchronized FlowSource getCurrentFlowSource() {
return flowSource;
}
@@ -35,7 +54,7 @@ public interface FlowSource {
* @return the current flow runtime
*/
public synchronized static Flow currentFlow() {
- Objects.requireNonNull(flowSource, "Flows.flowSource is not set - Flows.currentFlow() should only be called from within a FaaS function invocation");
+ Objects.requireNonNull(flowSource, "Flows.flowSource is not set - Flows.currentFlow() is the @FnFeature(FlowFeature.class) annotation set on your function?");
return flowSource.currentFlow();
}
diff --git a/api/src/main/java/com/fnproject/fn/api/flow/FunctionInvocationException.java b/flow-api/src/main/java/com/fnproject/fn/api/flow/FunctionInvocationException.java
similarity index 54%
rename from api/src/main/java/com/fnproject/fn/api/flow/FunctionInvocationException.java
rename to flow-api/src/main/java/com/fnproject/fn/api/flow/FunctionInvocationException.java
index b3cc78c5..5c8c1e14 100644
--- a/api/src/main/java/com/fnproject/fn/api/flow/FunctionInvocationException.java
+++ b/flow-api/src/main/java/com/fnproject/fn/api/flow/FunctionInvocationException.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api.flow;
/**
diff --git a/flow-api/src/main/java/com/fnproject/fn/api/flow/FunctionInvokeFailedException.java b/flow-api/src/main/java/com/fnproject/fn/api/flow/FunctionInvokeFailedException.java
new file mode 100644
index 00000000..731fae5d
--- /dev/null
+++ b/flow-api/src/main/java/com/fnproject/fn/api/flow/FunctionInvokeFailedException.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.api.flow;
+
+/**
+ * Exception thrown when a function call failed within the fn platform - the function may or may not have been invoked and
+ * that invocation may or may not have completed.
+ */
+public class FunctionInvokeFailedException extends PlatformException {
+ public FunctionInvokeFailedException(String reason) { super(reason); }
+}
diff --git a/flow-api/src/main/java/com/fnproject/fn/api/flow/FunctionTimeoutException.java b/flow-api/src/main/java/com/fnproject/fn/api/flow/FunctionTimeoutException.java
new file mode 100644
index 00000000..b38317a9
--- /dev/null
+++ b/flow-api/src/main/java/com/fnproject/fn/api/flow/FunctionTimeoutException.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.api.flow;
+
+/**
+ * Exception thrown when a function execution exceeds its configured timeout.
+ *
+ * When this exception is raised the fn server has terminated the container hosting the function.
+ */
+public class FunctionTimeoutException extends PlatformException {
+ public FunctionTimeoutException(String reason) { super(reason); }
+}
diff --git a/flow-api/src/main/java/com/fnproject/fn/api/flow/HttpMethod.java b/flow-api/src/main/java/com/fnproject/fn/api/flow/HttpMethod.java
new file mode 100644
index 00000000..b5109343
--- /dev/null
+++ b/flow-api/src/main/java/com/fnproject/fn/api/flow/HttpMethod.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.api.flow;
+
+/**
+ * Enum representing the different HTTP types that can be used to invoke an external function
+ *
+ * @see Flow#invokeFunction
+ */
+public enum HttpMethod {
+ GET("GET"),
+ HEAD("HEAD"),
+ POST("POST"),
+ PUT("PUT"),
+ DELETE("DELETE"),
+ OPTIONS("OPTIONS"),
+ PATCH("PATCH");
+
+ private final String verb;
+
+ HttpMethod(String verb) {
+ this.verb = verb;
+ }
+
+ @Override
+ public String toString() {
+ return this.verb;
+ }
+}
diff --git a/flow-api/src/main/java/com/fnproject/fn/api/flow/HttpRequest.java b/flow-api/src/main/java/com/fnproject/fn/api/flow/HttpRequest.java
new file mode 100644
index 00000000..c7e4b3f1
--- /dev/null
+++ b/flow-api/src/main/java/com/fnproject/fn/api/flow/HttpRequest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.api.flow;
+
+import com.fnproject.fn.api.Headers;
+
+/**
+ * An abstract HTTP request details (without location)
+ */
+public interface HttpRequest {
+ /**
+ * Return the HTTP method used to supply this value
+ *
+ * @return the HTTP method
+ */
+ HttpMethod getMethod();
+
+ /**
+ * Return the headers on the HTTP request
+ *
+ * @return the headers
+ */
+ Headers getHeaders();
+
+ /**
+ * Returns the body of the request as a byte array
+ *
+ * @return the function request body
+ */
+ byte[] getBodyAsBytes();
+}
diff --git a/api/src/main/java/com/fnproject/fn/api/flow/HttpResponse.java b/flow-api/src/main/java/com/fnproject/fn/api/flow/HttpResponse.java
similarity index 51%
rename from api/src/main/java/com/fnproject/fn/api/flow/HttpResponse.java
rename to flow-api/src/main/java/com/fnproject/fn/api/flow/HttpResponse.java
index 1df7b86f..f994b9ed 100644
--- a/api/src/main/java/com/fnproject/fn/api/flow/HttpResponse.java
+++ b/flow-api/src/main/java/com/fnproject/fn/api/flow/HttpResponse.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api.flow;
import com.fnproject.fn.api.Headers;
diff --git a/flow-api/src/main/java/com/fnproject/fn/api/flow/InvalidStageResponseException.java b/flow-api/src/main/java/com/fnproject/fn/api/flow/InvalidStageResponseException.java
new file mode 100644
index 00000000..852f6c4f
--- /dev/null
+++ b/flow-api/src/main/java/com/fnproject/fn/api/flow/InvalidStageResponseException.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.api.flow;
+
+/**
+ * Exception thrown when a completion stage responds with an incompatible datum type for its corresponding completion
+ * graph stage.
+ */
+public class InvalidStageResponseException extends PlatformException {
+ public InvalidStageResponseException(String reason) { super(reason); }
+}
diff --git a/flow-api/src/main/java/com/fnproject/fn/api/flow/LambdaSerializationException.java b/flow-api/src/main/java/com/fnproject/fn/api/flow/LambdaSerializationException.java
new file mode 100644
index 00000000..6a4af778
--- /dev/null
+++ b/flow-api/src/main/java/com/fnproject/fn/api/flow/LambdaSerializationException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.api.flow;
+
+/**
+ * Exception thrown when a lambda or any referenced objects fail to be serialized.
+ * The cause will typically be a {@link java.io.NotSerializableException} or other {@link java.io.IOException} detailing what could not be serialized
+ */
+public class LambdaSerializationException extends FlowCompletionException {
+ public LambdaSerializationException(String message) {
+ super(message);
+ }
+
+ public LambdaSerializationException(String message, Exception e) {
+ super(message, e);
+ }
+}
diff --git a/api/src/main/java/com/fnproject/fn/api/flow/PlatformException.java b/flow-api/src/main/java/com/fnproject/fn/api/flow/PlatformException.java
similarity index 53%
rename from api/src/main/java/com/fnproject/fn/api/flow/PlatformException.java
rename to flow-api/src/main/java/com/fnproject/fn/api/flow/PlatformException.java
index cb1932e7..9d90277c 100644
--- a/api/src/main/java/com/fnproject/fn/api/flow/PlatformException.java
+++ b/flow-api/src/main/java/com/fnproject/fn/api/flow/PlatformException.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api.flow;
/**
diff --git a/flow-api/src/main/java/com/fnproject/fn/api/flow/ResultSerializationException.java b/flow-api/src/main/java/com/fnproject/fn/api/flow/ResultSerializationException.java
new file mode 100644
index 00000000..2eafb9c9
--- /dev/null
+++ b/flow-api/src/main/java/com/fnproject/fn/api/flow/ResultSerializationException.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.api.flow;
+
+/**
+ * Exception thrown when a result returned by a completion stage fails to be serialized.
+ */
+public class ResultSerializationException extends FlowCompletionException {
+ public ResultSerializationException(String message, Throwable e) {
+ super(message, e);
+ }
+}
diff --git a/flow-api/src/main/java/com/fnproject/fn/api/flow/StageInvokeFailedException.java b/flow-api/src/main/java/com/fnproject/fn/api/flow/StageInvokeFailedException.java
new file mode 100644
index 00000000..9bd5b04c
--- /dev/null
+++ b/flow-api/src/main/java/com/fnproject/fn/api/flow/StageInvokeFailedException.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.api.flow;
+
+/**
+ * Exception thrown when a a completion stage invocation failed within Fn - the stage may or may not have been invoked
+ * and that invocation may or may not have completed.
+ */
+public class StageInvokeFailedException extends PlatformException {
+ public StageInvokeFailedException(String reason) { super(reason); }
+}
diff --git a/flow-api/src/main/java/com/fnproject/fn/api/flow/StageLostException.java b/flow-api/src/main/java/com/fnproject/fn/api/flow/StageLostException.java
new file mode 100644
index 00000000..2e86d650
--- /dev/null
+++ b/flow-api/src/main/java/com/fnproject/fn/api/flow/StageLostException.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.api.flow;
+
+/**
+ * Exception thrown when a stage failed after an internal error in the flow server, the stage may or may not have been
+ * invoked and that invocation may or may not have completed.
+ */
+public class StageLostException extends PlatformException {
+ public StageLostException(String reason) { super(reason); }
+}
diff --git a/flow-api/src/main/java/com/fnproject/fn/api/flow/StageTimeoutException.java b/flow-api/src/main/java/com/fnproject/fn/api/flow/StageTimeoutException.java
new file mode 100644
index 00000000..5f84f7a8
--- /dev/null
+++ b/flow-api/src/main/java/com/fnproject/fn/api/flow/StageTimeoutException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.api.flow;
+
+/**
+ * Exception thrown when a completion stage function execution exceeds it configured timeout -
+ * the stage may or may not have completed normally.
+ *
+ * When this exception is raised the fn server has terminated the container hosting the function.
+ */
+public class StageTimeoutException extends PlatformException {
+ public StageTimeoutException(String reason) { super(reason); }
+}
diff --git a/api/src/main/java/com/fnproject/fn/api/flow/WrappedFunctionException.java b/flow-api/src/main/java/com/fnproject/fn/api/flow/WrappedFunctionException.java
similarity index 57%
rename from api/src/main/java/com/fnproject/fn/api/flow/WrappedFunctionException.java
rename to flow-api/src/main/java/com/fnproject/fn/api/flow/WrappedFunctionException.java
index 065e6ee7..0bd7e7fa 100644
--- a/api/src/main/java/com/fnproject/fn/api/flow/WrappedFunctionException.java
+++ b/flow-api/src/main/java/com/fnproject/fn/api/flow/WrappedFunctionException.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.api.flow;
import java.io.Serializable;
diff --git a/flow-api/src/main/java/com/fnproject/fn/api/flow/package-info.java b/flow-api/src/main/java/com/fnproject/fn/api/flow/package-info.java
new file mode 100644
index 00000000..f4a567b1
--- /dev/null
+++ b/flow-api/src/main/java/com/fnproject/fn/api/flow/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * SDK for creating and running asynchronous processes from within fn for Java.
+ */
+package com.fnproject.fn.api.flow;
diff --git a/flow-api/src/test/java/com/fnproject/fn/api/flow/FlowsTest.java b/flow-api/src/test/java/com/fnproject/fn/api/flow/FlowsTest.java
new file mode 100644
index 00000000..778ccf1c
--- /dev/null
+++ b/flow-api/src/test/java/com/fnproject/fn/api/flow/FlowsTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.api.flow;
+
+import org.junit.Test;
+
+import java.lang.reflect.Modifier;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class FlowsTest {
+ public FlowsTest() {
+ }
+
+ /** People shall not be allowed to create subclasses of {@code Flow}:
+ *
+ */
+ @Test
+ public void dontSubclassFlows() {
+ assertTrue("Flows is final", Modifier.isFinal(Flows.class.getModifiers()));
+ assertEquals("No visible constructors", 0, Flows.class.getConstructors().length);
+ }
+}
diff --git a/flow-runtime/pom.xml b/flow-runtime/pom.xml
new file mode 100644
index 00000000..c6d6681e
--- /dev/null
+++ b/flow-runtime/pom.xml
@@ -0,0 +1,106 @@
+
+
+
+
+
+ fdk
+ com.fnproject.fn
+ 1.0.0-SNAPSHOT
+
+ 4.0.0
+ flow-runtime
+ flow-runtime
+
+
+
+ com.fnproject.fn
+ api
+
+
+ com.fnproject.fn
+ flow-api
+
+
+
+ com.fnproject.fn
+ runtime
+
+
+ com.fnproject.fn
+ testing-junit4
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ junit
+ junit
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+ org.apache.httpcomponents
+ httpclient
+ test
+
+
+
+
+
+
+
+ maven-dependency-plugin
+
+
+ copy-dependencies
+ package
+
+ copy-dependencies
+
+
+ ${project.build.directory}/dependency
+ runtime
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+
+
diff --git a/runtime/src/main/java/com/fnproject/fn/runtime/flow/APIModel.java b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/APIModel.java
similarity index 96%
rename from runtime/src/main/java/com/fnproject/fn/runtime/flow/APIModel.java
rename to flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/APIModel.java
index eea809a9..886d093c 100644
--- a/runtime/src/main/java/com/fnproject/fn/runtime/flow/APIModel.java
+++ b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/APIModel.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.runtime.flow;
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -5,8 +21,8 @@
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fnproject.fn.api.Headers;
-import com.fnproject.fn.api.flow.*;
import com.fnproject.fn.api.exception.FunctionInputHandlingException;
+import com.fnproject.fn.api.flow.*;
import com.fnproject.fn.runtime.exception.PlatformCommunicationException;
import org.apache.commons.io.IOUtils;
diff --git a/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/BlobResponse.java b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/BlobResponse.java
new file mode 100644
index 00000000..4adcb02c
--- /dev/null
+++ b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/BlobResponse.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.runtime.flow;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class BlobResponse {
+ @JsonProperty("blob_id")
+ public String blobId;
+
+ @JsonProperty("length")
+ public Long blobLength;
+
+ @JsonProperty("content_type")
+ public String contentType;
+}
diff --git a/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/BlobStoreClient.java b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/BlobStoreClient.java
new file mode 100644
index 00000000..9b898adf
--- /dev/null
+++ b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/BlobStoreClient.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.runtime.flow;
+
+import java.io.InputStream;
+import java.util.function.Function;
+
+public interface BlobStoreClient {
+
+
+ BlobResponse writeBlob(String prefix, byte[] bytes, String contentType);
+
+ T readBlob(String prefix, String blobId, Function reader, String expectedContentType);
+}
diff --git a/runtime/src/main/java/com/fnproject/fn/runtime/flow/CodeLocation.java b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/CodeLocation.java
similarity index 71%
rename from runtime/src/main/java/com/fnproject/fn/runtime/flow/CodeLocation.java
rename to flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/CodeLocation.java
index 341cc647..721161c9 100644
--- a/runtime/src/main/java/com/fnproject/fn/runtime/flow/CodeLocation.java
+++ b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/CodeLocation.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.runtime.flow;
diff --git a/runtime/src/main/java/com/fnproject/fn/runtime/flow/CompleterClient.java b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/CompleterClient.java
similarity index 80%
rename from runtime/src/main/java/com/fnproject/fn/runtime/flow/CompleterClient.java
rename to flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/CompleterClient.java
index cee4a055..235115a0 100644
--- a/runtime/src/main/java/com/fnproject/fn/runtime/flow/CompleterClient.java
+++ b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/CompleterClient.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.runtime.flow;
import com.fnproject.fn.api.Headers;
@@ -16,8 +32,8 @@ public interface CompleterClient {
/**
* create a new flow against the flow service
*
- * @param functionId
- * @return
+ * @param functionId Id of the function for which flow needs to be created
+ * @return a FlowId
*/
FlowId createFlow(String functionId);
@@ -33,6 +49,10 @@ public interface CompleterClient {
* Compose a function into the tree
* The transmitted function is wrapped to convert th ElvisFuture into it's completion iD
*
+ * @param flowId flowId for thenCompose
+ * @param completionId completionId for thenCompose
+ * @param fn fn for thenCompose
+ * @param codeLocation codeLocation for thenCompose
* @return a completion ID that completes when the completion returned by the inner function completes
*/
CompletionId thenCompose(FlowId flowId, CompletionId completionId, Serializable fn, CodeLocation codeLocation);
diff --git a/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/CompleterClientFactory.java b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/CompleterClientFactory.java
new file mode 100644
index 00000000..4fdb118b
--- /dev/null
+++ b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/CompleterClientFactory.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.runtime.flow;
+
+import java.io.Serializable;
+
+public interface CompleterClientFactory extends Serializable {
+ CompleterClient getCompleterClient();
+
+ BlobStoreClient getBlobStoreClient();
+
+}
diff --git a/runtime/src/main/java/com/fnproject/fn/runtime/flow/CompletionId.java b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/CompletionId.java
similarity index 54%
rename from runtime/src/main/java/com/fnproject/fn/runtime/flow/CompletionId.java
rename to flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/CompletionId.java
index c533ec5e..5cc5d0ea 100644
--- a/runtime/src/main/java/com/fnproject/fn/runtime/flow/CompletionId.java
+++ b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/CompletionId.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.runtime.flow;
import java.io.Serializable;
diff --git a/runtime/src/main/java/com/fnproject/fn/runtime/flow/DefaultHttpResponse.java b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/DefaultHttpResponse.java
similarity index 57%
rename from runtime/src/main/java/com/fnproject/fn/runtime/flow/DefaultHttpResponse.java
rename to flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/DefaultHttpResponse.java
index 5aac5105..6caacaa5 100644
--- a/runtime/src/main/java/com/fnproject/fn/runtime/flow/DefaultHttpResponse.java
+++ b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/DefaultHttpResponse.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.runtime.flow;
import com.fnproject.fn.api.Headers;
diff --git a/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/EntityReader.java b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/EntityReader.java
new file mode 100644
index 00000000..42728b8e
--- /dev/null
+++ b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/EntityReader.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.runtime.flow;
+
+import java.io.InputStream;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Both an HTTP response and an individual part of a multipart MIME stream are constituted of
+ * a set of headers together with the body stream. This interface abstracts the access to those parts.
+ */
+interface EntityReader {
+ String getHeaderElement(String h, String e);
+
+ Optional getHeaderValue(String header);
+
+ InputStream getContentStream();
+
+ Map getHeaders();
+}
diff --git a/runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowContinuationInvoker.java b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowContinuationInvoker.java
similarity index 89%
rename from runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowContinuationInvoker.java
rename to flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowContinuationInvoker.java
index 81d12f1a..bbd2e3a9 100644
--- a/runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowContinuationInvoker.java
+++ b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowContinuationInvoker.java
@@ -1,12 +1,27 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.runtime.flow;
import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
import com.fnproject.fn.api.*;
+import com.fnproject.fn.api.exception.FunctionInputHandlingException;
import com.fnproject.fn.api.flow.Flow;
import com.fnproject.fn.api.flow.Flows;
import com.fnproject.fn.api.flow.PlatformException;
-import com.fnproject.fn.api.exception.FunctionInputHandlingException;
import com.fnproject.fn.runtime.exception.InternalFunctionInvocationException;
import com.fnproject.fn.runtime.exception.PlatformCommunicationException;
@@ -32,6 +47,10 @@ public final class FlowContinuationInvoker implements FunctionInvoker {
public static final String FLOW_ID_HEADER = "Fnproject-FlowId";
+ FlowContinuationInvoker() {
+
+ }
+
private static class URLCompleterClientFactory implements CompleterClientFactory {
private final String completerBaseUrl;
private transient CompleterClient completerClient;
@@ -45,7 +64,7 @@ private static class URLCompleterClientFactory implements CompleterClientFactory
public synchronized CompleterClient getCompleterClient() {
if (this.completerClient == null) {
this.completerClient = new RemoteFlowApiClient(completerBaseUrl + "/v1",
- getBlobStoreClient(), new HttpClient());
+ getBlobStoreClient(), new HttpClient());
}
return this.completerClient;
}
@@ -135,7 +154,7 @@ public synchronized Flow currentFlow() {
if (matchingDispatchPattern != null) {
if (matchingDispatchPattern.numArguments() != invokeStageRequest.args.size()) {
- throw new FunctionInputHandlingException("Number of arguments provided (" + invokeStageRequest.args.size() + ") in .InvokeStageRequest does not match the number required by the function type (" + matchingDispatchPattern.numArguments() + ")");
+ throw new FunctionInputHandlingException("Number of arguments provided (" + invokeStageRequest.args.size() + ") in .InvokeStageRequest does not match the number required by the function type (" + matchingDispatchPattern.numArguments() + ")");
}
} else {
throw new FunctionInputHandlingException("No functional interface type matches the supplied continuation class");
@@ -168,7 +187,7 @@ public synchronized Flow currentFlow() {
@Override
public synchronized Flow currentFlow() {
if (runtime == null) {
- String functionId = evt.getAppName() + evt.getRoute();
+ String functionId = ctx.getRuntimeContext().getFunctionID();
CompleterClientFactory factory = getOrCreateCompleterClientFactory(completerBaseUrl);
final FlowId flowId = factory.getCompleterClient().createFlow(functionId);
runtime = new RemoteFlow(flowId);
@@ -204,9 +223,9 @@ private OutputEvent invokeContinuation(BlobStoreClient blobStoreClient, FlowId f
APIModel.Datum datum = APIModel.datumFromJava(flowId, ite.getCause(), blobStoreClient);
throw new InternalFunctionInvocationException(
- "Error invoking flows lambda",
- ite.getCause(),
- constructOutputEvent(datum, false)
+ "Error invoking flows lambda",
+ ite.getCause(),
+ constructOutputEvent(datum, false)
);
} catch (Exception ex) {
throw new PlatformException(ex);
@@ -223,27 +242,18 @@ private OutputEvent invokeContinuation(BlobStoreClient blobStoreClient, FlowId f
*/
final static class ContinuationOutputEvent implements OutputEvent {
private final byte[] body;
+ private static final Headers headers = Headers.emptyHeaders().setHeader(OutputEvent.CONTENT_TYPE_HEADER, "application/json");
private ContinuationOutputEvent(boolean success, byte[] body) {
this.body = body;
}
- /**
- * The completer expects a 200 on the output event.
- *
- * @return
- */
@Override
- public int getStatusCode() {
- return OutputEvent.SUCCESS;
+ public Status getStatus() {
+ return Status.Success;
}
- @Override
- public Optional getContentType() {
- return Optional.of("application/json");
- }
-
@Override
public void writeToOutput(OutputStream out) throws IOException {
out.write(body);
@@ -251,7 +261,7 @@ public void writeToOutput(OutputStream out) throws IOException {
@Override
public Headers getHeaders() {
- return Headers.emptyHeaders();
+ return headers;
}
}
diff --git a/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowFeature.java b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowFeature.java
new file mode 100644
index 00000000..42df40c7
--- /dev/null
+++ b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowFeature.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.runtime.flow;
+
+import com.fnproject.fn.api.FunctionInvoker;
+import com.fnproject.fn.api.RuntimeContext;
+import com.fnproject.fn.api.RuntimeFeature;
+
+/**
+ *
+ * The flow feature enables the Flow Client SDK and runtime behaviour in a Java function in order to use Flow in a function you must add the following to the function class:
+ *
+ *
+ *
+ * import com.fnproject.fn.api.FnFeature;
+ * import com.fnproject.fn.runtime.flow.FlowFeature;
+ *
+ * {@literal @}FnFeature(FlowFeature.class)
+ * public class MyFunction {
+ *
+ *
+ * public void myFunction(String input){
+ * Flows.currentFlow()....
+ *
+ * }
+ * }
+ *
+ *
+ *
+ * Created on 10/09/2018.
+ *
+ * (c) 2018 Oracle Corporation
+ */
+public class FlowFeature implements RuntimeFeature {
+ @Override
+ public void initialize(RuntimeContext context){
+ FunctionInvoker invoker = new FlowContinuationInvoker();
+ context.addInvoker(invoker,FunctionInvoker.Phase.PreCall);
+ }
+}
diff --git a/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowFutureSource.java b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowFutureSource.java
new file mode 100644
index 00000000..9f7cdacb
--- /dev/null
+++ b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowFutureSource.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fnproject.fn.runtime.flow;
+
+import com.fnproject.fn.api.flow.FlowFuture;
+
+/**
+ * Created on 27/11/2017.
+ *
+ * (c) 2017 Oracle Corporation
+ */
+public interface FlowFutureSource {
+ FlowFuture createFlowFuture(CompletionId completionId);
+}
diff --git a/runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowId.java b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowId.java
similarity index 55%
rename from runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowId.java
rename to flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowId.java
index ced213f0..65481eb9 100644
--- a/runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowId.java
+++ b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowId.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.runtime.flow;
import java.io.Serializable;
diff --git a/runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowRuntimeGlobals.java b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowRuntimeGlobals.java
similarity index 75%
rename from runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowRuntimeGlobals.java
rename to flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowRuntimeGlobals.java
index 66e61456..405af273 100644
--- a/runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowRuntimeGlobals.java
+++ b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/FlowRuntimeGlobals.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.runtime.flow;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -64,7 +80,7 @@ public static void setCompleterClientFactory(CompleterClientFactory currentClien
/**
* return the current Fn Flow client factory;
*
- * @return
+ * @return a factory of CompleterClient
*/
public static CompleterClientFactory getCompleterClientFactory() {
return completerClientFactory;
diff --git a/runtime/src/main/java/com/fnproject/fn/runtime/flow/HttpClient.java b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/HttpClient.java
similarity index 90%
rename from runtime/src/main/java/com/fnproject/fn/runtime/flow/HttpClient.java
rename to flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/HttpClient.java
index 99637d3c..557fc336 100644
--- a/runtime/src/main/java/com/fnproject/fn/runtime/flow/HttpClient.java
+++ b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/HttpClient.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.runtime.flow;
import org.apache.commons.io.IOUtils;
diff --git a/runtime/src/main/java/com/fnproject/fn/runtime/flow/JsonInvoke.java b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/JsonInvoke.java
similarity index 82%
rename from runtime/src/main/java/com/fnproject/fn/runtime/flow/JsonInvoke.java
rename to flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/JsonInvoke.java
index cfb21953..64d1fc60 100644
--- a/runtime/src/main/java/com/fnproject/fn/runtime/flow/JsonInvoke.java
+++ b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/JsonInvoke.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.runtime.flow;
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -73,7 +89,7 @@ public static FlowFuture invokeFunction(Flow flow, String func
String inputString = getObjectMapper().writeValueAsString(input);
Headers newHeaders;
if (!headers.get("Content-type").isPresent()) {
- newHeaders = headers.withHeader("Content-type", "application/json");
+ newHeaders = headers.addHeader("Content-type", "application/json");
} else {
newHeaders = headers;
}
diff --git a/runtime/src/main/java/com/fnproject/fn/runtime/flow/RemoteBlobStoreClient.java b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/RemoteBlobStoreClient.java
similarity index 76%
rename from runtime/src/main/java/com/fnproject/fn/runtime/flow/RemoteBlobStoreClient.java
rename to flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/RemoteBlobStoreClient.java
index 18f0d644..86762868 100644
--- a/runtime/src/main/java/com/fnproject/fn/runtime/flow/RemoteBlobStoreClient.java
+++ b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/RemoteBlobStoreClient.java
@@ -1,6 +1,21 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.runtime.flow;
-import com.fasterxml.jackson.databind.ObjectMapper;
import com.fnproject.fn.runtime.exception.PlatformCommunicationException;
import java.io.IOException;
diff --git a/runtime/src/main/java/com/fnproject/fn/runtime/flow/RemoteFlow.java b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/RemoteFlow.java
similarity index 93%
rename from runtime/src/main/java/com/fnproject/fn/runtime/flow/RemoteFlow.java
rename to flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/RemoteFlow.java
index 48c2f429..f9535139 100644
--- a/runtime/src/main/java/com/fnproject/fn/runtime/flow/RemoteFlow.java
+++ b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/RemoteFlow.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.runtime.flow;
import com.fnproject.fn.api.Headers;
diff --git a/runtime/src/main/java/com/fnproject/fn/runtime/flow/RemoteFlowApiClient.java b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/RemoteFlowApiClient.java
similarity index 92%
rename from runtime/src/main/java/com/fnproject/fn/runtime/flow/RemoteFlowApiClient.java
rename to flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/RemoteFlowApiClient.java
index 60c96a09..39783de8 100644
--- a/runtime/src/main/java/com/fnproject/fn/runtime/flow/RemoteFlowApiClient.java
+++ b/flow-runtime/src/main/java/com/fnproject/fn/runtime/flow/RemoteFlowApiClient.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.runtime.flow;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -120,12 +136,10 @@ public CompletionId invokeFunction(FlowId flowId, String functionId, byte[] data
httpReq.headers = new ArrayList<>();
- headers.getAll().forEach((k, v) -> {
- APIModel.HTTPHeader h = new APIModel.HTTPHeader();
- h.key = k;
- h.value = v;
- httpReq.headers.add(h);
- });
+ headers.asMap().forEach((k, vs) -> vs.forEach(v -> httpReq.headers.add(APIModel.HTTPHeader.create(k, v))));
+
+ Map> headersMap = headers.asMap();
+ headersMap.forEach((key, values) -> values.forEach(value -> httpReq.headers.add(APIModel.HTTPHeader.create(key, value))));
}
httpReq.method = APIModel.HTTPMethod.fromFlow(method);
@@ -153,9 +167,9 @@ public CompletionId completedValue(FlowId flowId, boolean success, Object value,
APIModel.CompletionResult completionResult = new APIModel.CompletionResult();
completionResult.successful = success;
- if(value instanceof RemoteFlow.RemoteFlowFuture) {
+ if (value instanceof RemoteFlow.RemoteFlowFuture) {
APIModel.StageRefDatum stageRefDatum = new APIModel.StageRefDatum();
- stageRefDatum.stageId = ((RemoteFlow.RemoteFlowFuture)value).id();
+ stageRefDatum.stageId = ((RemoteFlow.RemoteFlowFuture) value).id();
completionResult.result = stageRefDatum;
} else {
APIModel.Datum blobDatum = APIModel.datumFromJava(flowId, value, blobStoreClient);
@@ -251,14 +265,14 @@ public Object waitForCompletion(FlowId flowId, CompletionId id, ClassLoader igno
long remainingTimeout = Math.max(1, start + msTimeout - lastStart);
try (HttpClient.HttpResponse response =
- httpClient.execute(prepareGet(apiUrlBase + "/flows/" + flowId.getId() + "/stages/" + id.getId() + "/await?timeout_ms=" + remainingTimeout))) {
+ httpClient.execute(prepareGet(apiUrlBase + "/flows/" + flowId.getId() + "/stages/" + id.getId() + "/await?timeout_ms=" + remainingTimeout))) {
if (response.getStatusCode() == 200) {
APIModel.AwaitStageResponse resp = FlowRuntimeGlobals.getObjectMapper().readValue(response.getContentStream(), APIModel.AwaitStageResponse.class);
if (resp.result.successful) {
return resp.result.toJava(flowId, blobStoreClient, getClass().getClassLoader());
} else {
- throw new FlowCompletionException((Throwable)resp.result.toJava(flowId, blobStoreClient, getClass().getClassLoader()));
+ throw new FlowCompletionException((Throwable) resp.result.toJava(flowId, blobStoreClient, getClass().getClassLoader()));
}
} else if (response.getStatusCode() == 408) {
// do nothing go round again
@@ -313,10 +327,10 @@ private static PlatformCommunicationException asError(HttpClient.HttpResponse re
try {
String body = response.entityAsString();
return new PlatformCommunicationException(String.format("Received unexpected response (%d) from " +
- "Flow service: %s", response.getStatusCode(), body == null ? "Empty body" : body));
+ "Flow service: %s", response.getStatusCode(), body == null ? "Empty body" : body));
} catch (IOException e) {
return new PlatformCommunicationException(String.format("Received unexpected response (%d) from " +
- "Flow service. Could not read body.", response.getStatusCode()), e);
+ "Flow service. Could not read body.", response.getStatusCode()), e);
}
}
@@ -342,7 +356,7 @@ private static byte[] serializeClosure(Object data) {
private CompletionId addStageWithClosure(APIModel.CompletionOperation operation, FlowId flowId, Serializable supplier, CodeLocation codeLocation, List deps) {
byte[] serialized = serializeClosure(supplier);
- BlobResponse blobResponse = blobStoreClient.writeBlob(flowId.getId(), serialized, CONTENT_TYPE_JAVA_OBJECT);
+ BlobResponse blobResponse = blobStoreClient.writeBlob(flowId.getId(), serialized, CONTENT_TYPE_JAVA_OBJECT);
return addStage(operation, APIModel.Blob.fromBlobResponse(blobResponse), deps, flowId, codeLocation);
diff --git a/runtime/src/test/java/com/fnproject/fn/runtime/flow/FlowsContinuationInvokerTest.java b/flow-runtime/src/test/java/com/fnproject/fn/runtime/flow/FlowsContinuationInvokerTest.java
similarity index 74%
rename from runtime/src/test/java/com/fnproject/fn/runtime/flow/FlowsContinuationInvokerTest.java
rename to flow-runtime/src/test/java/com/fnproject/fn/runtime/flow/FlowsContinuationInvokerTest.java
index 389d4e34..227b507b 100644
--- a/runtime/src/test/java/com/fnproject/fn/runtime/flow/FlowsContinuationInvokerTest.java
+++ b/flow-runtime/src/test/java/com/fnproject/fn/runtime/flow/FlowsContinuationInvokerTest.java
@@ -1,12 +1,27 @@
+/*
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.fnproject.fn.runtime.flow;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fnproject.fn.api.*;
+import com.fnproject.fn.api.exception.FunctionInputHandlingException;
import com.fnproject.fn.api.flow.*;
-import com.fnproject.fn.runtime.QueryParametersImpl;
import com.fnproject.fn.runtime.ReadOnceInputEvent;
-import com.fnproject.fn.api.exception.FunctionInputHandlingException;
import com.fnproject.fn.runtime.exception.InternalFunctionInvocationException;
import org.junit.After;
import org.junit.Before;
@@ -15,10 +30,8 @@
import org.junit.rules.ExpectedException;
import java.io.*;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Serializable;
import java.lang.reflect.Method;
+import java.time.Instant;
import java.util.*;
import static com.fnproject.fn.runtime.flow.FlowContinuationInvoker.FLOW_ID_HEADER;
@@ -62,9 +75,9 @@ public void continuationInvokedWhenGraphHeaderPresent() throws IOException, Clas
// Given
InputEvent event = newRequest()
- .withClosure((Flows.SerFunction) (x) -> x * 2)
- .withJavaObjectArgs(10)
- .asEvent();
+ .withClosure((Flows.SerFunction) (x) -> x * 2)
+ .withJavaObjectArgs(10)
+ .asEvent();
// When
Optional result = invoker.tryInvoke(new EmptyInvocationContext(), event);
@@ -84,8 +97,8 @@ public void continuationNotInvokedWhenHeaderMissing() throws IOException, ClassN
// Given
InputEvent event = new InputEventBuilder()
- .withBody("")
- .build();
+ .withBody("")
+ .build();
// When
FlowContinuationInvoker invoker = new FlowContinuationInvoker();
@@ -102,8 +115,8 @@ public void failsIfArgMissing() throws IOException, ClassNotFoundException {
// Given
InputEvent event = newRequest()
- .withClosure((Flows.SerFunction) (x) -> x * 2)
- .asEvent();
+ .withClosure((Flows.SerFunction) (x) -> x * 2)
+ .asEvent();
invoker.tryInvoke(new EmptyInvocationContext(), event);
@@ -121,8 +134,8 @@ public void failsIfUnknownClosureType() {
thrown.expect(FunctionInputHandlingException.class);
// Given
InputEvent event = newRequest()
- .withClosure(new TestIf())
- .asEvent();
+ .withClosure(new TestIf())
+ .asEvent();
invoker.tryInvoke(new EmptyInvocationContext(), event);
}
@@ -143,25 +156,25 @@ private Tc(Serializable closure, Object result, Object... args) {
}
Tc[] cases = new Tc[]{
- new Tc((Flows.SerConsumer) (v) -> {
- }, null, "hello"),
- new Tc((Flows.SerBiFunction) (String::concat), "hello bob", "hello ", "bob"),
- new Tc((Flows.SerBiConsumer) (a, b) -> {
- }, null, "hello ", "bob"),
- new Tc((Flows.SerFunction) (String::toUpperCase), "HELLO BOB", "hello bob"),
- new Tc((Flows.SerRunnable) () -> {
- }, null),
- new Tc((Flows.SerCallable) () -> "hello", "hello"),
- new Tc((Flows.SerSupplier) () -> "hello", "hello"),
+ new Tc((Flows.SerConsumer) (v) -> {
+ }, null, "hello"),
+ new Tc((Flows.SerBiFunction) (String::concat), "hello bob", "hello ", "bob"),
+ new Tc((Flows.SerBiConsumer) (a, b) -> {
+ }, null, "hello ", "bob"),
+ new Tc((Flows.SerFunction) (String::toUpperCase), "HELLO BOB", "hello bob"),
+ new Tc((Flows.SerRunnable) () -> {
+ }, null),
+ new Tc((Flows.SerCallable) () -> "hello", "hello"),
+ new Tc((Flows.SerSupplier) () -> "hello", "hello"),
};
for (Tc tc : cases) {
InputEvent event = newRequest()
- .withClosure(tc.closure)
- .withJavaObjectArgs(tc.args)
- .asEvent();
+ .withClosure(tc.closure)
+ .withJavaObjectArgs(tc.args)
+ .asEvent();
Optional result = invoker.tryInvoke(new EmptyInvocationContext(), event);
assertThat(result).isPresent();
@@ -180,13 +193,13 @@ private Tc(Serializable closure, Object result, Object... args) {
public void emptyValueCorrectlySerialized() throws IOException, ClassNotFoundException {
// Given
InputEvent event = newRequest()
- .withClosure((Flows.SerConsumer