diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..1f6a95a3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,38 @@
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+.DS_Store
+*.iml
+.idea
+
+# maven shade plugin
+dependency-reduced-pom.xml
+target
+
+# documentation
+ /_build/
+/venv/
+_build
+/dist/html/
+build-linter
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..261eeb9e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ 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.
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..ec3cbe0b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,223 @@
+![Stateful Functions](stateful-functions-docs/images/stateful_functions_logo.png)
+
+Stateful Functions is a library for distributed applications and services, based on, well, you guessed it: stateful functions.
+
+The project aims to simplify the development of distributed stateful applications by solving some of the common
+challenges in those applications: scaling, consistent state management, reliable interaction between distributed
+services, and resource management.
+
+Stateful Functions uses Apache Flink for distributed coordination, state, and communication.
+
+This README is meant as a brief walkthrough on the core concepts and how to set things up
+to get yourself started with Stateful Functions. For a fully detailed documentation, please
+see [https://statefun.io](https://statefun.io).
+
+## Table of Contents
+
+- [Core Concepts](#core-concepts)
+ * [Abstraction](#abstraction)
+ * [Function modules and extensibility](#modules)
+- [Getting Started](#getting-started)
+ * [Building the project](#build)
+ * [Running a full example](#greeter)
+ * [Project setup](#project-setup)
+ * [Running in the IDE](#ide-harness)
+- [Deploying Applications](#deploying)
+ * [Deploying with a Docker image](#docker)
+ * [Deploying as a Flink job](#flink)
+- [Contributing](#contributing)
+- [License](#license)
+
+## Core Concepts
+
+### Abstraction
+
+A Stateful Functions application consists of the following primitives: stateful functions, ingresses,
+routers, and egresses.
+
+#### Stateful functions
+
+* Stateful functions are the building blocks and namesake of the Stateful Functions framework.
+A function is a small piece of logic (currently simple Java functions) that are invoked through a message.
+
+* Each stateful function exist as uniquely invokable _virtual instances_ of a _function type_. Each instance
+is addressed by its type, as well as an unique id (a string) within its type.
+
+* Stateful functions may be invoked from ingresses or any other stateful function (including itself).
+The caller simply needs to know the address of the target function.
+
+* Function instances are _virtual_, because they are not all active in memory at the same time.
+At any point in time, only a small set of functions and their state exists as actual objects. When a
+virtual instance receives a message, one of the objects is configured and loaded with the state of that virtual
+instance and then processes the message. Similar to virtual memory, the state of many functions might be “swapped out”
+at any point in time.
+
+* Each virtual instance of a function has its own state, which can be accessed in local variables.
+That state is private and local to that instance.
+
+If you know Apache Flink’s DataStream API, you can think of stateful functions a bit like a lightweight
+`KeyedProcessFunction`. The function type is process function transformation, while the ID is the key. The difference
+is that functions are not assembled in a directed acyclic graph that defines the flow of data (the streaming topology),
+but rather send events arbitrarily to all other functions using addresses.
+
+#### Ingresses and Egresses
+
+* _Ingresses_ are the way that events initially arrive in a Stateful Functions application.
+Ingresses can be message queues, logs, or HTTP servers - anything that produces an event to be
+handled by the application.
+
+* _Routers_ are attached to ingresses to determine which function instance should handle an event initially.
+
+* _Egresses_ are a way to send events out from the application in a standardized way.
+Egresses are optional; it is also possible that no events leave the application and functions sink events or
+directly make calls to other applications.
+
+### Modules and extensibility
+
+A _module_ is the entry point for adding to a Stateful Functions
+application the core building block primitives, i.e. ingresses, egresses, routers, and stateful functions.
+
+A single application may be a combination of multiple modules, each contributing a part of the whole application.
+This allows different parts of the application to be contributed by different modules; for example,
+one module may provide ingresses and egresses, while other modules may individually contribute specific parts of the
+business logic as stateful functions. This facilitates working in independent teams, but still deploying
+into the same larger application.
+
+This extensibility is achieved by leveraging the [Java Service Loader](https://docs.oracle.com/javase/tutorial/ext/basics/spi.html#the-serviceloader-class).
+In this context, each module is essentially a service provider.
+
+## Getting Started
+
+Follow the steps here to get started right away with Stateful Functions.
+
+This guide will walk you through locally building the project, running an existing example, and setup to
+start developing and testing your own Stateful Functions application.
+
+### Building the project
+
+Prerequisites:
+
+* Docker
+* Maven 3.5.x or above
+* Java 8 or above
+
+Currently, the project does not have any publicly available artifacts or Docker images for use, so you would have to
+first build the project yourself before trying it out.
+
+```
+mvn clean install
+```
+
+If you want to [deploy your applications using Docker](#docker), you should also build the base Docker image:
+
+```
+./tools/docker/build-stateful-functions.sh
+```
+
+### Running a full example
+
+As a simple demonstration, we will be going through the steps to run the [Greeter example](stateful-functions-examples/stateful-functions-greeter-example).
+
+Before anything else, make sure that you have locally [built the project as well as the base Stateful Functions Docker image](#build).
+Then, follow the next steps to run the example:
+
+```
+cd stateful-functions-examples/stateful-functions-greeter-example
+docker-compose build
+docker-compose up
+```
+
+This example contains a very basic stateful function with a Kafka ingress and a Kafka egress.
+
+To see the example in action, send some messages to the topic `names`, and see what comes out out of the topic `greetings`:
+
+```
+KAFKA=$(docker ps -f "name=stateful-functions-greeter-example_kafka-broker_1" --format "{{.ID}}") ; \
+docker exec -it $KAFKA kafka-console-producer.sh \
+ --broker-list localhost:9092 \
+ --topic names
+```
+
+```
+KAFKA=$(docker ps -f "name=stateful-functions-greeter-example_kafka-broker_1" --format "{{.ID}}") ; \
+docker exec -it $KAFKA kafka-console-consumer.sh \
+ --bootstrap-server localhost:9092 \
+ --topic greetings
+```
+
+### Project setup
+
+You can quickly get started building Stateful Functions applications using the provided quickstart Maven archetype:
+
+```
+mvn archetype:generate \
+ -DarchetypeGroupId=com.ververica \
+ -DarchetypeArtifactId=stateful-functions-quickstart \
+ -DarchetypeVersion=1.0-SNAPSHOT
+```
+
+This allows you to name your newly created project. It will interactively ask you for the groupId,
+artifactId, and package name. There will be a new directory with the same name as your artifact id.
+
+We recommend you import this project into your IDE to develop and test it.
+IntelliJ IDEA supports Maven projects out of the box. If you use Eclipse, the `m2e` plugin allows to import
+Maven projects. Some Eclipse bundles include that plugin by default, others require you to install it manually.
+
+### Running from the IDE
+
+To test out your application, you can directly run it in the IDE without any further packaging or deployments.
+
+Please see the [Harness example](stateful-functions-examples/stateful-functions-flink-harness-example) on how to do that.
+
+## Deploying Applications
+
+Stateful Functions applications can be packaged as either standalone applications or Flink jobs that can be
+submitted to a Flink cluster.
+
+### Deploying with a Docker image
+
+Below is an example Dockerfile for building an image for an application called `stateful-functions-example`:
+
+```
+FROM stateful-functions
+
+RUN mkdir -p /opt/stateful-functions/modules/stateful-functions-example
+COPY target/stateful-functions-example*jar /opt/stateful-functions/modules/stateful-functions-example/
+```
+
+### Deploying as a Flink job
+
+If you prefer to package your Stateful Functions application as a Flink job to submit to an existing Flink cluster,
+simply include `stateful-functions-flink-distribution` as a dependency to your application.
+
+```
+
+ com.ververica
+ stateful-functions-flink-distribution
+ 1.0-SNAPSHOT
+
+```
+
+It includes all the runtime dependencies and configures the application's main entry-point.
+You do not need to take any action beyond adding the dependency to your Maven pom.
+
+Bundle the distribution with your application as a fat jar, and then submit it as you normally would
+with any other Flink job:
+
+```
+{$FLINK_DIR}/bin/flink run ./stateful-functions-example.jar
+```
+
+## Contributing
+
+If you find these ideas interesting or promising, try Stateful Functions out and get involved!
+Check out the example walkthrough or the docs. File an issue if you have an idea how to improve things.
+
+The project is work-in-progress. We believe we are off to a promising direction, but there is still a
+way to go to make all parts of this vision a reality. There are many possible ways to enhance the Stateful
+Functions API for different types of applications. Runtime and operations of Stateful Functions
+will also evolve with the capabilities of Apache Flink.
+
+## License
+
+The code in this repository is under the Apache license. See [license](blob/master/LICENSE).
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 00000000..37ac068c
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,148 @@
+
+
+
+
+ 4.0.0
+
+ stateful-functions-parent
+ com.ververica
+ stateful-functions-parent
+ 1.0-SNAPSHOT
+ pom
+
+
+ stateful-functions-sdk
+ stateful-functions-kafka-io
+ stateful-functions-examples
+ stateful-functions-flink
+ stateful-functions-quickstart
+ stateful-functions-docs
+
+
+
+ UTF-8
+ UTF-8
+ 1.8
+ 3.1.1
+ 1.20.0
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+
+ 8
+
+
+
+
+
+ com.github.spotbugs
+ spotbugs-maven-plugin
+ ${spotbugs.version}
+
+
+ com.github.spotbugs
+ spotbugs
+ ${spotbugs.version}
+
+
+
+
+ analyze-compile
+ verify
+
+ check
+
+
+
+
+ Max
+ Low
+ tools/maven/spotbugs-exclude.xml
+
+
+
+
+
+
+ org.apache.rat
+ apache-rat-plugin
+ 0.13
+
+
+ verify
+
+ check
+
+
+
+
+
+
+ **/.*/**
+ **/*.prefs
+ **/*.log
+
+ **/README.md
+ .github/**
+
+ **/target/**
+ **/_build/**
+ stateful-functions-docs/requirements.txt
+ stateful-functions-docs/runtime.txt
+ stateful-functions-docs/docs/_templates/**
+
+ **/generated/**
+
+
+
+
+
+
+ com.diffplug.spotless
+ spotless-maven-plugin
+ ${spotless-maven-plugin.version}
+
+
+
+ 1.7
+
+
+
+
+
+
+
+ spotless-check
+ verify
+
+ check
+
+
+
+
+
+
+
+
diff --git a/stateful-functions-docs/Dockerfile b/stateful-functions-docs/Dockerfile
new file mode 100644
index 00000000..feaa46b4
--- /dev/null
+++ b/stateful-functions-docs/Dockerfile
@@ -0,0 +1,25 @@
+# Copyright 2019 Ververica GmbH.
+#
+# 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 python:3
+
+RUN apt-get update && apt-get -y install texlive-latex-recommended texlive-fonts-recommended texlive-latex-extra latexmk
+
+COPY requirements.txt /requirements.txt
+
+RUN set -e; \
+ pip install --no-cache-dir -r /requirements.txt
+
+VOLUME "/build"
+EXPOSE 8000
+CMD ["make" "-C", "/build", "html"]
diff --git a/stateful-functions-docs/Dockerfile-linter b/stateful-functions-docs/Dockerfile-linter
new file mode 100644
index 00000000..d306ae21
--- /dev/null
+++ b/stateful-functions-docs/Dockerfile-linter
@@ -0,0 +1,23 @@
+# Copyright 2019 Ververica GmbH.
+#
+# 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 alpine:3.8 as docs-linter
+
+RUN apk add --no-cache \
+ python3 \
+ nodejs \
+ npm
+
+RUN pip3 install --disable-pip-version-check proselint \
+ && npm install --global alex markdown-spellcheck retext-mapbox-standard write-good
diff --git a/stateful-functions-docs/Makefile b/stateful-functions-docs/Makefile
new file mode 100644
index 00000000..33867769
--- /dev/null
+++ b/stateful-functions-docs/Makefile
@@ -0,0 +1,168 @@
+# Copyright 2019 Ververica GmbH.
+#
+# 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.
+
+# ________________________________________________________
+# _________/\\\___________________________________________
+# ________\/\\\___________________________________________
+# ________\/\\\___________________________________________
+# ________\/\\\______/\\\\\________/\\\\\\\\__/\\\\\\\\\\_
+# ___/\\\\\\\\\____/\\\///\\\____/\\\//////__\/\\\//////__
+# __/\\\////\\\___/\\\__\//\\\__/\\\_________\/\\\\\\\\\\_
+# _\/\\\__\/\\\__\//\\\__/\\\__\//\\\________\////////\\\_
+# _\//\\\\\\\/\\__\///\\\\\/____\///\\\\\\\\__/\\\\\\\\\\_
+# __\///////\//_____\/////________\////////__\//////////__
+# ________________________________________________________
+#
+# Makefile for Ververica Platform Docs
+#
+
+BUILDDIR = _build
+DOCKER_GROUP = $(or $(shell printenv DOCKER_GROUP), $(GID), 1000)
+DOCKER_USER = $(or $(shell printenv DOCKER_USER), $(UID), 1000)
+DOCS_FILES = $(shell find . -name \*.rst -print)
+SOURCEDIR = docs
+SPHINXBUILD = sphinx-build
+SPHINXOPTS = -W
+SPHINXPROJ = Stateful Functions
+
+
+# Target called by netlify, does some setup tasks specific to netlify
+.PHONY: multiversion
+multiversion:
+ sphinx-versioning build "$(SOURCEDIR)" "$(BUILDDIR)/html"
+
+# Target for building the docs on netlify
+.PHONY: netlify
+netlify:
+ ./netlify-build.sh
+
+# Run sphinx-autobuild to watch and rebuild the docs on changes
+.PHONY: autobuild
+autobuild:
+ sphinx-autobuild \
+ --host 0.0.0.0 \
+ --port 8000 \
+ --ignore "*.sw?" \
+ --ignore "*.sw??" \
+ --ignore "*~" \
+ "$(SOURCEDIR)" \
+ "$(BUILDDIR)/html" \
+ $(SPHINXOPTS) \
+ $(0)
+
+.PHONY: latexpdf
+latexpdf:
+ sphinx-build \
+ -M latexpdf \
+ "$(SOURCEDIR)" \
+ "$(BUILDDIR)/pdf" \
+ $(SPHINXOPTS) \
+ $(0)
+
+# Build the base docker image for the docker-% target to use
+.PHONY: docker
+docker:
+ docker build -t stateful-functions-docs-builder .
+
+# Run other make targets in docker... obviously don't call another docker target
+.PHONY: docker-%
+docker-%: docker
+ docker \
+ run \
+ --rm \
+ -it \
+ -v $(PWD):/build \
+ --user $(DOCKER_USER):$(DOCKER_GROUP) \
+ -p 8000:8000 \
+ stateful-functions-docs-builder \
+ make -C /build $*
+
+# Run sphinx-autobuild in a docker container, much nicer than running locally
+.PHONY: auto
+auto: docker-autobuild
+
+.PHONY: html
+html:
+ @$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+# Legacy build hosted in a docker container, then deployed to kubernetes
+.PHONY: dist
+dist: docker-html
+ rsync -a _build/html dist/
+ make -C dist
+
+# Remove/clean the build directory
+.PHONY: clean
+clean:
+ rm -rf "$(BUILDDIR)"
+
+build-linter: Dockerfile-linter
+ docker build -f Dockerfile-linter -t stateful-functions/stateful-functions-docs-linter .
+ touch $@
+
+.PHONY: docker-linter
+docker-linter: build-linter
+ docker \
+ run \
+ --rm \
+ -it \
+ --name docker-linter \
+ -v ${PWD}:/run/docs \
+ -w /run/docs \
+ stateful-functions/stateful-functions-docs-linter:latest \
+ $(LINTER) $(or $(LINTER_TARGET), $(DOCS_FILES))
+
+.PHONY: linter-alex
+linter-alex:
+ make docker-linter LINTER=alex
+
+.PHONY: linter-mdspell
+linter-mdspell:
+ make docker-linter LINTER="mdspell --en-us --ignore-numbers --ignore-acronyms --report"
+
+.PHONY: linter-proselint
+linter-proselint:
+ make docker-linter LINTER=proselint
+
+.PHONY: linter-retext-mapbox-standard
+linter-retext-mapbox-standard:
+ make docker-linter LINTER=retext-mapbox-standard
+
+.PHONY: linter-write-good
+linter-write-good:
+ make docker-linter LINTER=write-good
+
+.PHONY: lint
+lint:
+ make linter-write-good || true
+ make linter-retext-mapbox-standard || true
+ make linter-alex || true
+ make linter-proselint || true
+
+# This recreates the netlify build environment for local testing and troubleshooting.
+# If you're really stumped, try running it with BUILD_ARGS=/bin/bash to get a shell
+# and look around the filesystem to see whats up.
+.PHONY: netlify-build-local
+netlify-build-local:
+ @mkdir -p _build
+ @chmod g+w _build
+ docker \
+ run \
+ --rm \
+ -it \
+ -v $(PWD):/opt/buildhome/local_repo \
+ -e NETLIFY_REPO_URL=/opt/buildhome/local_repo \
+ --user buildbot:$(DOCKER_GROUP) \
+ netlify/build \
+ build $(or $(BUILD_ARGS), ./netlify-build-local.sh)
diff --git a/stateful-functions-docs/README.md b/stateful-functions-docs/README.md
new file mode 100644
index 00000000..0e84ca5d
--- /dev/null
+++ b/stateful-functions-docs/README.md
@@ -0,0 +1,18 @@
+Stateful Functions Documentation
+-------------------------
+
+This documentation is using http://www.sphinx-doc.org/ with the https://pypi.org/project/sphinxcontrib-versioning/ plugin.
+
+Consult the [requirements.txt](requirements.txt) file for the relevant versions of the packages we're using.
+
+# Build the documentation locally
+
+Since we're using the [sphinxcontrib-versioning](https://pypi.org/project/sphinxcontrib-versioning/) plugin to build multiple versions of the documentation, building the docs locally will depend on what you want to achieve.
+
+If you are only concerend with seeing a particular branch/commit that you're working on, then using `make docker-autobuild` will achieve what you want. You can then navigate to http://localhost:8000/index.html to see the resulting pages, and it will auto rebuild the docs on every edit as well.
+
+If the build fails or you experience any issues, try doing a `make clean` to remove any old build artifacts first.
+
+# Export the documentation locally as PDF
+
+`make docker-latexpdf` will export the current brach/commit of the documentation asd PDF. The final documentation will be located under `_build/pdf/latex/ApplicationManager.pdf`.
diff --git a/stateful-functions-docs/docs/_static/.gitkeep b/stateful-functions-docs/docs/_static/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/stateful-functions-docs/docs/_static/css/customize-theme.css b/stateful-functions-docs/docs/_static/css/customize-theme.css
new file mode 100644
index 00000000..ac0d2979
--- /dev/null
+++ b/stateful-functions-docs/docs/_static/css/customize-theme.css
@@ -0,0 +1,51 @@
+/* Copyright 2019 Ververica GmbH.
+
+ 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.
+*/
+
+.wy-side-nav-search>a, .wy-side-nav-search .wy-dropdown>a {
+ display: block;
+}
+
+.wy-nav-top {
+ background-color: #446D6F;
+}
+
+.wy-side-nav-search>a img.logo, .wy-side-nav-search .wy-dropdown>a img.logo {
+ width: 100%;
+ height: auto;
+}
+
+.wy-menu-vertical a:active {
+ background-color: #446D6F;
+}
+
+.wy-side-nav-search {
+ background-color: #446D6F;
+}
+
+.wy-nav-content a {
+ color: #446D6F;
+}
+
+.wy-nav-content a:visited {
+ color: #7A9E9C;
+}
+
+.wy-nav-content a:hover {
+ text-decoration: underline;
+}
+
+.scv-banner > a {
+ color: white ! important;
+}
\ No newline at end of file
diff --git a/stateful-functions-docs/docs/_static/favicon.png b/stateful-functions-docs/docs/_static/favicon.png
new file mode 100644
index 00000000..000a1038
Binary files /dev/null and b/stateful-functions-docs/docs/_static/favicon.png differ
diff --git a/stateful-functions-docs/docs/_static/images/async_exec.png b/stateful-functions-docs/docs/_static/images/async_exec.png
new file mode 100644
index 00000000..c96fa9c3
Binary files /dev/null and b/stateful-functions-docs/docs/_static/images/async_exec.png differ
diff --git a/stateful-functions-docs/docs/_static/images/fault_tolerant.png b/stateful-functions-docs/docs/_static/images/fault_tolerant.png
new file mode 100644
index 00000000..dd552d0a
Binary files /dev/null and b/stateful-functions-docs/docs/_static/images/fault_tolerant.png differ
diff --git a/stateful-functions-docs/docs/_static/images/greeter-function.gif b/stateful-functions-docs/docs/_static/images/greeter-function.gif
new file mode 100644
index 00000000..c7055ee4
Binary files /dev/null and b/stateful-functions-docs/docs/_static/images/greeter-function.gif differ
diff --git a/stateful-functions-docs/docs/_static/images/resource_footprint.png b/stateful-functions-docs/docs/_static/images/resource_footprint.png
new file mode 100644
index 00000000..c16e34f2
Binary files /dev/null and b/stateful-functions-docs/docs/_static/images/resource_footprint.png differ
diff --git a/stateful-functions-docs/docs/_static/images/state_first.png b/stateful-functions-docs/docs/_static/images/state_first.png
new file mode 100644
index 00000000..bede9254
Binary files /dev/null and b/stateful-functions-docs/docs/_static/images/state_first.png differ
diff --git a/stateful-functions-docs/docs/_static/images/stateful_functions_overview.png b/stateful-functions-docs/docs/_static/images/stateful_functions_overview.png
new file mode 100755
index 00000000..85b4f0d6
Binary files /dev/null and b/stateful-functions-docs/docs/_static/images/stateful_functions_overview.png differ
diff --git a/stateful-functions-docs/docs/_static/logo.png b/stateful-functions-docs/docs/_static/logo.png
new file mode 100644
index 00000000..b3d80575
Binary files /dev/null and b/stateful-functions-docs/docs/_static/logo.png differ
diff --git a/stateful-functions-docs/docs/_templates/breadcrumbs.html b/stateful-functions-docs/docs/_templates/breadcrumbs.html
new file mode 100644
index 00000000..ce288078
--- /dev/null
+++ b/stateful-functions-docs/docs/_templates/breadcrumbs.html
@@ -0,0 +1,22 @@
+{%- extends "sphinx_rtd_theme/breadcrumbs.html" %}
+
+
+{% block breadcrumbs %} {% endblock %}
+
+{% block breadcrumbs_aside %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/stateful-functions-docs/docs/_templates/footer.html b/stateful-functions-docs/docs/_templates/footer.html
new file mode 100644
index 00000000..6903152e
--- /dev/null
+++ b/stateful-functions-docs/docs/_templates/footer.html
@@ -0,0 +1,9 @@
+{% extends '!footer.html' %}
+
+{% block extrafooter %}
+
+
Apache Flink, Flink®, Apache®, the squirrel logo, and the Apache feather logo are either registered trademarks or trademarks of The Apache Software Foundation.
+
+{% endblock %}
\ No newline at end of file
diff --git a/stateful-functions-docs/docs/_templates/layout.html b/stateful-functions-docs/docs/_templates/layout.html
new file mode 100644
index 00000000..650013a5
--- /dev/null
+++ b/stateful-functions-docs/docs/_templates/layout.html
@@ -0,0 +1,13 @@
+{% extends '!layout.html' %}
+
+{%- set favicon = 'favicon.png' %}
+{%- set logo = 'logo.png' %}
+{%- set theme_logo_only = True %}
+
+{% block sidebartitle %}
+
+ {{ super() }}
+
+
A framework for stateful distributed applications by the original creators of Apache Flink®.
+
+{% endblock %}
diff --git a/stateful-functions-docs/docs/_templates/searchbox.html b/stateful-functions-docs/docs/_templates/searchbox.html
new file mode 100644
index 00000000..e69de29b
diff --git a/stateful-functions-docs/docs/api_concepts/index.rst b/stateful-functions-docs/docs/api_concepts/index.rst
new file mode 100644
index 00000000..aa71be84
--- /dev/null
+++ b/stateful-functions-docs/docs/api_concepts/index.rst
@@ -0,0 +1,31 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+.. _api-concepts:
+
+############
+API Concepts
+############
+
+.. toctree::
+ :hidden:
+
+ stateful_functions
+ match_functions
+ persistence
+ io_module/index
+
+Stateful Functions applications are a collection of virtual stateful functions that can send arbitrary messages between each other and external systems.
+The execution can happen on a local JVM, or clusters of many machines.
+This section walks you through the basic API's you need to be familiar with to get started.
diff --git a/stateful-functions-docs/docs/api_concepts/io_module/custom.rst b/stateful-functions-docs/docs/api_concepts/io_module/custom.rst
new file mode 100644
index 00000000..0c8a721f
--- /dev/null
+++ b/stateful-functions-docs/docs/api_concepts/io_module/custom.rst
@@ -0,0 +1,86 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+########################
+User Defined I/O Modules
+########################
+
+An I/O module provides access to data which is stored in an external system.
+If a pre-built I/O module for a particular system does not exist, you can define your own.
+
+Stateful Functions I/O modules are built on top of {flink} connectors, for details of how to build a custom connector see the official {flink} `documentation `_.
+
+.. contents:: :local:
+
+A Two Package Approach
+======================
+
+Stateful Functions applications are typically modular, containing many modules multiplexed into a single Flink application.
+For that reason, I/O modules provide two components, a specification, and an implementation.
+That way, multiple modules can depend on the same I/O type while the implementation only needs to be provided once on the classpath.
+
+Specifications
+==============
+
+Specifications are the user-facing component of an I/O module and only depend on ``stateful-functions-sdk``.
+They include an ingress or egress type and spec.
+
+Ingress and egress types are similar to function types, they provide an namespace and type associated with a class of I/O components.
+Specs are what users configure to set properties for a particular instance of an I/O connection.
+The only required parameter is the ingress or egress identifier, all other properties will by system specific.
+
+.. literalinclude:: ../../../src/main/java/com/ververica/statefun/docs/io/custom/MyIngressSpec.java
+ :language: java
+ :lines: 16-
+
+.. literalinclude:: ../../../src/main/java/com/ververica/statefun/docs/io/custom/MyEgressSpec.java
+ :language: java
+ :lines: 16-
+
+Implementations
+===============
+
+The implementation maps specs to Flink sources and sinks.
+They depend on ``stateful-functions-flink-io``, your specifications module, and the underlying Flink connector.
+
+Source and Sink Providers
+"""""""""""""""""""""""""
+
+Providers take in the ingress and egress specs and return configured Flink sources and sinks.
+
+.. literalinclude:: ../../../src/main/java/com/ververica/statefun/docs/io/custom/flink/MySourceProvider.java
+ :language: java
+ :lines: 16-
+
+.. literalinclude:: ../../../src/main/java/com/ververica/statefun/docs/io/custom/flink/MySinkProvider.java
+ :language: java
+ :lines: 16-
+
+Flink I/O Module
+""""""""""""""""
+
+Flink I/O modules are Stateful Functions' top level entry point for accessing Flink connectors.
+They define the relationship between ingress and egress types and source and sink providers.
+It also provides runtime configurations through the ``globalConfguration`` which is the union of all configurations in the applications ``flink-conf.yaml`` and any command line arguments passed in the form ``--key value``.
+
+.. literalinclude:: ../../../src/main/java/com/ververica/statefun/docs/io/custom/flink/MyFlinkIoModule.java
+ :language: java
+ :lines: 16-
+
+I/O modules leverage `Java’s Service Provider Interfaces (SPI) `_ for discovery.
+This means that every JAR should contain a file ``com.ververica.statefun.flink.io.spi.FlinkIoModule`` in the ``META_INF/services`` resource directory that lists all available modules that it provides.
+
+.. code-block:: yaml
+
+ com.ververica.statefun.docs.impl.io.MyFlinkIoModule
\ No newline at end of file
diff --git a/stateful-functions-docs/docs/api_concepts/io_module/index.rst b/stateful-functions-docs/docs/api_concepts/io_module/index.rst
new file mode 100644
index 00000000..f273aafa
--- /dev/null
+++ b/stateful-functions-docs/docs/api_concepts/io_module/index.rst
@@ -0,0 +1,100 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+##########
+I/O Module
+##########
+
+.. toctree::
+ :hidden:
+
+ kafka
+ source_sink
+ custom
+
+Stateful Functions' I/O modules allow functions to receive and send messages to external systems.
+Based on the concept of Ingress (input) and Egress (output) points, and built on top of the {flink} connector ecosystem, I/O modules enable functions to interact with the outside world through the style of message passing.
+
+.. contents:: :local:
+
+.. _ingress:
+
+Ingress
+^^^^^^^^
+
+An Ingress is an input point where data is consumed from an external system and forwarded to zero or more functions.
+An ``IngressIdentifier`` and an ``IngressSpec`` define it.
+
+An ingress identifier, similar to an function type, uniquely identifies an ingress by specifying its input type, a namespace, and a name.
+
+.. literalinclude:: ../../../src/main/java/com/ververica/statefun/docs/io/ingress/Identifiers.java
+ :language: java
+ :lines: 16-
+
+The spec defines the details of how to connect to the external system, which is specific to each individual I/O module.
+Each identifier-spec pair is bound to the system inside an stateful function module.
+
+.. literalinclude:: ../../../src/main/java/com/ververica/statefun/docs/io/ingress/ModuleWithIngress.java
+ :language: java
+ :lines: 16-
+
+Router
+""""""
+
+A router is a stateless operator that takes each record from an ingress and routes it to zero or more functions.
+
+.. literalinclude:: ../../../src/main/java/com/ververica/statefun/docs/io/ingress/UserRouter.java
+ :language: java
+ :lines: 16-
+
+Routers are bound to the system via a stateful function module.
+Unlike other components, an ingress may have any number of routers.
+
+.. literalinclude:: ../../../src/main/java/com/ververica/statefun/docs/io/ingress/ModuleWithRouter.java
+ :language: java
+ :lines: 16-
+
+.. _egress:
+
+Egress
+^^^^^^
+
+Egress is the opposite of ingress; it is a point that takes messages and writes them to external systems.
+Each egress is defined using two components, an ``EgressIdentifier`` and an ``EgressSpec``.
+
+An egress identifier uniquely identifies an egress based on a namespace, name, and producing type.
+
+.. literalinclude:: ../../../src/main/java/com/ververica/statefun/docs/io/egress/Identifiers.java
+ :language: java
+ :lines: 16-
+
+An egress spec defines the details of how to connect to the external system, the details are specific to each individual I/O module.
+Each identifier-spec pair are bound to the system inside a stateful function module.
+
+.. literalinclude:: ../../../src/main/java/com/ververica/statefun/docs/io/egress/ModuleWithEgress.java
+ :language: java
+ :lines: 16-
+
+Stateful functions may then message an egress the same way they message another function, passing the egress identifier as function type.
+
+.. literalinclude:: ../../../src/main/java/com/ververica/statefun/docs/io/egress/FnOutputting.java
+ :language: java
+ :lines: 16-
+
+I/O modules leverage `Java’s Service Provider Interfaces (SPI) `_ for discovery.
+This means that every JAR should contain a file ``com.ververica.statefun.sdk.spi.StatefulFunctionModule`` in the ``META_INF/services`` resource directory that lists all available modules that it provides.
+
+.. code-block:: yaml
+
+ com.ververica.statefun.docs.BasicFunctionModule
\ No newline at end of file
diff --git a/stateful-functions-docs/docs/api_concepts/io_module/kafka.rst b/stateful-functions-docs/docs/api_concepts/io_module/kafka.rst
new file mode 100644
index 00000000..9482ba59
--- /dev/null
+++ b/stateful-functions-docs/docs/api_concepts/io_module/kafka.rst
@@ -0,0 +1,106 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+############
+Apache Kafka
+############
+
+Stateful Functions offers an Apache Kafka I/O Module for reading from and writing to Kafka topics.
+It is based on Apache Flink's universal `Kafka connector `_ and provides exactly-once processing semantics.
+
+.. contents:: :local:
+
+Dependency
+===========
+
+To use the Kafka I/O Module, please include the following dependency in your pom.
+
+.. code-block:: xml
+
+
+ com.ververica
+ stateful-functions-kafka-io
+ {version}
+ provided
+
+
+Kafka Ingress Builder
+=====================
+
+A ``KafkaIngressBuilder`` declares an ingress spec for consuming from Kafka cluster.
+
+It accepts the following arguments:
+
+1) The ingress identifier associated with this ingress
+2) The topic name / list of topic names
+3) The address of the bootstrap servers
+4) A ``KafkaIngressDeserializer`` for deserializing data from Kafka
+5) Properties for the Kafka consumer
+
+.. literalinclude:: ../../../src/main/java/com/ververica/statefun/docs/io/kafka/IngressSpecs.java
+ :language: java
+ :lines: 16-
+
+Please refer to the Kafka `consumer configuration `_ documentation for the full list of available properties.
+
+Kafka Deserializer
+""""""""""""""""""
+
+The Kafka ingress needs to know how to turn the binary data in Kafka into Java objects.
+The ``KafkaIngressDeserializer`` allows users to specify such a schema.
+The ``T deserialize(ConsumerRecord record)`` method gets called for each Kafka message, passing the key, value, and metadata from Kafka.
+
+.. literalinclude:: ../../../src/main/java/com/ververica/statefun/docs/io/kafka/UserDeserializer.java
+ :language: java
+ :lines: 16-
+
+Kafka Egress Spec
+=================
+
+A ``KafkaEgressBuilder`` declares an egress spec for writing data out to a Kafka cluster.
+
+It accepts the following arguments:
+
+1) The egress identifier associated with this egress
+2) The address of the bootstrap servers
+3) A ``KafkaEgressSerializer`` for serializing data into Kafka
+4) The fault tolerance semantic
+5) Properties for the Kafka producer
+
+.. literalinclude:: ../../../src/main/java/com/ververica/statefun/docs/io/kafka/EgressSpecs.java
+ :language: java
+ :lines: 16-
+
+Please refer to the Kafka `producer configuration `_ documentation for the full list of available properties.
+
+Kafka Egress and Fault Tolerance
+""""""""""""""""""""""""""""""""
+
+With fault tolerance enabled, the Kafka egress can provide exactly-once delivery guarantees.
+You can choose three different modes of operating based through the ``KafkaEgressBuilder``.
+
+* ``KafkaEgressBuilder#withNoProducerSemantics``: Nothing is guaranteed. Produced records can be lost or duplicated.
+* ``KafkaEgressBuilder#withAtLeastOnceProducerSemantics``: Stateful Functions will guarantee that nor records will be lost but they can be duplicated.
+* ``KafkaEgressBuilder#withExactlyOnceProducerSemantics``: Stateful Functions uses Kafka transactions to provide exactly-once semantics.
+
+Kafka Serializer
+""""""""""""""""
+
+The Kafka egress needs to know how to turn Java objects into binary data.
+The ``KafkaEgressSerializer`` allows users to specify such a schema.
+The ``ProducerRecord serialize(T out)`` method gets called for each message, allowing users to set a key, value, and other metadata.
+
+.. literalinclude:: ../../../src/main/java/com/ververica/statefun/docs/io/kafka/UserSerializer.java
+ :language: java
+ :lines: 16-
diff --git a/stateful-functions-docs/docs/api_concepts/io_module/source_sink.rst b/stateful-functions-docs/docs/api_concepts/io_module/source_sink.rst
new file mode 100644
index 00000000..af7f6940
--- /dev/null
+++ b/stateful-functions-docs/docs/api_concepts/io_module/source_sink.rst
@@ -0,0 +1,52 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+################
+Flink Connectors
+################
+
+The source-sink I/O module allows you to plug in existing, or custom, Flink connectors that are not already integrated into a dedicated I/O module.
+For details details of how to build a custom connector see the official {flink} `documentation `_.
+
+Dependency
+==========
+
+To use the Source/Sink I/O Module, please include the following dependency in your pom.
+
+.. code-block:: xml
+
+
+ com.ververica
+ stateful-functions-flink-io/artifactId>
+ {version}
+ provided
+
+
+Source Spec
+===========
+
+A source function spec creates an ingress from a Flink source function.
+
+.. literalinclude:: ../../../src/main/java/com/ververica/statefun/docs/io/flink/ModuleWithSourceSpec.java
+ :language: java
+ :lines: 16-
+
+Source Spec
+===========
+
+A sink function spec creates an ingress from a Flink sink function.
+
+.. literalinclude:: ../../../src/main/java/com/ververica/statefun/docs/io/flink/ModuleWithSinkSpec.java
+ :language: java
+ :lines: 16-
diff --git a/stateful-functions-docs/docs/api_concepts/match_functions.rst b/stateful-functions-docs/docs/api_concepts/match_functions.rst
new file mode 100644
index 00000000..396984e9
--- /dev/null
+++ b/stateful-functions-docs/docs/api_concepts/match_functions.rst
@@ -0,0 +1,68 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+###############
+Match Functions
+###############
+
+Stateful functions provide a powerful abstraction for working with events and state, allowing developers to build components that can react to any kind of message.
+Commonly, functions only need to handle a known set of message types, and the ``StatefulMatchFunction`` interface provides an opinionated solution to that problem.
+
+.. contents:: :local:
+
+Common Patterns
+===============
+
+Imagine a greeter function that wants to print specialized greeters depending on the type of input.
+
+.. literalinclude:: ../../src/main/java/com/ververica/statefun/docs/match/FnUserGreeter.java
+ :language: java
+ :lines: 16-
+
+Customers receive one standard message, and employees receive a personalized message depending on whether or not they are managers.
+The input is expected to be from a set of known classes.
+Certain variants perform some type specific checks and then call the appropriate action.
+
+Simple Match Function
+=====================
+
+Stateful match functions are an opinionated variant of stateful functions for precisely this pattern.
+Developers outline expected types, optional predicates, and well-typed business logic and let the system dispatch each input to the correct action.
+Variants are bound inside a ``configure`` method that is executed once the first time an instance is loaded.
+
+.. literalinclude:: ../../src/main/java/com/ververica/statefun/docs/match/FnMatchGreeter.java
+ :language: java
+ :lines: 16-
+
+Making Your Function Complete
+=============================
+
+Similar to the first example, match functions are partial by default and will throw an ``IllegalStateException`` on any input that does not match any branch.
+They can be made complete by providing an ``otherwise`` clause that serves as a catch-all for unmatched input, think of it as a default clause in a Java switch statement.
+The ``otherwise`` action takes its message as an untyped ``java.lang.Object``, allowing you to handle any unexpected messages.
+
+.. literalinclude:: ../../src/main/java/com/ververica/statefun/docs/match/FnMatchGreeterWithCatchAll.java
+ :language: java
+ :lines: 16-
+ :emphasize-lines: 15
+
+
+Action Resolution Order
+=======================
+
+Match functions will always match actions from most to least specific using the following resolution rules.
+
+First, find an action that matches the type and predicate. If two predicates will return true for a particular input, the one registered in the binder first wins.
+Next, search for an action that matches the type but does not have an associated predicate.
+Finally, if a catch-all exists, it will be executed or an ``IllegalStateException`` will be thrown.
diff --git a/stateful-functions-docs/docs/api_concepts/persistence.rst b/stateful-functions-docs/docs/api_concepts/persistence.rst
new file mode 100644
index 00000000..8e7594aa
--- /dev/null
+++ b/stateful-functions-docs/docs/api_concepts/persistence.rst
@@ -0,0 +1,80 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+###########
+Persistence
+###########
+
+Stateful Functions treats state as a first class citizen and so all stateful functions can easily define state that is automatically made fault tolerant by the runtime.
+
+.. contents:: :local:
+
+.. _persisted-value:
+
+Defining a Persistent Values
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+All stateful functions may contain state by merely defining one or more ``PersistedValue`` fields.
+A ``PersistedValue`` is defined by its name and the class of the type that it stores.
+The data is always scoped to a specific function type and identifier.
+Below is a stateful function that greets users based on the number of times they have been seen.
+
+.. warning::
+
+ All ``PersistedValue`` fields must be marked with an ``@Persisted`` annotation or they will not be made fault tolerant by the runtime.
+
+.. literalinclude:: ../../src/main/java/com/ververica/statefun/docs/FnUserGreeter.java
+ :language: java
+ :lines: 16-
+
+Persisted value comes with the right primitive methods to build powerful stateful applications.
+Calling ``PersistedValue#get`` will return the current value of an object stored in state, or ``null`` if nothing is set.
+Conversely, ``PersistedValue#set`` will update the value in state and ``PersistedValue#clear`` will delete the value from state.
+
+
+Supported Types
+^^^^^^^^^^^^^^^
+
+Stateful Functions applications are typically designed to run indefinitely or for long periods of time.
+As with all long-running services, the applications need to be updated to adapt to changing requirements.
+This goes the same for data schemas that the applications work against; they evolve along with the application.
+That is why the system limits the types that can be stored inside a ``PersistedValue`` to all Java primitives and complex types that support well defined schema migration semantics.
+
+.. note::
+
+ Schema evolution is supported naturally with protobuf and json, and the project is working on connecting it to Flink’s schema evolution capabilities.
+
+POJO types
+""""""""""
+
+Stateful Functions recognizes data types as a POJO type if the following conditions are fulfilled:
+
+* The class is public and standalone (no non-static inner class)
+* The class has a public no-argument constructor
+* All non-static, non-transient fields in the class (and all superclasses) are either public (and non-final) or have a public getter- and a setter- method that follows the Java beans naming conventions for getters and setters.
+
+Apache Avro
+"""""""""""
+
+Stateful Functions can store any Apache Avro class and fully supports evolving schema of Avro type state, as long as the schema change is considered compatible by `Avro’s rules for schema resolution `_.
+
+Protocol Buffers
+""""""""""""""""
+
+Stateful Functions can store any `Protocol Buffer `_ class and fully supports schema evolution as long as the schema change is considered compatible by ProtoBuff's rules for schema evolution.
+
+Json
+""""
+
+Stateful Functions can store any object that serializes as JSON.
diff --git a/stateful-functions-docs/docs/api_concepts/stateful_functions.rst b/stateful-functions-docs/docs/api_concepts/stateful_functions.rst
new file mode 100644
index 00000000..074916c2
--- /dev/null
+++ b/stateful-functions-docs/docs/api_concepts/stateful_functions.rst
@@ -0,0 +1,156 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+.. _stateful-functions:
+
+##################
+Stateful Functions
+##################
+
+Stateful functions are the building blocks of applications; they are atomic units of isolation, distribution, and persistence.
+As objects, they encapsulate the state of a single entity (e.g., a specific use, device, or session) and encode its behavior.
+Stateful functions can interact with each other, and external systems, through message passing.
+
+.. contents:: :local:
+
+Defining A Stateful Function
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A stateful function is any class that implements the ``StatefulFunction`` interface. The following is an example of a simple hello world function.
+
+.. literalinclude:: ../../src/main/java/com/ververica/statefun/docs/FnHelloWorld.java
+ :language: java
+ :lines: 16-
+
+Functions process each incoming message through their ``invoke`` method.
+Input's are untyped and passed through the system as a ``java.lang.Object`` so one function can potentially process multiple types of messages.
+
+The ``Context`` provides metadata about the current message and function, and is how you can call other functions or external systems.
+Functions are invoked based on a function type and unique identifier.
+
+Function Type's and Identifiers
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In a local environment, the address of an object is the same as a reference to it.
+But in a distributed system, objects may be spread across multiple machines and may or may not be active at any given moment.
+
+In Stateful Functions, function types and identifiers are used to reference specific stateful functions in the system.
+A function type is similar to a class in an object-oriented language; it declares what sort of function the address references.
+The id is a primary key and scopes the function call to a specific instance of the function type.
+
+Suppose a Stateful Functions application was tracking metadata about each user account on a website.
+The system would contain a user stateful function that accepts and responds to inputs about users and tracks relevant information.
+Stateful Functions will create one virtual instance of this stateful function for every user.
+Other functions can call the function for any particular user by the user function type and using the current user id as the instance identifier.
+
+.. literalinclude:: ../../src/main/java/com/ververica/statefun/docs/FnUser.java
+ :language: java
+ :emphasize-lines: 10
+ :lines: 16-
+
+.. literalinclude:: ../../src/main/java/com/ververica/statefun/docs/FnCaller.java
+ :language: java
+ :emphasize-lines: 14
+ :lines: 16-
+
+Virtual Functions
+^^^^^^^^^^^^^^^^^
+
+Functions are virtual, which means the system can support an infinite number of active functions while only requiring a static number of physical objects on the JVM heap.
+Any function can call any other without ever triggering an allocation.
+The system will make it appear as if functions are always available in-memory.
+
+Stateful Functions applications deploy on Apache Flink's horizontally parallel runtime.
+If the user function, seen above, is run on a Flink cluster with a parallelism of 10, then only ten objects will ever be allocated.
+Even if the application creates a billion user functions for a billion different users, memory usage will be stable.
+Those billion virtual functions will be evenly partitioned and run by the ten underlying objects.
+New object creation only occurs the first time a function of that type, regardless of id, is needed.
+
+Sending Delayed Messages
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+Functions are able to send messages on a delay so that they will arrive after some duration.
+Functions may even send themselves delayed messages that can serve as a callback.
+The delayed message is non-blocking so functions will continue to process records between the time a delayed message is sent and received.
+
+.. literalinclude:: ../../src/main/java/com/ververica/statefun/docs/delay/FnDelayedMessage.java
+ :language: java
+ :lines: 16-
+
+Completing Async Requests
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When interacting with external systems, such as a database or API, one needs to take care that communication delay with the external system does not dominate the application’s total work.
+Stateful Functions allows registering a java ``CompletableFuture`` that will resolve to a value at some point in the future.
+Future's are registered along with a metadata object that provides additional context about the caller.
+
+When the future completes, either successfully or exceptionally, the caller function type and id will be invoked with a ``AsyncOperationResult``.
+An asynchronous result can complete in one of three states:
+
+Success
+=======
+
+The asynchronous operation has succeeded, and the produced result can be obtained via ``AsyncOperationResult#value``.
+
+Failure
+=======
+
+The asynchronous operation has failed, and the cause can be obtained via ``AsyncOperationResult#throwable``.
+
+Unknown
+=======
+
+The stateful function was restarted, possibly on a different machine, before the ``CompletableFuture`` was completed, therefore it is unknown what is the status of the asynchronous operation.
+
+.. literalinclude:: ../../src/main/java/com/ververica/statefun/docs/async/EnrichmentFunction.java
+ :language: java
+ :lines: 16-
+
+Function Providers and Dependency Injection
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Stateful functions are created across a distributed cluster of nodes.
+``StatefulFunctionProvider`` is a factory class for creating a new instance of a stateful function the first time it is activated.
+
+.. literalinclude:: ../../src/main/java/com/ververica/statefun/docs/CustomProvider.java
+ :language: java
+ :lines: 16-
+
+Providers are called once per type on each parallel worker, not for each id.
+If a stateful function requires custom configurations, they can be defined inside a provider and passed to the functions' constructor.
+This is also where shared physical resources, such as a database connection, can be created that are used by any number of virtual functions.
+Now, tests can quickly provide mock, or test dependencies, without the need for complex dependency injection frameworks.
+
+.. literalinclude:: ../../src/main/java/com/ververica/statefun/docs/FunctionTest.java
+ :language: java
+ :lines: 16-
+
+.. _module:
+
+Stateful Function Modules
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Modules define a Stateful Functions application's top-level entry point and are where everything gets tied together.
+They offer a single configuration method where stateful functions are bound to the system.
+It also provides runtime configurations through the ``globalConfguration`` which is the union of all configurations in the applications ``flink-conf.yaml`` and any command line arguments passed in the form ``--key value``.
+
+.. literalinclude:: ../../src/main/java/com/ververica/statefun/docs/BasicFunctionModule.java
+ :language: java
+ :lines: 16-
+
+Modules leverage `Java’s Service Provider Interfaces (SPI) `_ for discovery.
+This means that every JAR should contain a file ``com.ververica.statefun.sdk.spi.StatefulFunctionModule`` in the ``META_INF/services`` resource directory that lists all available modules that it provides.
+
+.. code-block:: yaml
+
+ com.ververica.statefun.docs.BasicFunctionModule
diff --git a/stateful-functions-docs/docs/conf.py b/stateful-functions-docs/docs/conf.py
new file mode 100644
index 00000000..875768ea
--- /dev/null
+++ b/stateful-functions-docs/docs/conf.py
@@ -0,0 +1,242 @@
+# Copyright 2019 Ververica GmbH.
+#
+# 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.
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+import re
+
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = ['sphinx.ext.ifconfig']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# Warn about broken references
+nitpicky = True
+
+# General information about the project.
+project = u'Stateful Functions'
+copyright = u'2019, Ververica GmbH'
+author = u'Ververica GmbH'
+
+# Render it manually in the footer.html
+html_show_copyright = False
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = u'1.0-SNAPSHOT'
+# The full version, including alpha/beta/rc tags.
+release = u'1.0.0'
+# The latest Flink version supported by Stateful Functions
+flinkVersion = u'1.9'
+# The latest full Flink version supported by Stateful Functions
+flinkRelease = u'1.9.0'
+# The source code repository
+repo = "https://github.com/ververica/stateful-functions"
+
+# The examples directory
+example_source = repo + "/tree/master/stateful-functions-examples"
+
+# hide source link
+html_show_sourcelink = False
+# required for the search to work
+html_copy_source = True
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', "hidden"]
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'default'
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'sphinx_rtd_theme'
+
+theme_prev_next_buttons_location = 'Top'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#
+html_theme_options = {
+ 'titles_only': True,
+ 'canonical_url': 'https://statefun.io/',
+ 'display_version': False,
+ #'prev_next_buttons_location': None
+}
+
+html_show_sphinx = False
+
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Custom sidebar templates, must be a dictionary that maps document names
+# to template names.
+#
+# This is required for the alabaster theme
+# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
+html_sidebars = {
+ '**': [
+ 'relations.html', # needs 'show_related': True theme option to display
+ 'searchbox.html',
+ ]
+}
+
+# Custom CSS.
+html_css_files = [
+ 'css/customize-theme.css',
+]
+
+html_context = {
+ 'css_files': ['_static/css/customize-theme.css']
+}
+
+
+# -- Options for HTMLHelp output ------------------------------------------
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'StatefulFunctionsdoc'
+
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+ # The paper size ('letterpaper' or 'a4paper').
+ #
+ # 'papersize': 'letterpaper',
+
+ # The font size ('10pt', '11pt' or '12pt').
+ #
+ # 'pointsize': '10pt',
+
+ # Additional stuff for the LaTeX preamble.
+ #
+ # 'preamble': '',
+
+ # Latex figure (float) alignment
+ #
+ # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ (master_doc, 'StatefulFunctions.tex', u'Stateful Functions Documentation',
+ u'Ververica GmbH', 'manual'),
+]
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ (master_doc, 'stateful-functions', u'Stateful Functions Documentation',
+ [author], 1)
+]
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ (master_doc, 'Stateful Functions', u'Stateful Functions Documentation',
+ author, 'Stateful Functions', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+
+# -- Settings for sphinxcontrib-versioning --------------------------------
+scv_greatest_tag = True
+scv_show_banner = True
+scv_banner_greatest_tag = True
+scv_sort = ('semver', 'alpha')
+scv_whitelist_branches = (re.compile(r'^v\d+\.\d+$'),
+ re.compile(r'^v\d+\.\d+\.\d+$'),
+ 'latest')
+scv_whitelist_tags = (re.compile(r'^v\d+\.\d+$'),
+ re.compile(r'^v\d+\.\d+\.\d+$'),
+ 'latest')
+
+## Sphynx macros do not work inside of
+## code blocks, so we include these custom
+## replacements to autofill in versions in
+## all places.
+def customReplace(app, docname, source):
+ result = source[0]
+ for key in app.config.custom_replacements:
+ result = result.replace(key, app.config.custom_replacements[key])
+ source[0] = result
+
+custom_replacements = {
+ "{release}" : release,
+ "{version}" : version,
+ "{flink}" : u'Apache Flink®',
+ "{flinkVersion}" : flinkVersion,
+ "{flinkRelease}" : flinkRelease,
+ "{repo}" : repo,
+ "{examples}" : example_source
+}
+
+def setup(app):
+ app.add_config_value('custom_replacements', {}, True)
+ app.connect('source-read', customReplace)
diff --git a/stateful-functions-docs/docs/contribute.rst b/stateful-functions-docs/docs/contribute.rst
new file mode 100644
index 00000000..3db200e2
--- /dev/null
+++ b/stateful-functions-docs/docs/contribute.rst
@@ -0,0 +1,19 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+.. _contribute:
+
+#################
+How to Contribute
+#################
\ No newline at end of file
diff --git a/stateful-functions-docs/docs/deployment_operations/configurations.rst b/stateful-functions-docs/docs/deployment_operations/configurations.rst
new file mode 100644
index 00000000..81ce9193
--- /dev/null
+++ b/stateful-functions-docs/docs/deployment_operations/configurations.rst
@@ -0,0 +1,73 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+##############
+Configurations
+##############
+
+Stateful Functions includes a small number of SDK specific configurations.
+
+Command Line Arguments
+^^^^^^^^^^^^^^^^^^^^^^
+
+The following may be set as flags in the form ``--key value``.
+
+stateful-functions.state.checkpointing-interval-ms
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Description: Flink checkpoint interval in milliseconds, -1 to disable.
+
+Default: 30 s
+
+stateful-functions.message.serializer
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Description: The serializer to use for on the wire messages.
+
+Default: ``WITH_PROTOBUF_PAYLOADS``
+
+Options: ``WITH_PROTOBUF_PAYLOADS``, ``WITH_KRYO_PAYLOADS``, ``WITH_RAW_PAYLOADS``,
+
+stateful-functions.flink-job-name
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Description: The name to display in the Flink-UI
+
+Default: StatefulFunctions
+
+Flink Configuration Options
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+These may be set through your job's ``flink-conf.yaml``.
+
+stateful-functions.feedback.memory.bytes
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Description: The number of bytes to use for in memory buffering of the feedback channel, before spilling to disk.
+
+Default: 32 MB
+
+stateful-functions.state.multiplex-flink-state
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Description: Use a single MapState to multiplex different function types and persisted values instead of using a ValueState for each combination.
+
+Default: true
+
+.. note::
+
+ When using RocksDB each registered state is backed by a column family.
+ By default column family's require 2x64 MB of state.
+ This could prevent loading many functions with a small resource footprint.
+
diff --git a/stateful-functions-docs/docs/deployment_operations/index.rst b/stateful-functions-docs/docs/deployment_operations/index.rst
new file mode 100644
index 00000000..2cf401d4
--- /dev/null
+++ b/stateful-functions-docs/docs/deployment_operations/index.rst
@@ -0,0 +1,28 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+#########################
+Deployment and Operations
+#########################
+
+.. toctree::
+ :hidden:
+
+ packaging
+ configurations
+ metrics
+
+Stateful Functions is a framework built on top of the {flink} runtime, which means it inherits Flink's deployment and operations model, and there are no new concepts you need to learn.
+Read through the official `Apache Flink documentation `_ to learn how to run and maintain an application in production.
+The following pages outline Stateful Functions' specific configurations and metrics.
\ No newline at end of file
diff --git a/stateful-functions-docs/docs/deployment_operations/metrics.rst b/stateful-functions-docs/docs/deployment_operations/metrics.rst
new file mode 100644
index 00000000..7598d842
--- /dev/null
+++ b/stateful-functions-docs/docs/deployment_operations/metrics.rst
@@ -0,0 +1,59 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+#######
+Metrics
+#######
+
+Stateful Functions includes a number of SDK specific metrics, scoped on a per function basis, one level below operator scope.
+
+..in
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The number of incoming messages.
+
+..inRate
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The average number of incoming messages per second.
+
+..out-local
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The number of messages sent to a function on the same task slot.
+
+..out-localRate
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The average number of messages sent to a function on the same task slot per second.
+
+..out-remote
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The number of messages sent to a function on another same task slot.
+
+..out-remoteRate
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The average number of messages sent to a function on another task slot per second.
+
+..out-egress
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The number of messages sent to an egress.
+
+..out-egressRate
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The average number of messages sent to an egress per second.
+
+.writeback.produced
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The number of messages read from the write back channel.
+
+.writeback.produced
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The average number of messages read from the write back channel per second.
\ No newline at end of file
diff --git a/stateful-functions-docs/docs/deployment_operations/packaging.rst b/stateful-functions-docs/docs/deployment_operations/packaging.rst
new file mode 100644
index 00000000..b53f48d9
--- /dev/null
+++ b/stateful-functions-docs/docs/deployment_operations/packaging.rst
@@ -0,0 +1,61 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+########################
+Packaging For Deployment
+########################
+
+Stateful Functions applications can be packaged as either standalone applications or Flink jobs that can be submitted to a cluster.
+
+.. contents:: :local:
+
+Images
+^^^^^^
+
+The recommended deployment mode for Stateful Functions applications is to build a Docker image.
+This way, user code does not need to package any Apache Flink components.
+The provided base image allows teams to package their applications with all the necessary runtime dependencies quickly.
+
+Below is an example Dockerfile for building a Stateful Functions image for an application called ``stateful-functions-example``.
+
+.. code-block:: java
+
+ FROM stateful-functions
+
+ RUN mkdir -p /opt/stateful-functions/modules/stateful-functions-example
+ COPY target/stateful-functions-example*jar /opt/stateful-functions/modules/stateful-functions-example/
+
+Flink Jar
+^^^^^^^^^
+
+If you prefer to package your job to submit to an existing Flink cluster, simply include ``stateful-functions-flink-distribution`` as a dependency to your application.
+
+.. code-block:: xml
+
+
+ com.ververica
+ stateful-functions-flink-distribution
+ {version}
+
+
+It includes all of Stateful Functions' runtime dependencies and configure's the applications main entry-point.
+You do not need to take any action beyond adding the dependency to your pom.
+
+.. note::
+
+ The distribution must be bundled in your application fat jar so that it is on Flink's `user code class loader `_.
+
+.. code-block:: bash
+
+ ./bin/flink run ./stateful-functions-example.jar
\ No newline at end of file
diff --git a/stateful-functions-docs/docs/getting_started/index.rst b/stateful-functions-docs/docs/getting_started/index.rst
new file mode 100644
index 00000000..7879681c
--- /dev/null
+++ b/stateful-functions-docs/docs/getting_started/index.rst
@@ -0,0 +1,48 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+###############
+Getting Started
+###############
+
+.. toctree::
+ :hidden:
+
+ project_setup
+ walkthrough
+
+There are many ways to get started with Stateful Functions.
+Which one is the best for you depends on your goals and prior experience.
+Whether you prefer a more theoretical or a practical approach, we hope you’ll find this section helpful.
+
+.. contents:: :local:
+
+Learn By Doing
+==============
+
+If you prefer to learn by doing, start with our code :ref:`walkthrough `.
+It provides a step by step introduction to the API and guides you through real applications.
+
+Learn Concepts Step By Step
+===========================
+
+If you prefer to learn concepts step by step, start with our guide to :ref:`main concepts `.
+It will walk you through all the API's and concepts to build advanced stateful systems.
+
+
+Start A New Project
+===================
+
+The :ref:`project setup ` instructions show you how to create a project for a new Stateful Functions application in just a few steps.
+
diff --git a/stateful-functions-docs/docs/getting_started/project_setup.rst b/stateful-functions-docs/docs/getting_started/project_setup.rst
new file mode 100644
index 00000000..e12ccdee
--- /dev/null
+++ b/stateful-functions-docs/docs/getting_started/project_setup.rst
@@ -0,0 +1,87 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+.. _project_setup:
+
+#############
+Project Setup
+#############
+
+You can quickly get started building a Stateful Functions applications by adding the ``stateful-functions-sdk`` to an existing project or using the provided maven archetype.
+
+.. toctree::
+ :hidden:
+
+Dependency
+==========
+
+.. code-block:: xml
+
+
+ com.ververica
+ stateful-functions-sdk
+ {version}
+ provided
+
+
+
+Maven Archetype
+===============
+
+.. code-block:: bash
+
+ $ mvn archetype:generate \
+ -DarchetypeGroupId=com.ververica \
+ -DarchetypeArtifactId=stateful-functions-quickstart \
+ -DarchetypeVersion={version}
+
+This allows you to name your newly created project.
+It will interactively ask you for the groupId, artifactId, and package name.
+There will be a new directory with the same name as your artifact id.
+
+.. code-block:: bash
+
+ $ tree stateful-functions-quickstart/
+ stateful-functions-quickstart/
+ ├── Dockerfile
+ ├── pom.xml
+ └── src
+ └── main
+ ├── java
+ │ └── com
+ │ └── ververica
+ │ └── statefun
+ │ └── Module.java
+ └── resources
+ └── META-INF
+ └── services
+ └── com.ververica.statefun.sdk.spi.StatefulFunctionModule
+
+The project contains four files:
+
+* ``pom.xml``: A pom file with the basic dependencies to start building a Stateful Functions application.
+* ``Module``: The entry point for the application.
+* ``com.ververica.statefun.sdk.spi.StatefulFunctionModule``: A service entry for the runtime to find the module.
+* ``Dockerfile``: A Dockerfile to quickly build a Stateful Functions image ready to deploy.
+
+We recommend you import this project into your IDE to develop and test it.
+IntelliJ IDEA supports Maven projects out of the box.
+If you use Eclipse, the m2e plugin allows to import Maven projects.
+Some Eclipse bundles include that plugin by default, others require you to install it manually.
+
+Build Project
+=============
+
+If you want to build/package your project, go to your project directory and run the ``mvn clean package`` command.
+You will find a JAR file that contains your application, plus any libraries that you may have added as dependencies to the application: ``target/-.jar``.
diff --git a/stateful-functions-docs/docs/getting_started/walkthrough.rst b/stateful-functions-docs/docs/getting_started/walkthrough.rst
new file mode 100644
index 00000000..ef28a6b9
--- /dev/null
+++ b/stateful-functions-docs/docs/getting_started/walkthrough.rst
@@ -0,0 +1,204 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+.. _walkthrough:
+
+###########
+Walkthrough
+###########
+
+Like all great introductions in software, this walkthrough will start at the beginning: saying hello.
+The application will run a simple function that accepts a request and responds with a greeting.
+It will not attempt to cover all the complexities of application development, but instead focus on building a stateful function — which is where you will implement your business logic.
+
+.. contents:: :local:
+
+A Basic Hello
+^^^^^^^^^^^^^
+
+Greeting actions are triggered by consuming, routing and passing messages that are defined using ProtoBuf.
+
+.. code-block:: proto
+
+ syntax = "proto3";
+
+ message GreetRequest {
+ string who = 1;
+ }
+
+ message GreetResponse {
+ string who = 1;
+ string greeting = 2;
+ }
+
+
+Under the hood, messages are processed using :ref:`stateful functions `, by definition any class that implements the ``StatefulFunction`` interface.
+
+.. code-block:: java
+
+ package com.ververica.statefun.examples.greeter;
+
+ import com.ververica.statefun.sdk.Context;
+ import com.ververica.statefun.sdk.StatefulFunction;
+
+ public final class GreetFunction implements StatefulFunction {
+
+ @Override
+ public void invoke(Context context, Object input) {
+ GreetRequest greetMessage = (GreetRequest) input;
+
+ GreetResponse response = GreetResponse.newBuilder()
+ .setWho(greetMessage.getWho())
+ .setGreeting("Hello " + greetMessage.getWho())
+ .build();
+
+ context.send(GreetingConstants.GREETING_EGRESS_ID, response);
+ }
+ }
+
+This function takes in a request and sends a response to an external system (or :ref:`egress `).
+While this is nice, it does not show off the real power of stateful functions: handling state.
+
+A Stateful Hello
+^^^^^^^^^^^^^^^^
+
+Suppose you want to generate a personalized response for each user depending on how many times they have sent a request.
+
+.. code-block:: java
+
+ private static String greetText(String name, int seen) {
+ switch (seen) {
+ case 0:
+ return String.format("Hello %s !", name);
+ case 1:
+ return String.format("Hello again %s !", name);
+ case 2:
+ return String.format("Third times the charm! %s!", name);
+ case 3:
+ return String.format("Happy to see you once again %s !", name);
+ default:
+ return String.format("Hello at the %d-th time %s", seen + 1, name);
+ }
+
+Routing Messages
+================
+
+To send a user a personalized greeting, the system needs to keep track of how many times it has seen each user so far.
+Speaking in general terms, the simplest solution would be to create one function for every user and independently track the number of times they have been seen. Using most frameworks, this would be prohibitively expensive.
+However, stateful functions are virtual and do not consume any CPU or memory when not actively being invoked.
+That means your application can create as many functions as necessary — in this case, users — without worrying about resource consumption.
+
+Whenever data is consumed from an external system (or :ref:`ingress `), it is routed to a specific function based on a given function type and identifier.
+The function type represents the Class of function to be invoked, such as the Greeter function, while the identifier (``GreetRequest#getWho``) scopes the call to a specific virtual instance based on some key.
+
+.. code-block:: java
+
+ package com.ververica.statefun.examples.greeter;
+
+ import com.ververica.statefun.examples.kafka.generated.GreetRequest;
+ import com.ververica.statefun.sdk.io.Router;
+
+ final class GreetRouter implements Router {
+
+ @Override
+ public void route(GreetRequest message, Downstream downstream) {
+ downstream.forward(GreetingConstants.GREETER_FUNCTION_TYPE, message.getWho(), message);
+ }
+ }
+
+So, if a message for a user named John comes in, it will be shipped to John’s dedicated Greeter function.
+In case there is a following message for a user named Jane, a new instance of the Greeter function will be spawned.
+
+Persistence
+===========
+
+:ref:`Persisted value ` is a special data type that enables stateful functions to maintain fault-tolerant state scoped to their identifiers, so that each instance of a function can track state independently.
+To “remember” information across multiple greeting messages, you then need to associate a persisted value field (``count``) to the Greet function. For each user, functions can now track how many times they have been seen.
+
+.. code-block:: java
+
+ package com.ververica.statefun.examples.greeter;
+
+ import com.ververica.statefun.sdk.Context;
+ import com.ververica.statefun.sdk.StatefulFunction;
+ import com.ververica.statefun.sdk.annotations.Persisted;
+ import com.ververica.statefun.sdk.state.PersistedValue;
+
+ public final class GreetFunction implements StatefulFunction {
+
+ @Persisted
+ private final PersistedValue count = PersistedValue.of("count", Integer.class);
+
+ @Override
+ public void invoke(Context context, Object input) {
+ GreetRequest greetMessage = (GreetRequest) input;
+
+ GreetResponse response = computePersonalizedGreeting(greetMessage);
+
+ context.send(GreetingConstants.GREETING_EGRESS_ID, response);
+ }
+
+ private GreetResponse computePersonalizedGreeting(GreetRequest greetMessage) {
+ final String name = greetMessage.getWho();
+ final int seen = count.getOrDefault(0);
+ count.set(seen + 1);
+
+ String greeting = greetText(name, seen);
+
+ return GreetResponse.newBuilder()
+ .setWho(name)
+ .setGreeting(greeting)
+ .build();
+ }
+ }
+
+Each time a message is processed, the function computes a personalized message for that user.
+It reads and updates the number of times that user has been seen and sends a greeting to the egress.
+
+You can check the full code for the application described in this walkthrough `here <{examples}/stateful-functions-greeter-example>`_.
+In particular, take a look at the :ref:`module ` GreetingModule, which is the main entry point for the full application, to see how everything gets tied together.
+You can run this example locally using the provided Docker setup.
+
+.. code-block:: bash
+
+ $ docker-compose build
+ $ docker-compose up
+
+Then, send some messages to the topic "names", and observe what comes out of "greetings".
+
+.. code-block:: bash
+
+ $ KAFKA=$(docker ps -f "name=stateful-functions-greeter-example_kafka-broker_1" --format "{{.ID}}")
+ $ docker exec -it $KAFKA kafka-console-producer.sh \
+ --broker-list localhost:9092 \
+ --topic names
+
+.. code-block:: bash
+
+ $ KAFKA=$(docker ps -f "name=stateful-functions-greeter-example-broker_1" --format "{{.ID}}")
+ $ docker exec -it $KAFKA kafka-console-consumer.sh \
+ --bootstrap-server localhost:9092 \
+ --topic greetings
+
+.. image:: ../_static/images/greeter-function.gif
+ :align: center
+
+Want To Go Further?
+^^^^^^^^^^^^^^^^^^^
+
+This Greeter never forgets a user.
+Try and modify the function so that it will reset the ``count`` for any user that spends more than 60 seconds without interacting with the system.
+
+**Hint:** sending messages with a delay is supported, using ``Context#sendAfter``.
+How could you use this to implement a periodic check?
diff --git a/stateful-functions-docs/docs/index.rst b/stateful-functions-docs/docs/index.rst
new file mode 100644
index 00000000..ea9a6482
--- /dev/null
+++ b/stateful-functions-docs/docs/index.rst
@@ -0,0 +1,27 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+.. toctree::
+ :hidden:
+
+ overview/index
+ getting_started/index
+ api_concepts/index
+ deployment_operations/index
+ roadmap
+ contribute
+
+############################################
+Lightweight, Stateful Applications at Scale
+############################################
\ No newline at end of file
diff --git a/stateful-functions-docs/docs/overview/benefits_grid.html b/stateful-functions-docs/docs/overview/benefits_grid.html
new file mode 100644
index 00000000..798f1a71
--- /dev/null
+++ b/stateful-functions-docs/docs/overview/benefits_grid.html
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
State-First
+
+
+
+
+
Fault Tolerance
+
+
+
+
+
Async Execution
+
+
+
+
+
Minimal Footprint
+
+
+
+
+
\ No newline at end of file
diff --git a/stateful-functions-docs/docs/overview/consistency_model.rst b/stateful-functions-docs/docs/overview/consistency_model.rst
new file mode 100644
index 00000000..a1ec4ca8
--- /dev/null
+++ b/stateful-functions-docs/docs/overview/consistency_model.rst
@@ -0,0 +1,19 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+.. _consistency_model:
+
+#################
+Consistency Model
+#################
diff --git a/stateful-functions-docs/docs/overview/execution_model.rst b/stateful-functions-docs/docs/overview/execution_model.rst
new file mode 100644
index 00000000..146ce11d
--- /dev/null
+++ b/stateful-functions-docs/docs/overview/execution_model.rst
@@ -0,0 +1,19 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+.. _execution_model:
+
+###########################
+Distributed Execution Model
+###########################
diff --git a/stateful-functions-docs/docs/overview/index.rst b/stateful-functions-docs/docs/overview/index.rst
new file mode 100644
index 00000000..b23f54ab
--- /dev/null
+++ b/stateful-functions-docs/docs/overview/index.rst
@@ -0,0 +1,70 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+########
+Overview
+########
+
+**Stateful Functions** is a framework for building and orchestrating distributed stateful applications at scale that aims to solve some of the most significant operational challenges for developers: consistent state management, reliable interaction between distributed services and resource management.
+
+Key Benefits
+############
+
+.. raw:: html
+ :file: benefits_grid.html
+
+.. toctree::
+ :hidden:
+
+ stateful_functions
+ consistency_model
+ execution_model
+ tech_space
+
+Architecture Overview
+#####################
+
+The framework is based on :ref:`functions with persistent state ` that can share a pool of resources and interact arbitrarily with strong consistency guarantees. **Stateful Functions** uses a state-of-the-art runtime built on `Apache Flink `_ for distributed coordination, communication and state management.
+
+.. topic:: Stateful Functions API
+
+ The API is based on, well, stateful functions: small snippets of functionality that encapsulate business logic, somewhat similar to `actors `_. These functions exist as virtual instances — typically, one per entity in the application (for example, per user or stock item) — and are distributed across shards, making applications **scalable out-of-the-box**. Each function has persistent user-defined state in local variables and can message other functions (including itself!). This model makes **computing with state natural and uncomplicated**.
+
+.. topic:: Stream Processing Runtime
+
+ The runtime that powers **Stateful Functions** is based on stream processing with Apache Flink. State is kept in the stream processing engine, co-located with the computation, giving you fast and consistent state access. **State durability and fault tolerance** build on Flink’s robust `distributed snapshots model `_.
+
+.. topic:: Messaging Model
+
+ In **Stateful Functions** applications, everything is inherently strongly consistent: state modifications and messaging are integrated to create the effect of **consistent state and reliable messaging** within all interacting functions. Care about consistency needs to be taken only when interacting with the "outside world”. Event Ingresses and Egresses — optionally with transactional semantics — support interaction with the “outside world” via event streams.
+
+.. topic:: Consistency Model
+
+ Interactions flow between functions as event streams, in the style of message passing. Apache Flink’s snapshot-based fault tolerance model was extended to support cyclic data flow graphs while ensuring **exactly-once messaging guarantees** (yay!). As a result, you can have functions messaging each other arbitrarily, efficiently, and reliably.
+
+**Stateful Functions** splits compute and storage differently to the classical two-tier architecture: one ephemeral state/compute tier and a simple persistent blob storage tier. This approach **eliminates the need to provision additional databases, key-value stores or message brokers** and effectively offloads application state management from the shoulders of developers.
+
+Technology Space
+################
+
+Stateful Functions is heavily inspired by multiple existing technologies for stateless application development and orchestration. Other than Apache Flink, also Function-as-a-Service (FaaS) systems such as AWS Lambda and the `virtual stateful actor model `_ from Microsoft Orleans served as inspiration for this project.
+
+The framework is mostly implemented in Java and runs on the JVM. Extending the API to be cross-language compatible and support languages like Python, Go or NodeJS is part of the :ref:`Roadmap `.
+
+Get Involved!
+#############
+
+If you find these ideas interesting, give **Stateful Functions** a try and get involved! Check out the Getting Started section for introduction walkthroughs. File an issue if you have an idea how to improve things.
+
+The project is a work-in-progress. We believe we are off to a promising direction, but there is still a way to go to make all parts of this vision a reality. There are many possible ways to enhance **Stateful Functions** for different types of applications. Possibilities for enhancements to the runtime and operations will also evolve with the evolution of capabilities of Apache Flink.
\ No newline at end of file
diff --git a/stateful-functions-docs/docs/overview/stateful_functions.rst b/stateful-functions-docs/docs/overview/stateful_functions.rst
new file mode 100644
index 00000000..d654ff2e
--- /dev/null
+++ b/stateful-functions-docs/docs/overview/stateful_functions.rst
@@ -0,0 +1,19 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+.. _stateful_functions:
+
+##############################
+Stateful Functions Abstraction
+##############################
\ No newline at end of file
diff --git a/stateful-functions-docs/docs/overview/tech_space.rst b/stateful-functions-docs/docs/overview/tech_space.rst
new file mode 100644
index 00000000..f9726cd3
--- /dev/null
+++ b/stateful-functions-docs/docs/overview/tech_space.rst
@@ -0,0 +1,19 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+.. _tech_space:
+
+##########################
+How Does It Compare To...?
+##########################
diff --git a/stateful-functions-docs/docs/roadmap.rst b/stateful-functions-docs/docs/roadmap.rst
new file mode 100644
index 00000000..1249b1fe
--- /dev/null
+++ b/stateful-functions-docs/docs/roadmap.rst
@@ -0,0 +1,19 @@
+.. Copyright 2019 Ververica GmbH.
+
+ 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.
+
+.. _roadmap:
+
+#######################
+Roadmap and Future Work
+#######################
\ No newline at end of file
diff --git a/stateful-functions-docs/images/stateful_functions_logo.png b/stateful-functions-docs/images/stateful_functions_logo.png
new file mode 100644
index 00000000..677c4d73
Binary files /dev/null and b/stateful-functions-docs/images/stateful_functions_logo.png differ
diff --git a/stateful-functions-docs/pom.xml b/stateful-functions-docs/pom.xml
new file mode 100644
index 00000000..addfde4b
--- /dev/null
+++ b/stateful-functions-docs/pom.xml
@@ -0,0 +1,143 @@
+
+
+
+
+ 4.0.0
+
+
+ stateful-functions-parent
+ com.ververica
+ 1.0-SNAPSHOT
+
+
+ stateful-functions-docs
+ stateful-functions-docs
+ 1.0-SNAPSHOT
+ pom
+
+
+
+ com.ververica
+ stateful-functions-sdk
+ ${project.version}
+ provided
+
+
+ com.ververica
+ stateful-functions-flink-io
+ ${project.version}
+ provided
+
+
+ com.ververica
+ stateful-functions-kafka-io
+ ${project.version}
+ provided
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.9.6
+ provided
+
+
+ junit
+ junit
+ 4.12
+ provided
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.12.4
+
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 2.6
+
+
+ default-jar
+ none
+
+
+
+
+ org.apache.maven.plugins
+ maven-install-plugin
+ 2.5.2
+
+
+ default-install
+ none
+
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.8.2
+
+
+ default-deploy
+ none
+
+
+
+
+ org.apache.rat
+ apache-rat-plugin
+ 0.13
+
+
+ verify
+
+ check
+
+
+
+
+
+
+ **/.*/**
+ **/*.prefs
+ **/*.log
+
+ **/README.md
+ .github/**
+
+ **/target/**
+ **/_build/**
+ requirements.txt
+ runtime.txt
+ /docs/_templates/**
+
+ **/generated/**
+
+
+
+
+
+
\ No newline at end of file
diff --git a/stateful-functions-docs/requirements.txt b/stateful-functions-docs/requirements.txt
new file mode 100644
index 00000000..97882e2b
--- /dev/null
+++ b/stateful-functions-docs/requirements.txt
@@ -0,0 +1,5 @@
+six==1.11.0
+Sphinx==1.7.9
+sphinx-autobuild==0.7.1
+sphinx_rtd_theme==0.4.1
+sphinxcontrib-versioning==2.2.1
diff --git a/stateful-functions-docs/runtime.txt b/stateful-functions-docs/runtime.txt
new file mode 100644
index 00000000..475ba515
--- /dev/null
+++ b/stateful-functions-docs/runtime.txt
@@ -0,0 +1 @@
+3.7
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/BasicFunctionModule.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/BasicFunctionModule.java
new file mode 100644
index 00000000..2ccd9064
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/BasicFunctionModule.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs;
+
+import com.ververica.statefun.sdk.spi.StatefulFunctionModule;
+import java.util.Map;
+
+public class BasicFunctionModule implements StatefulFunctionModule {
+
+ public void configure(Map globalConfiguration, Binder binder) {
+
+ // Declare the user function and bind it to its type
+ binder.bindFunctionProvider(FnWithDependency.TYPE, new CustomProvider());
+
+ // Stateful functions that do not require any configuration
+ // can declare their provider using java 8 lambda syntax
+ binder.bindFunctionProvider(FnUser.TYPE, unused -> new FnUser());
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/CustomProvider.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/CustomProvider.java
new file mode 100644
index 00000000..c7a0cf50
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/CustomProvider.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs;
+
+import com.ververica.statefun.docs.dependency.ProductionDependency;
+import com.ververica.statefun.docs.dependency.RuntimeDependency;
+import com.ververica.statefun.sdk.FunctionType;
+import com.ververica.statefun.sdk.StatefulFunction;
+import com.ververica.statefun.sdk.StatefulFunctionProvider;
+
+public class CustomProvider implements StatefulFunctionProvider {
+
+ public StatefulFunction functionOfType(FunctionType type) {
+ RuntimeDependency dependency = new ProductionDependency();
+ return new FnWithDependency(dependency);
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/FnCaller.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/FnCaller.java
new file mode 100644
index 00000000..11574081
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/FnCaller.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs;
+
+import com.ververica.statefun.sdk.Context;
+import com.ververica.statefun.sdk.FunctionType;
+import com.ververica.statefun.sdk.StatefulFunction;
+
+/** A simple stateful function that sends a message to the user with id "user1" */
+public class FnCaller implements StatefulFunction {
+
+ public static final FunctionType TYPE = new FunctionType("ververica", "caller");
+
+ @Override
+ public void invoke(Context context, Object input) {
+ context.send(FnUser.TYPE, "user1", new MyUserMessage());
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/FnHelloWorld.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/FnHelloWorld.java
new file mode 100644
index 00000000..260dbb52
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/FnHelloWorld.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs;
+
+import com.ververica.statefun.sdk.Context;
+import com.ververica.statefun.sdk.StatefulFunction;
+
+public class FnHelloWorld implements StatefulFunction {
+
+ @Override
+ public void invoke(Context context, Object input) {
+ System.out.println("Hello " + input.toString());
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/FnUser.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/FnUser.java
new file mode 100644
index 00000000..0e79ebad
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/FnUser.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs;
+
+import com.ververica.statefun.sdk.Context;
+import com.ververica.statefun.sdk.FunctionType;
+import com.ververica.statefun.sdk.StatefulFunction;
+
+/** A simple function that says hello. */
+public class FnUser implements StatefulFunction {
+
+ public static final FunctionType TYPE = new FunctionType("ververica", "user");
+
+ @Override
+ public void invoke(Context context, Object message) {
+ String userId = context.self().id();
+ System.out.println("Hello " + userId);
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/FnUserGreeter.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/FnUserGreeter.java
new file mode 100644
index 00000000..3e85abbf
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/FnUserGreeter.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs;
+
+import com.ververica.statefun.sdk.Context;
+import com.ververica.statefun.sdk.FunctionType;
+import com.ververica.statefun.sdk.StatefulFunction;
+import com.ververica.statefun.sdk.annotations.Persisted;
+import com.ververica.statefun.sdk.state.PersistedValue;
+
+public class FnUserGreeter implements StatefulFunction {
+
+ public static FunctionType TYPE = new FunctionType("ververica", "greeter");
+
+ @Persisted
+ private final PersistedValue count = PersistedValue.of("count", Integer.class);
+
+ public void invoke(Context context, Object input) {
+ String userId = context.self().id();
+ int seen = count.getOrDefault(0);
+
+ switch (seen) {
+ case 0:
+ System.out.println(String.format("Hello %s!", userId));
+ break;
+ case 1:
+ System.out.println("Hello Again!");
+ break;
+ case 2:
+ System.out.println("Third time is the charm :)");
+ break;
+ default:
+ System.out.println(String.format("Hello for the %d-th time", seen + 1));
+ }
+
+ count.set(seen + 1);
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/FnWithDependency.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/FnWithDependency.java
new file mode 100644
index 00000000..77f5b51f
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/FnWithDependency.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs;
+
+import com.ververica.statefun.docs.dependency.RuntimeDependency;
+import com.ververica.statefun.sdk.Context;
+import com.ververica.statefun.sdk.FunctionType;
+import com.ververica.statefun.sdk.StatefulFunction;
+
+public class FnWithDependency implements StatefulFunction {
+
+ static final FunctionType TYPE = new FunctionType("ververica", "fn-with-dep");
+
+ private final RuntimeDependency dependency;
+
+ public FnWithDependency(RuntimeDependency dependency) {
+ this.dependency = dependency;
+ }
+
+ @Override
+ public void invoke(Context context, Object input) {}
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/FunctionTest.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/FunctionTest.java
new file mode 100644
index 00000000..9f614c90
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/FunctionTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs;
+
+import com.ververica.statefun.docs.dependency.RuntimeDependency;
+import com.ververica.statefun.docs.dependency.TestDependency;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class FunctionTest {
+
+ @Test
+ public void testFunctionWithCustomDependency() {
+ RuntimeDependency dependency = new TestDependency();
+ FnWithDependency function = new FnWithDependency(dependency);
+
+ Assert.assertEquals("It appears math is broken", 1 + 1, 2);
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/MyUserMessage.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/MyUserMessage.java
new file mode 100644
index 00000000..1466e630
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/MyUserMessage.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs;
+
+class MyUserMessage {}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/async/EnrichmentFunction.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/async/EnrichmentFunction.java
new file mode 100644
index 00000000..9f65dd0c
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/async/EnrichmentFunction.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.async;
+
+import com.ververica.statefun.sdk.AsyncOperationResult;
+import com.ververica.statefun.sdk.Context;
+import com.ververica.statefun.sdk.StatefulFunction;
+import java.util.concurrent.CompletableFuture;
+
+@SuppressWarnings("unchecked")
+public class EnrichmentFunction implements StatefulFunction {
+
+ private final QueryService client;
+
+ public EnrichmentFunction(QueryService client) {
+ this.client = client;
+ }
+
+ @Override
+ public void invoke(Context context, Object input) {
+ if (input instanceof User) {
+ onUser(context, (User) input);
+ } else if (input instanceof AsyncOperationResult) {
+ onAsyncResult((AsyncOperationResult) input);
+ }
+ }
+
+ private void onUser(Context context, User user) {
+ CompletableFuture future = client.getDataAsync(user.getUserId());
+ context.registerAsyncOperation(user, future);
+ }
+
+ private void onAsyncResult(AsyncOperationResult result) {
+ if (result.successful()) {
+ User metadata = result.metadata();
+ UserEnrichment value = result.value();
+ System.out.println(String.format("Successfully completed future: %s %s", metadata, value));
+ } else if (result.failure()) {
+ System.out.println(String.format("Something has gone terribly wrong %s", result.throwable()));
+ } else {
+ System.out.println("Not sure what happened, maybe retry");
+ }
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/async/QueryService.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/async/QueryService.java
new file mode 100644
index 00000000..acd536b2
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/async/QueryService.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.async;
+
+import java.util.concurrent.CompletableFuture;
+
+interface QueryService {
+ CompletableFuture getDataAsync(String userId);
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/async/User.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/async/User.java
new file mode 100644
index 00000000..b7fcde54
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/async/User.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.async;
+
+final class User {
+ String getUserId() {
+ return "";
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/async/UserEnrichment.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/async/UserEnrichment.java
new file mode 100644
index 00000000..a37beb8a
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/async/UserEnrichment.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.async;
+
+final class UserEnrichment {}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/delay/DelayedMessage.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/delay/DelayedMessage.java
new file mode 100644
index 00000000..f2e6af26
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/delay/DelayedMessage.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.delay;
+
+class DelayedMessage {}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/delay/FnDelayedMessage.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/delay/FnDelayedMessage.java
new file mode 100644
index 00000000..2f18e7da
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/delay/FnDelayedMessage.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.delay;
+
+import com.ververica.statefun.sdk.Context;
+import com.ververica.statefun.sdk.StatefulFunction;
+import java.time.Duration;
+
+public class FnDelayedMessage implements StatefulFunction {
+
+ @Override
+ public void invoke(Context context, Object input) {
+ if (input instanceof Message) {
+ System.out.println("Hello");
+ context.sendAfter(Duration.ofMinutes(1), context.self(), new DelayedMessage());
+ }
+
+ if (input instanceof DelayedMessage) {
+ System.out.println("Welcome to the future!");
+ }
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/delay/Message.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/delay/Message.java
new file mode 100644
index 00000000..b0d92311
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/delay/Message.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.delay;
+
+class Message {}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/dependency/ProductionDependency.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/dependency/ProductionDependency.java
new file mode 100644
index 00000000..649be2a8
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/dependency/ProductionDependency.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.dependency;
+
+public class ProductionDependency implements RuntimeDependency {}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/dependency/RuntimeDependency.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/dependency/RuntimeDependency.java
new file mode 100644
index 00000000..21f5d801
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/dependency/RuntimeDependency.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.dependency;
+
+public interface RuntimeDependency {}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/dependency/TestDependency.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/dependency/TestDependency.java
new file mode 100644
index 00000000..8166f937
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/dependency/TestDependency.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.dependency;
+
+public class TestDependency implements RuntimeDependency {}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/MissingImplementationException.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/MissingImplementationException.java
new file mode 100644
index 00000000..4d2f71b0
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/MissingImplementationException.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io;
+
+public class MissingImplementationException extends RuntimeException {
+
+ public MissingImplementationException(String message) {
+ super(message);
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/custom/MyEgressSpec.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/custom/MyEgressSpec.java
new file mode 100644
index 00000000..6616550f
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/custom/MyEgressSpec.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.custom;
+
+import com.ververica.statefun.sdk.EgressType;
+import com.ververica.statefun.sdk.io.EgressIdentifier;
+import com.ververica.statefun.sdk.io.EgressSpec;
+
+public class MyEgressSpec implements EgressSpec {
+
+ public static final EgressType TYPE = new EgressType("ververica", "my-egress");
+
+ private final EgressIdentifier identifier;
+
+ public MyEgressSpec(EgressIdentifier identifier) {
+ this.identifier = identifier;
+ }
+
+ @Override
+ public EgressType type() {
+ return TYPE;
+ }
+
+ @Override
+ public EgressIdentifier id() {
+ return identifier;
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/custom/MyIngressSpec.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/custom/MyIngressSpec.java
new file mode 100644
index 00000000..6911355e
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/custom/MyIngressSpec.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.custom;
+
+import com.ververica.statefun.sdk.IngressType;
+import com.ververica.statefun.sdk.io.IngressIdentifier;
+import com.ververica.statefun.sdk.io.IngressSpec;
+
+public class MyIngressSpec implements IngressSpec {
+
+ public static final IngressType TYPE = new IngressType("ververica", "my-ingress");
+
+ private final IngressIdentifier identifier;
+
+ public MyIngressSpec(IngressIdentifier identifier) {
+ this.identifier = identifier;
+ }
+
+ @Override
+ public IngressType type() {
+ return TYPE;
+ }
+
+ @Override
+ public IngressIdentifier id() {
+ return identifier;
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/custom/flink/MyFlinkIoModule.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/custom/flink/MyFlinkIoModule.java
new file mode 100644
index 00000000..afe5ed02
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/custom/flink/MyFlinkIoModule.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.custom.flink;
+
+import com.ververica.statefun.docs.io.custom.MyEgressSpec;
+import com.ververica.statefun.docs.io.custom.MyIngressSpec;
+import com.ververica.statefun.flink.io.spi.FlinkIoModule;
+import java.util.Map;
+
+public final class MyFlinkIoModule implements FlinkIoModule {
+
+ @Override
+ public void configure(Map globalConfiguration, Binder binder) {
+ binder.bindSourceProvider(MyIngressSpec.TYPE, new MySourceProvider());
+ binder.bindSinkProvider(MyEgressSpec.TYPE, new MySinkProvider());
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/custom/flink/MySinkProvider.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/custom/flink/MySinkProvider.java
new file mode 100644
index 00000000..b02bfd13
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/custom/flink/MySinkProvider.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.custom.flink;
+
+import com.ververica.statefun.docs.io.custom.MyEgressSpec;
+import com.ververica.statefun.docs.io.custom.flink.sink.MySinkFunction;
+import com.ververica.statefun.flink.io.spi.SinkProvider;
+import com.ververica.statefun.sdk.io.EgressSpec;
+import org.apache.flink.streaming.api.functions.sink.SinkFunction;
+
+public class MySinkProvider implements SinkProvider {
+
+ @Override
+ public SinkFunction forSpec(EgressSpec egressSpec) {
+ MyEgressSpec spec = asMyEgressSpec(egressSpec);
+ MySinkFunction sink = new MySinkFunction<>();
+
+ // configure the sink based on the provided spec
+ return sink;
+ }
+
+ private static MyEgressSpec asMyEgressSpec(EgressSpec egressSpec) {
+ if (egressSpec == null) {
+ throw new NullPointerException("Unable to translate a NULL spec");
+ }
+
+ if (egressSpec instanceof MyEgressSpec) {
+ return (MyEgressSpec) egressSpec;
+ }
+
+ throw new IllegalArgumentException(String.format("Wrong type %s", egressSpec.type()));
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/custom/flink/MySourceProvider.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/custom/flink/MySourceProvider.java
new file mode 100644
index 00000000..56cf65a1
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/custom/flink/MySourceProvider.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.custom.flink;
+
+import com.ververica.statefun.docs.io.custom.MyIngressSpec;
+import com.ververica.statefun.docs.io.custom.flink.source.MySourceFunction;
+import com.ververica.statefun.flink.io.spi.SourceProvider;
+import com.ververica.statefun.sdk.io.IngressSpec;
+import org.apache.flink.streaming.api.functions.source.SourceFunction;
+
+public class MySourceProvider implements SourceProvider {
+
+ @Override
+ public SourceFunction forSpec(IngressSpec ingressSpec) {
+ MyIngressSpec spec = asMyIngressSpec(ingressSpec);
+ MySourceFunction source = new MySourceFunction();
+
+ // configure the source based on the provided spec
+ return source;
+ }
+
+ private static MyIngressSpec asMyIngressSpec(IngressSpec ingressSpec) {
+ if (ingressSpec == null) {
+ throw new NullPointerException("Unable to translate a NULL spec");
+ }
+
+ if (ingressSpec instanceof MyIngressSpec) {
+ return (MyIngressSpec) ingressSpec;
+ }
+
+ throw new IllegalArgumentException(String.format("Wrong type %s", ingressSpec.type()));
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/custom/flink/sink/MySinkFunction.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/custom/flink/sink/MySinkFunction.java
new file mode 100644
index 00000000..53f2e9c1
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/custom/flink/sink/MySinkFunction.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.custom.flink.sink;
+
+import org.apache.flink.streaming.api.functions.sink.SinkFunction;
+
+public class MySinkFunction implements SinkFunction {
+
+ @Override
+ public void invoke(T value, Context context) {}
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/custom/flink/source/MySourceFunction.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/custom/flink/source/MySourceFunction.java
new file mode 100644
index 00000000..fd1e4292
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/custom/flink/source/MySourceFunction.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.custom.flink.source;
+
+import org.apache.flink.streaming.api.functions.source.SourceFunction;
+
+public class MySourceFunction implements SourceFunction {
+
+ @Override
+ public void run(SourceContext ctx) throws Exception {}
+
+ @Override
+ public void cancel() {}
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/egress/FnOutputting.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/egress/FnOutputting.java
new file mode 100644
index 00000000..1dd5f61c
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/egress/FnOutputting.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.egress;
+
+import com.ververica.statefun.docs.models.User;
+import com.ververica.statefun.sdk.Context;
+import com.ververica.statefun.sdk.StatefulFunction;
+
+/** A simple function that outputs messages to an egress. */
+public class FnOutputting implements StatefulFunction {
+
+ @Override
+ public void invoke(Context context, Object input) {
+ context.send(Identifiers.EGRESS, new User());
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/egress/Identifiers.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/egress/Identifiers.java
new file mode 100644
index 00000000..d744b835
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/egress/Identifiers.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.egress;
+
+import com.ververica.statefun.docs.models.User;
+import com.ververica.statefun.sdk.io.EgressIdentifier;
+
+public final class Identifiers {
+
+ public static final EgressIdentifier EGRESS =
+ new EgressIdentifier<>("ververica", "egress", User.class);
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/egress/ModuleWithEgress.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/egress/ModuleWithEgress.java
new file mode 100644
index 00000000..88264c45
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/egress/ModuleWithEgress.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.egress;
+
+import com.ververica.statefun.docs.io.MissingImplementationException;
+import com.ververica.statefun.docs.models.User;
+import com.ververica.statefun.sdk.io.EgressIdentifier;
+import com.ververica.statefun.sdk.io.EgressSpec;
+import com.ververica.statefun.sdk.spi.StatefulFunctionModule;
+import java.util.Map;
+
+public class ModuleWithEgress implements StatefulFunctionModule {
+
+ @Override
+ public void configure(Map globalConfiguration, Binder binder) {
+ EgressSpec spec = createEgress(Identifiers.EGRESS);
+ binder.bindEgress(spec);
+ }
+
+ public EgressSpec createEgress(EgressIdentifier identifier) {
+ throw new MissingImplementationException("Replace with your specific egress");
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/flink/FlinkSink.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/flink/FlinkSink.java
new file mode 100644
index 00000000..ef61ebcb
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/flink/FlinkSink.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.flink;
+
+import org.apache.flink.streaming.api.functions.sink.SinkFunction;
+
+public class FlinkSink implements SinkFunction {}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/flink/FlinkSource.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/flink/FlinkSource.java
new file mode 100644
index 00000000..0e34e3b7
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/flink/FlinkSource.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.flink;
+
+import org.apache.flink.streaming.api.functions.source.SourceFunction;
+
+public class FlinkSource implements SourceFunction {
+ @Override
+ public void run(SourceContext ctx) throws Exception {}
+
+ @Override
+ public void cancel() {}
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/flink/ModuleWithSinkSpec.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/flink/ModuleWithSinkSpec.java
new file mode 100644
index 00000000..e0cdba52
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/flink/ModuleWithSinkSpec.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.flink;
+
+import com.ververica.statefun.docs.models.User;
+import com.ververica.statefun.flink.io.datastream.SinkFunctionSpec;
+import com.ververica.statefun.sdk.io.EgressIdentifier;
+import com.ververica.statefun.sdk.io.EgressSpec;
+import com.ververica.statefun.sdk.spi.StatefulFunctionModule;
+import java.util.Map;
+
+public class ModuleWithSinkSpec implements StatefulFunctionModule {
+
+ @Override
+ public void configure(Map globalConfiguration, Binder binder) {
+ EgressIdentifier id = new EgressIdentifier<>("ververica", "user", User.class);
+ EgressSpec spec = new SinkFunctionSpec<>(id, new FlinkSink<>());
+ binder.bindEgress(spec);
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/flink/ModuleWithSourceSpec.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/flink/ModuleWithSourceSpec.java
new file mode 100644
index 00000000..60ab7cdc
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/flink/ModuleWithSourceSpec.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.flink;
+
+import com.ververica.statefun.docs.models.User;
+import com.ververica.statefun.flink.io.datastream.SourceFunctionSpec;
+import com.ververica.statefun.sdk.io.IngressIdentifier;
+import com.ververica.statefun.sdk.io.IngressSpec;
+import com.ververica.statefun.sdk.spi.StatefulFunctionModule;
+import java.util.Map;
+
+public class ModuleWithSourceSpec implements StatefulFunctionModule {
+
+ @Override
+ public void configure(Map globalConfiguration, Binder binder) {
+ IngressIdentifier id = new IngressIdentifier<>(User.class, "ververica", "users");
+ IngressSpec spec = new SourceFunctionSpec<>(id, new FlinkSource<>());
+ binder.bindIngress(spec);
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/ingress/Identifiers.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/ingress/Identifiers.java
new file mode 100644
index 00000000..8b921267
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/ingress/Identifiers.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.ingress;
+
+import com.ververica.statefun.docs.models.User;
+import com.ververica.statefun.sdk.io.IngressIdentifier;
+
+public final class Identifiers {
+
+ public static final IngressIdentifier INGRESS =
+ new IngressIdentifier<>(User.class, "ververica", "user-ingress");
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/ingress/ModuleWithIngress.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/ingress/ModuleWithIngress.java
new file mode 100644
index 00000000..2a7d2502
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/ingress/ModuleWithIngress.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.ingress;
+
+import com.ververica.statefun.docs.io.MissingImplementationException;
+import com.ververica.statefun.docs.models.User;
+import com.ververica.statefun.sdk.io.IngressIdentifier;
+import com.ververica.statefun.sdk.io.IngressSpec;
+import com.ververica.statefun.sdk.spi.StatefulFunctionModule;
+import java.util.Map;
+
+public class ModuleWithIngress implements StatefulFunctionModule {
+
+ @Override
+ public void configure(Map globalConfiguration, Binder binder) {
+ IngressSpec spec = createIngress(Identifiers.INGRESS);
+ binder.bindIngress(spec);
+ }
+
+ private IngressSpec createIngress(IngressIdentifier identifier) {
+ throw new MissingImplementationException("Replace with your specific ingress");
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/ingress/ModuleWithRouter.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/ingress/ModuleWithRouter.java
new file mode 100644
index 00000000..e34bd620
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/ingress/ModuleWithRouter.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.ingress;
+
+import com.ververica.statefun.docs.models.User;
+import com.ververica.statefun.sdk.io.IngressIdentifier;
+import com.ververica.statefun.sdk.io.IngressSpec;
+import com.ververica.statefun.sdk.io.Router;
+import com.ververica.statefun.sdk.spi.StatefulFunctionModule;
+import java.util.Map;
+
+public class ModuleWithRouter implements StatefulFunctionModule {
+
+ @Override
+ public void configure(Map globalConfiguration, Binder binder) {
+ IngressSpec spec = createIngressSpec(Identifiers.INGRESS);
+ Router router = new UserRouter();
+
+ binder.bindIngress(spec);
+ binder.bindIngressRouter(Identifiers.INGRESS, router);
+ }
+
+ private IngressSpec createIngressSpec(IngressIdentifier identifier) {
+ throw new MissingImplementationException("Replace with your specific ingress");
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/ingress/UserRouter.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/ingress/UserRouter.java
new file mode 100644
index 00000000..b155a8d3
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/ingress/UserRouter.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.ingress;
+
+import com.ververica.statefun.docs.FnUser;
+import com.ververica.statefun.docs.models.User;
+import com.ververica.statefun.sdk.io.Router;
+
+public class UserRouter implements Router {
+
+ @Override
+ public void route(User message, Downstream downstream) {
+ downstream.forward(FnUser.TYPE, message.getUserId(), message);
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/kafka/EgressSpecs.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/kafka/EgressSpecs.java
new file mode 100644
index 00000000..95f5af92
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/kafka/EgressSpecs.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.kafka;
+
+import com.ververica.statefun.docs.models.User;
+import com.ververica.statefun.sdk.io.EgressIdentifier;
+import com.ververica.statefun.sdk.io.EgressSpec;
+import com.ververica.statefun.sdk.kafka.KafkaEgressBuilder;
+
+public class EgressSpecs {
+
+ public static final EgressIdentifier ID =
+ new EgressIdentifier<>("ververica", "output-egress", User.class);
+
+ public static final EgressSpec kafkaEgress =
+ KafkaEgressBuilder.forIdentifier(ID)
+ .withKafkaAddress("localhost:9092")
+ .withSerializer(UserSerializer.class)
+ .build();
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/kafka/IngressSpecs.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/kafka/IngressSpecs.java
new file mode 100644
index 00000000..49a4b7c9
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/kafka/IngressSpecs.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.kafka;
+
+import com.ververica.statefun.docs.models.User;
+import com.ververica.statefun.sdk.io.IngressIdentifier;
+import com.ververica.statefun.sdk.io.IngressSpec;
+import com.ververica.statefun.sdk.kafka.KafkaIngressBuilder;
+import org.apache.kafka.clients.consumer.ConsumerConfig;
+
+public class IngressSpecs {
+
+ public static final IngressIdentifier ID =
+ new IngressIdentifier<>(User.class, "ververica", "input-ingress");
+
+ public static final IngressSpec kafkaIngress =
+ KafkaIngressBuilder.forIdentifier(ID)
+ .withKafkaAddress("localhost:9092")
+ .withTopic("my-topic")
+ .withDeserializer(UserDeserializer.class)
+ .withProperty(ConsumerConfig.GROUP_ID_CONFIG, "greetings")
+ .build();
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/kafka/UserDeserializer.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/kafka/UserDeserializer.java
new file mode 100644
index 00000000..f935d39c
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/kafka/UserDeserializer.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.kafka;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.ververica.statefun.docs.models.User;
+import com.ververica.statefun.sdk.kafka.KafkaIngressDeserializer;
+import java.io.IOException;
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class UserDeserializer implements KafkaIngressDeserializer {
+
+ private static Logger LOG = LoggerFactory.getLogger(UserDeserializer.class);
+
+ private final ObjectMapper mapper = new ObjectMapper();
+
+ @Override
+ public User deserialize(ConsumerRecord input) {
+ try {
+ return mapper.readValue(input.value(), User.class);
+ } catch (IOException e) {
+ LOG.debug("Failed to deserialize record", e);
+ return null;
+ }
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/kafka/UserSerializer.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/kafka/UserSerializer.java
new file mode 100644
index 00000000..df224b14
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/io/kafka/UserSerializer.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.io.kafka;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.ververica.statefun.docs.models.User;
+import com.ververica.statefun.sdk.kafka.KafkaEgressSerializer;
+import org.apache.kafka.clients.producer.ProducerRecord;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class UserSerializer implements KafkaEgressSerializer {
+
+ private static final Logger LOG = LoggerFactory.getLogger(UserSerializer.class);
+
+ private static final String TOPIC = "user-topic";
+
+ private final ObjectMapper mapper = new ObjectMapper();
+
+ @Override
+ public ProducerRecord serialize(User user) {
+ try {
+ byte[] key = user.getUserId().getBytes();
+ byte[] value = mapper.writeValueAsBytes(user);
+
+ return new ProducerRecord<>(TOPIC, key, value);
+ } catch (JsonProcessingException e) {
+ LOG.info("Failed to serializer user", e);
+ return null;
+ }
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/match/Customer.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/match/Customer.java
new file mode 100644
index 00000000..fd63c5f5
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/match/Customer.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.match;
+
+class Customer {
+
+ String getName() {
+ return "";
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/match/Employee.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/match/Employee.java
new file mode 100644
index 00000000..e594a682
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/match/Employee.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.match;
+
+class Employee {
+
+ String getEmployeeId() {
+ return "";
+ }
+
+ boolean isManager() {
+ return true;
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/match/FnMatchGreeter.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/match/FnMatchGreeter.java
new file mode 100644
index 00000000..3e1d3c82
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/match/FnMatchGreeter.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.match;
+
+import com.ververica.statefun.sdk.Context;
+import com.ververica.statefun.sdk.match.MatchBinder;
+import com.ververica.statefun.sdk.match.StatefulMatchFunction;
+
+public class FnMatchGreeter extends StatefulMatchFunction {
+
+ @Override
+ public void configure(MatchBinder binder) {
+ binder
+ .predicate(Customer.class, this::greetCustomer)
+ .predicate(Employee.class, Employee::isManager, this::greetManager)
+ .predicate(Employee.class, this::greetEmployee);
+ }
+
+ private void greetManager(Context context, Employee message) {
+ System.out.println("Hello manager " + message.getEmployeeId());
+ }
+
+ private void greetEmployee(Context context, Employee message) {
+ System.out.println("Hello employee " + message.getEmployeeId());
+ }
+
+ private void greetCustomer(Context context, Customer message) {
+ System.out.println("Hello customer " + message.getName());
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/match/FnMatchGreeterWithCatchAll.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/match/FnMatchGreeterWithCatchAll.java
new file mode 100644
index 00000000..d6d66f16
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/match/FnMatchGreeterWithCatchAll.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.match;
+
+import com.ververica.statefun.sdk.Context;
+import com.ververica.statefun.sdk.match.MatchBinder;
+import com.ververica.statefun.sdk.match.StatefulMatchFunction;
+
+public class FnMatchGreeterWithCatchAll extends StatefulMatchFunction {
+
+ @Override
+ public void configure(MatchBinder binder) {
+ binder
+ .predicate(Customer.class, this::greetCustomer)
+ .predicate(Employee.class, Employee::isManager, this::greetManager)
+ .predicate(Employee.class, this::greetEmployee)
+ .otherwise(this::catchAll);
+ }
+
+ private void catchAll(Context context, Object message) {
+ System.out.println("Hello unexpected message");
+ }
+
+ private void greetManager(Context context, Employee message) {
+ System.out.println("Hello manager");
+ }
+
+ private void greetEmployee(Context context, Employee message) {
+ System.out.println("Hello employee");
+ }
+
+ private void greetCustomer(Context context, Customer message) {
+ System.out.println("Hello customer");
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/match/FnUserGreeter.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/match/FnUserGreeter.java
new file mode 100644
index 00000000..2cf2466b
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/match/FnUserGreeter.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.match;
+
+import com.ververica.statefun.sdk.Context;
+import com.ververica.statefun.sdk.StatefulFunction;
+
+public class FnUserGreeter implements StatefulFunction {
+
+ @Override
+ public void invoke(Context context, Object input) {
+ if (input instanceof Employee) {
+ Employee message = (Employee) input;
+
+ if (message.isManager()) {
+ greetManager(context, message);
+ } else {
+ greetEmployee(context, message);
+ }
+ } else if (input instanceof Customer) {
+ Customer message = (Customer) input;
+ greetCustomer(context, message);
+ } else {
+ throw new IllegalStateException("Unknown message type " + input.getClass());
+ }
+ }
+
+ private void greetManager(Context context, Employee message) {
+ System.out.println("Hello manager " + message.getEmployeeId());
+ }
+
+ private void greetEmployee(Context context, Employee message) {
+ System.out.println("Hello employee " + message.getEmployeeId());
+ }
+
+ private void greetCustomer(Context context, Customer message) {
+ System.out.println("Hello customer " + message.getName());
+ }
+}
diff --git a/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/models/User.java b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/models/User.java
new file mode 100644
index 00000000..6e69887e
--- /dev/null
+++ b/stateful-functions-docs/src/main/java/com/ververica/statefun/docs/models/User.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.docs.models;
+
+/** A dummy message type. */
+public class User {
+
+ private String userId;
+
+ private String name;
+
+ private long birthday;
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public long getBirthday() {
+ return birthday;
+ }
+
+ public void setBirthday(long birthday) {
+ this.birthday = birthday;
+ }
+}
diff --git a/stateful-functions-examples/pom.xml b/stateful-functions-examples/pom.xml
new file mode 100644
index 00000000..2cfb470f
--- /dev/null
+++ b/stateful-functions-examples/pom.xml
@@ -0,0 +1,42 @@
+
+
+
+
+ 4.0.0
+
+
+ stateful-functions-parent
+ com.ververica
+ 1.0-SNAPSHOT
+
+
+ stateful-functions-examples
+ stateful-functions-examples
+ 1.0-SNAPSHOT
+ pom
+
+
+ stateful-functions-greeter-example
+ stateful-functions-ridesharing-example
+ stateful-functions-flink-harness-example
+ stateful-functions-shopping-cart-example
+ stateful-functions-async-example
+
+
+
+
diff --git a/stateful-functions-examples/stateful-functions-async-example/pom.xml b/stateful-functions-examples/stateful-functions-async-example/pom.xml
new file mode 100644
index 00000000..2d782d5a
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-async-example/pom.xml
@@ -0,0 +1,36 @@
+
+
+
+
+ stateful-functions-examples
+ com.ververica
+ 1.0-SNAPSHOT
+ ..
+
+ 4.0.0
+
+ stateful-functions-async-example
+
+
+ com.ververica
+ stateful-functions-flink-harness
+ ${project.version}
+
+
+
\ No newline at end of file
diff --git a/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/Constants.java b/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/Constants.java
new file mode 100644
index 00000000..5691affb
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/Constants.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.async;
+
+import com.ververica.statefun.examples.async.events.TaskCompletionEvent;
+import com.ververica.statefun.examples.async.events.TaskStartedEvent;
+import com.ververica.statefun.sdk.io.EgressIdentifier;
+import com.ververica.statefun.sdk.io.IngressIdentifier;
+
+final class Constants {
+
+ static final IngressIdentifier REQUEST_INGRESS =
+ new IngressIdentifier<>(
+ TaskStartedEvent.class, "com.ververica.statefun.examples.async", "tasks");
+
+ static final EgressIdentifier RESULT_EGRESS =
+ new EgressIdentifier<>(
+ "com.ververica.statefun.examples.async", "out", TaskCompletionEvent.class);
+}
diff --git a/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/Main.java b/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/Main.java
new file mode 100644
index 00000000..a71de06f
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/Main.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.async;
+
+import com.ververica.statefun.examples.async.events.TaskCompletionEvent;
+import com.ververica.statefun.examples.async.events.TaskStartedEvent;
+import com.ververica.statefun.flink.harness.Harness;
+import com.ververica.statefun.flink.harness.io.SerializableConsumer;
+import com.ververica.statefun.flink.harness.io.SerializableSupplier;
+import java.util.concurrent.ThreadLocalRandom;
+import javax.annotation.Nonnull;
+import org.apache.flink.util.StringUtils;
+
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+ Harness harness =
+ new Harness()
+ .noCheckpointing()
+ .withKryoMessageSerializer()
+ .withSupplyingIngress(Constants.REQUEST_INGRESS, new MessageGenerator())
+ .withConsumingEgress(Constants.RESULT_EGRESS, new MessagePrinter());
+
+ harness.start();
+ }
+
+ /** generate a random message, once a second a second. */
+ private static final class MessageGenerator implements SerializableSupplier {
+
+ private static final long serialVersionUID = 1;
+
+ @Override
+ public TaskStartedEvent get() {
+ try {
+ Thread.sleep(1_000);
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Interrupted", e);
+ }
+ return randomMessage();
+ }
+
+ @Nonnull
+ private TaskStartedEvent randomMessage() {
+ final ThreadLocalRandom random = ThreadLocalRandom.current();
+ final String taskId = StringUtils.generateRandomAlphanumericString(random, 2);
+ return new TaskStartedEvent(taskId, System.currentTimeMillis());
+ }
+ }
+
+ /** prints the messages to stdout. */
+ private static final class MessagePrinter implements SerializableConsumer {
+
+ private static final long serialVersionUID = 1;
+
+ @Override
+ public void accept(TaskCompletionEvent message) {
+ System.out.println(
+ String.format(
+ "Task %s has completed, total duration: %d ms",
+ message.getTaskId(), message.getEndTime() - message.getStartTime()));
+ }
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/Module.java b/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/Module.java
new file mode 100644
index 00000000..2ef6fa2f
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/Module.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.async;
+
+import com.ververica.statefun.examples.async.service.DummyTaskQueryService;
+import com.ververica.statefun.examples.async.service.TaskQueryService;
+import com.ververica.statefun.sdk.spi.StatefulFunctionModule;
+import java.util.Map;
+
+public class Module implements StatefulFunctionModule {
+
+ @Override
+ public void configure(Map globalConfiguration, Binder binder) {
+ TaskQueryService service = new DummyTaskQueryService();
+
+ binder.bindFunctionProvider(
+ TaskDurationTrackerFunction.TYPE, unused -> new TaskDurationTrackerFunction(service));
+
+ binder.bindIngressRouter(
+ Constants.REQUEST_INGRESS,
+ (message, downstream) ->
+ downstream.forward(TaskDurationTrackerFunction.TYPE, message.getTaskId(), message));
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/TaskDurationTrackerFunction.java b/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/TaskDurationTrackerFunction.java
new file mode 100644
index 00000000..0eed50dd
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/TaskDurationTrackerFunction.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.async;
+
+import com.ververica.statefun.examples.async.events.TaskCompletionEvent;
+import com.ververica.statefun.examples.async.events.TaskStartedEvent;
+import com.ververica.statefun.examples.async.service.TaskQueryService;
+import com.ververica.statefun.examples.async.service.TaskStatus;
+import com.ververica.statefun.sdk.AsyncOperationResult;
+import com.ververica.statefun.sdk.Context;
+import com.ververica.statefun.sdk.FunctionType;
+import com.ververica.statefun.sdk.StatefulFunction;
+import java.time.Duration;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * TaskDurationTrackerFunction - demonstrates the use of asynchronous operations.
+ *
+ *
In this example scenario there are tasks executing in an an external system, the external
+ * system exposes a {@link TaskQueryService} that can report back the status of each individual
+ * task, in addition the external system generates a {@link TaskStartedEvent} with the task id and
+ * creation time.
+ *
+ *
This function reacts to {@link TaskStartedEvent} and pulls the external {@link
+ * TaskQueryService} for the task completion, when finally the task completes, this function
+ * produces an {@link TaskCompletionEvent} with the task execution duration.
+ */
+final class TaskDurationTrackerFunction implements StatefulFunction {
+
+ static final FunctionType TYPE =
+ new FunctionType("com.ververica.statefun.examples.async", "duration-tracker");
+
+ private final TaskQueryService service;
+
+ TaskDurationTrackerFunction(TaskQueryService service) {
+ this.service = Objects.requireNonNull(service);
+ }
+
+ @Override
+ public void invoke(Context context, Object message) {
+ if (message instanceof TaskStartedEvent) {
+ // We received a TaskStartedEvent, in response, we need to check with an external service what
+ // is the status of that task. Since the external service (represented by the client) is
+ // asynchronous (returns a CompletableFuture) we register that future as a pending
+ // asynchronous operation, and we will
+ // get notified once the async operation completes via the special AsyncOperationResult
+ // message.
+ // we also attach the original input message as metadata to the async operation.
+ TaskStartedEvent e = (TaskStartedEvent) message;
+ CompletableFuture result = service.getTaskStatusAsync(e.getTaskId());
+ context.registerAsyncOperation(message, result);
+ return;
+ }
+ if (message instanceof AsyncOperationResult) {
+ // This is a result of an async operation we have previously registered.
+ // The message's metadata would be the original input event that triggered the async operation
+ // (TaskStartedEvent)
+ // and possibly the asynchronously computed TaskStatus.
+ @SuppressWarnings("unchecked")
+ AsyncOperationResult asyncOp =
+ (AsyncOperationResult) message;
+
+ onAsyncOperationResultEvent(context, asyncOp);
+ return;
+ }
+ throw new IllegalArgumentException("Unknown event " + message);
+ }
+
+ /**
+ * Handle the result of an asynchronous operation. The logic of this example is as follows: 1. If
+ * the async operation itself failed (i.e. IOException, TimeoutException etc') just blindly retry
+ * within a second 2. If the
+ */
+ private static void onAsyncOperationResultEvent(
+ Context context, AsyncOperationResult asyncOp) {
+
+ // We have attached the original TaskStartedEvent as a metadata, when registering the async
+ // operation, so we can just grab it.
+ final TaskStartedEvent e = asyncOp.metadata();
+
+ if (!asyncOp.successful()) {
+ // Something went wrong while trying to obtain the TaskStatus asynchronously, we can inspect
+ // the cause by
+ // calling asyncOp.throwable() or asking if the status is unknown (asyncOp.unknown())
+ // in any case we retry in 1 second, by just sending a delayed message to ourselves.
+ Duration delay = oneSecondPlusJitter();
+ context.sendAfter(delay, context.self(), e);
+ return;
+ }
+ // The async op has completed successfully now we can obtain the asynchronously computed value.
+ final TaskStatus status = asyncOp.value();
+ if (!status.isCompleted()) {
+ // The task status is not yet complete, therefore we need to pull the status again at some
+ // later point in time, lets retry in 10 seconds
+ context.sendAfter(Duration.ofSeconds(10), context.self(), e);
+ return;
+ }
+ handleCompletedTask(context, e, status);
+ }
+
+ /**
+ * compute a duration that represents slightly more than one second (with a random jitter) to
+ * avoid thundering herds.
+ */
+ private static Duration oneSecondPlusJitter() {
+ final long randomJitter = ThreadLocalRandom.current().nextLong(1_000, 1_250);
+ return Duration.ofMillis(randomJitter);
+ }
+
+ /** The task was completed, we can compute the task execution duration and emit it downstream. */
+ private static void handleCompletedTask(
+ Context context, TaskStartedEvent taskStartedEvent, TaskStatus finishedTaskStatus) {
+
+ TaskCompletionEvent taskCompletionEvent =
+ new TaskCompletionEvent(
+ taskStartedEvent.getTaskId(),
+ taskStartedEvent.getStartTime(),
+ finishedTaskStatus.getCompletionTime());
+
+ context.send(Constants.RESULT_EGRESS, taskCompletionEvent);
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/events/TaskCompletionEvent.java b/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/events/TaskCompletionEvent.java
new file mode 100644
index 00000000..8183e71e
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/events/TaskCompletionEvent.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.async.events;
+
+/** A message represents an event of a task completion. */
+public final class TaskCompletionEvent {
+ private final String taskId;
+ private final Long startTime;
+ private final Long endTime;
+
+ public TaskCompletionEvent(String taskId, Long startTime, Long endTime) {
+ this.taskId = taskId;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ }
+
+ public String getTaskId() {
+ return taskId;
+ }
+
+ public Long getStartTime() {
+ return startTime;
+ }
+
+ public Long getEndTime() {
+ return endTime;
+ }
+
+ public long taskCompletionDuration() {
+ return endTime - startTime;
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/events/TaskStartedEvent.java b/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/events/TaskStartedEvent.java
new file mode 100644
index 00000000..aba845ad
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/events/TaskStartedEvent.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.async.events;
+
+/** A message represents an event of a new task was created. A task has an id and a startingTime. */
+public final class TaskStartedEvent {
+ private final String taskId;
+ private final Long startTime;
+
+ public TaskStartedEvent(String taskId, Long startTime) {
+ this.taskId = taskId;
+ this.startTime = startTime;
+ }
+
+ public String getTaskId() {
+ return taskId;
+ }
+
+ public Long getStartTime() {
+ return startTime;
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/service/DummyTaskQueryService.java b/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/service/DummyTaskQueryService.java
new file mode 100644
index 00000000..faa177ec
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/service/DummyTaskQueryService.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.async.service;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A dummy implementation that simulates a service that might take a while to compute it's result.
+ */
+public final class DummyTaskQueryService implements TaskQueryService {
+
+ /**
+ * used to complete the futures after an artificial random delay, that simulates network latency,
+ * service busyness etc'
+ */
+ private final ScheduledExecutorService executor = newScheduledExecutorService();
+
+ public CompletableFuture getTaskStatusAsync(String taskId) {
+ CompletableFuture result = new CompletableFuture<>();
+
+ final long randomCompletionDelay = ThreadLocalRandom.current().nextLong(5_000);
+ executor.schedule(
+ completeRandomly(taskId, result), randomCompletionDelay, TimeUnit.MILLISECONDS);
+
+ return result;
+ }
+
+ /**
+ * returns a {@link Runnable}, that when runs, it completes the supplied future {@code result}
+ * with a {@link TaskStatus} that might be either done or still running. If done, then the task
+ * status would have a completion time.
+ */
+ private static Runnable completeRandomly(String taskId, CompletableFuture result) {
+ return () -> {
+ boolean taskStatus = ThreadLocalRandom.current().nextBoolean();
+
+ if (taskStatus) {
+ final long now = System.currentTimeMillis();
+ result.complete(new TaskStatus(taskId, true, now));
+ } else {
+ result.complete(new TaskStatus(taskId, false, null));
+ }
+ };
+ }
+
+ /** A scheduled executor service with daemon threads. */
+ private static ScheduledExecutorService newScheduledExecutorService() {
+ return Executors.newSingleThreadScheduledExecutor(
+ r -> {
+ Thread t = new Thread(r);
+ t.setDaemon(true);
+ return t;
+ });
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/service/TaskQueryService.java b/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/service/TaskQueryService.java
new file mode 100644
index 00000000..300a0c99
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/service/TaskQueryService.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.async.service;
+
+import java.util.concurrent.CompletableFuture;
+
+/** A remote service that keeps task status by id. */
+public interface TaskQueryService {
+
+ /**
+ * Retrieves a {@link TaskStatus} of a task with an @taskId.
+ *
+ * @param taskId the task to retrieves it's status
+ * @return the status of the task.
+ */
+ CompletableFuture getTaskStatusAsync(String taskId);
+}
diff --git a/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/service/TaskStatus.java b/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/service/TaskStatus.java
new file mode 100644
index 00000000..2ca181ef
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/service/TaskStatus.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.async.service;
+
+/**
+ * Represents A task status, as returned from the Dummy task service.
+ *
+ *
A Task might be either completed or uncompleted. If a task is completed then it would also
+ * have a completion time.
+ */
+public class TaskStatus {
+ private final String taskId;
+ private final boolean completed;
+ private final Long completionTime;
+
+ TaskStatus(String taskId, boolean completed, Long completionTime) {
+ this.taskId = taskId;
+ this.completed = completed;
+ this.completionTime = completionTime;
+ }
+
+ public String getTaskId() {
+ return taskId;
+ }
+
+ public boolean isCompleted() {
+ return completed;
+ }
+
+ public Long getCompletionTime() {
+ return completionTime;
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/service/package-info.java b/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/service/package-info.java
new file mode 100644
index 00000000..63d481db
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-async-example/src/main/java/com/ververica/statefun/examples/async/service/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.
+ */
+
+/**
+ * This package represents a fictions service that needs to compute something asynchronously. In
+ * this example, a query service keeps tack of tasks that are executed elsewhere (perhaps some
+ * worker pool outside of the application) see: {@link
+ * com.ververica.statefun.examples.async.service.TaskQueryService}. In reality, functions might want
+ * to compute something asynchronously like sending an http request, querying a remote database, or
+ * anything really that needs some time to complete.
+ */
+package com.ververica.statefun.examples.async.service;
diff --git a/stateful-functions-examples/stateful-functions-async-example/src/main/resources/META-INF/services/com.ververica.statefun.sdk.spi.StatefulFunctionModule b/stateful-functions-examples/stateful-functions-async-example/src/main/resources/META-INF/services/com.ververica.statefun.sdk.spi.StatefulFunctionModule
new file mode 100644
index 00000000..c1a6e6a1
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-async-example/src/main/resources/META-INF/services/com.ververica.statefun.sdk.spi.StatefulFunctionModule
@@ -0,0 +1,16 @@
+#
+# Copyright 2019 Ververica GmbH.
+#
+# 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.
+#
+com.ververica.statefun.examples.async.Module
\ No newline at end of file
diff --git a/stateful-functions-examples/stateful-functions-flink-harness-example/README.md b/stateful-functions-examples/stateful-functions-flink-harness-example/README.md
new file mode 100644
index 00000000..0f1bb0df
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-flink-harness-example/README.md
@@ -0,0 +1,11 @@
+# Using the Flink Harness to run Stateful Functions applications in the IDE
+
+This is a simple example to demonstrate how one would run Stateful Functions application in the IDE using the
+provided Flink Harness.
+
+Take a look at the `com.ververica.statefun.examples.harness.Main` class to see example usages of `Harness`.
+
+## Running the example
+
+Simply run the `com.ververica.statefun.examples.harness.Main` class.
+
diff --git a/stateful-functions-examples/stateful-functions-flink-harness-example/pom.xml b/stateful-functions-examples/stateful-functions-flink-harness-example/pom.xml
new file mode 100644
index 00000000..d90293ba
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-flink-harness-example/pom.xml
@@ -0,0 +1,36 @@
+
+
+
+
+ stateful-functions-examples
+ com.ververica
+ 1.0-SNAPSHOT
+ ..
+
+ 4.0.0
+
+ stateful-functions-flink-harness-example
+
+
+ com.ververica
+ stateful-functions-flink-harness
+ ${project.version}
+
+
+
\ No newline at end of file
diff --git a/stateful-functions-examples/stateful-functions-flink-harness-example/src/main/java/com/ververica/statefun/examples/harness/Main.java b/stateful-functions-examples/stateful-functions-flink-harness-example/src/main/java/com/ververica/statefun/examples/harness/Main.java
new file mode 100644
index 00000000..38ef0d12
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-flink-harness-example/src/main/java/com/ververica/statefun/examples/harness/Main.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.harness;
+
+import com.ververica.statefun.examples.harness.MyMessages.MyInputMessage;
+import com.ververica.statefun.examples.harness.MyMessages.MyOutputMessage;
+import com.ververica.statefun.flink.harness.Harness;
+import com.ververica.statefun.flink.harness.io.SerializableConsumer;
+import com.ververica.statefun.flink.harness.io.SerializableSupplier;
+import java.util.concurrent.ThreadLocalRandom;
+import javax.annotation.Nonnull;
+import org.apache.flink.util.StringUtils;
+
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+ Harness harness =
+ new Harness()
+ .noCheckpointing()
+ .withKryoMessageSerializer()
+ .withSupplyingIngress(MyConstants.REQUEST_INGRESS, new MessageGenerator())
+ .withConsumingEgress(MyConstants.RESULT_EGRESS, new MessagePrinter());
+
+ harness.start();
+ }
+
+ /** generate a random message, once a second a second. */
+ private static final class MessageGenerator implements SerializableSupplier {
+
+ private static final long serialVersionUID = 1;
+
+ @Override
+ public MyInputMessage get() {
+ try {
+ Thread.sleep(1_000);
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Interrupted", e);
+ }
+ return randomMessage();
+ }
+
+ @Nonnull
+ private MyInputMessage randomMessage() {
+ final ThreadLocalRandom random = ThreadLocalRandom.current();
+ final String userId = StringUtils.generateRandomAlphanumericString(random, 2);
+ return new MyInputMessage(userId, "hello " + userId);
+ }
+ }
+
+ /** prints the messages to stdout. */
+ private static final class MessagePrinter implements SerializableConsumer {
+
+ private static final long serialVersionUID = 1;
+
+ @Override
+ public void accept(MyOutputMessage message) {
+ System.out.println(message);
+ }
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-flink-harness-example/src/main/java/com/ververica/statefun/examples/harness/MyConstants.java b/stateful-functions-examples/stateful-functions-flink-harness-example/src/main/java/com/ververica/statefun/examples/harness/MyConstants.java
new file mode 100644
index 00000000..1de86371
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-flink-harness-example/src/main/java/com/ververica/statefun/examples/harness/MyConstants.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.harness;
+
+import com.ververica.statefun.examples.harness.MyMessages.MyInputMessage;
+import com.ververica.statefun.examples.harness.MyMessages.MyOutputMessage;
+import com.ververica.statefun.sdk.FunctionType;
+import com.ververica.statefun.sdk.io.EgressIdentifier;
+import com.ververica.statefun.sdk.io.IngressIdentifier;
+
+final class MyConstants {
+ static final IngressIdentifier REQUEST_INGRESS =
+ new IngressIdentifier<>(
+ MyInputMessage.class, "com.ververica.statefun.examples.harness", "in");
+
+ static final EgressIdentifier RESULT_EGRESS =
+ new EgressIdentifier<>(
+ "com.ververica.statefun.examples.harness", "out", MyOutputMessage.class);
+
+ static final FunctionType MY_FUNCTION_TYPE =
+ new FunctionType("com.ververica.statefun.examples.harness", "my-function");
+}
diff --git a/stateful-functions-examples/stateful-functions-flink-harness-example/src/main/java/com/ververica/statefun/examples/harness/MyFunction.java b/stateful-functions-examples/stateful-functions-flink-harness-example/src/main/java/com/ververica/statefun/examples/harness/MyFunction.java
new file mode 100644
index 00000000..54e6c96a
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-flink-harness-example/src/main/java/com/ververica/statefun/examples/harness/MyFunction.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.harness;
+
+import com.ververica.statefun.examples.harness.MyMessages.MyInputMessage;
+import com.ververica.statefun.examples.harness.MyMessages.MyOutputMessage;
+import com.ververica.statefun.sdk.Context;
+import com.ververica.statefun.sdk.StatefulFunction;
+
+final class MyFunction implements StatefulFunction {
+
+ @Override
+ public void invoke(Context context, Object input) {
+ if (!(input instanceof MyInputMessage)) {
+ throw new IllegalArgumentException("Unknown message received " + input);
+ }
+ MyInputMessage in = (MyInputMessage) input;
+ MyOutputMessage out = new MyOutputMessage(in.getUserId(), in.getMessage());
+
+ context.send(MyConstants.RESULT_EGRESS, out);
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-flink-harness-example/src/main/java/com/ververica/statefun/examples/harness/MyMessages.java b/stateful-functions-examples/stateful-functions-flink-harness-example/src/main/java/com/ververica/statefun/examples/harness/MyMessages.java
new file mode 100644
index 00000000..a6fde3ca
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-flink-harness-example/src/main/java/com/ververica/statefun/examples/harness/MyMessages.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.harness;
+
+final class MyMessages {
+
+ static final class MyInputMessage {
+ private final String userId;
+ private final String message;
+
+ MyInputMessage(String userId, String message) {
+ this.userId = userId;
+ this.message = message;
+ }
+
+ String getUserId() {
+ return userId;
+ }
+
+ String getMessage() {
+ return message;
+ }
+ }
+
+ static final class MyOutputMessage {
+ private final String userId;
+ private final String content;
+
+ MyOutputMessage(String userId, String content) {
+ this.userId = userId;
+ this.content = content;
+ }
+
+ String getUserId() {
+ return userId;
+ }
+
+ String getContent() {
+ return content;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("MyOutputMessage(%s, %s)", getUserId(), getContent());
+ }
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-flink-harness-example/src/main/java/com/ververica/statefun/examples/harness/MyModule.java b/stateful-functions-examples/stateful-functions-flink-harness-example/src/main/java/com/ververica/statefun/examples/harness/MyModule.java
new file mode 100644
index 00000000..91520008
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-flink-harness-example/src/main/java/com/ververica/statefun/examples/harness/MyModule.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.harness;
+
+import com.ververica.statefun.sdk.spi.StatefulFunctionModule;
+import java.util.Map;
+
+public class MyModule implements StatefulFunctionModule {
+
+ @Override
+ public void configure(Map globalConfiguration, Binder binder) {
+ binder.bindIngressRouter(MyConstants.REQUEST_INGRESS, new MyRouter());
+ binder.bindFunctionProvider(MyConstants.MY_FUNCTION_TYPE, unused -> new MyFunction());
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-flink-harness-example/src/main/java/com/ververica/statefun/examples/harness/MyRouter.java b/stateful-functions-examples/stateful-functions-flink-harness-example/src/main/java/com/ververica/statefun/examples/harness/MyRouter.java
new file mode 100644
index 00000000..8ac67c73
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-flink-harness-example/src/main/java/com/ververica/statefun/examples/harness/MyRouter.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.harness;
+
+import com.ververica.statefun.examples.harness.MyMessages.MyInputMessage;
+import com.ververica.statefun.sdk.io.Router;
+
+final class MyRouter implements Router {
+
+ @Override
+ public void route(MyInputMessage message, Downstream downstream) {
+ downstream.forward(MyConstants.MY_FUNCTION_TYPE, message.getUserId(), message);
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-flink-harness-example/src/main/resources/META-INF/services/com.ververica.statefun.sdk.spi.StatefulFunctionModule b/stateful-functions-examples/stateful-functions-flink-harness-example/src/main/resources/META-INF/services/com.ververica.statefun.sdk.spi.StatefulFunctionModule
new file mode 100644
index 00000000..165413ce
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-flink-harness-example/src/main/resources/META-INF/services/com.ververica.statefun.sdk.spi.StatefulFunctionModule
@@ -0,0 +1,16 @@
+#
+# Copyright 2019 Ververica GmbH.
+#
+# 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.
+#
+com.ververica.statefun.examples.harness.MyModule
\ No newline at end of file
diff --git a/stateful-functions-examples/stateful-functions-greeter-example/Dockerfile b/stateful-functions-examples/stateful-functions-greeter-example/Dockerfile
new file mode 100644
index 00000000..c1ee0b38
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-greeter-example/Dockerfile
@@ -0,0 +1,20 @@
+#
+# Copyright 2019 Ververica GmbH.
+#
+# 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 stateful-functions
+
+RUN mkdir -p /opt/stateful-functions/modules/stateful-functions-greeter-example
+COPY target/stateful-functions-greeter-example*jar /opt/stateful-functions/modules/stateful-functions-greeter-example/
diff --git a/stateful-functions-examples/stateful-functions-greeter-example/README.md b/stateful-functions-examples/stateful-functions-greeter-example/README.md
new file mode 100644
index 00000000..75f303f1
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-greeter-example/README.md
@@ -0,0 +1,32 @@
+# The Greeter Example
+
+This is a simple example that runs a simple stateful function that accepts requests from a Kafka ingress,
+and then responds by sending greeting responses to a Kafka egress. It demonstrates the primitive building blocks
+of a Stateful Functions applications, such as ingresses, routing messages to functions, handling state in functions,
+and sending messages to egresses.
+
+## Running the example
+
+To run the example:
+
+```
+docker-compose build
+docker-compose up
+```
+
+Then, to see the example in actions, send some messages to the topic `names`, and see what comes out
+out of the topic `greetings`:
+
+```
+KAFKA=$(docker ps -f "name=stateful-functions-greeter-example_kafka-broker_1" --format "{{.ID}}") ; \
+docker exec -it $KAFKA kafka-console-producer.sh \
+ --broker-list localhost:9092 \
+ --topic names
+```
+
+```
+KAFKA=$(docker ps -f "name=stateful-functions-greeter-example_kafka-broker_1" --format "{{.ID}}") ; \
+docker exec -it $KAFKA kafka-console-consumer.sh \
+ --bootstrap-server localhost:9092 \
+ --topic greetings
+```
diff --git a/stateful-functions-examples/stateful-functions-greeter-example/docker-compose.yml b/stateful-functions-examples/stateful-functions-greeter-example/docker-compose.yml
new file mode 100644
index 00000000..4df1c6cf
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-greeter-example/docker-compose.yml
@@ -0,0 +1,66 @@
+#
+# Copyright 2019 Ververica GmbH.
+#
+# 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.
+#
+
+version: "2.1"
+services:
+ zookeeper:
+ image: wurstmeister/zookeeper
+ ports:
+ - "2181:2181"
+ kafka-broker:
+ image: wurstmeister/kafka:2.12-2.0.1
+ ports:
+ - "9092:9092"
+ environment:
+ HOSTNAME_COMMAND: "route -n | awk '/UG[ \t]/{print $$2}'"
+ KAFKA_CREATE_TOPICS: "names:1:1,greetings:1:1"
+ KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
+ depends_on:
+ - zookeeper
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock
+ master:
+ build:
+ context: .
+ # uncomment to start from a savepoint
+ #command: -s /checkpoint-dir/savepoint/savepoint-bf101a-4e99820085fe
+ expose:
+ - "6123"
+ ports:
+ - "8081:8081"
+ environment:
+ - ROLE=master
+ - MASTER_HOST=master
+ volumes:
+ - ./checkpoint-dir:/checkpoint-dir
+ worker:
+ build:
+ context: .
+ expose:
+ - "6121"
+ - "6122"
+ depends_on:
+ - master
+ - kafka-broker
+ links:
+ - "master:master"
+ - "kafka-broker:kafka-broker"
+ environment:
+ - ROLE=worker
+ - MASTER_HOST=master
+ volumes:
+ - ./checkpoint-dir:/checkpoint-dir
+
diff --git a/stateful-functions-examples/stateful-functions-greeter-example/pom.xml b/stateful-functions-examples/stateful-functions-greeter-example/pom.xml
new file mode 100644
index 00000000..8c462b36
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-greeter-example/pom.xml
@@ -0,0 +1,48 @@
+
+
+
+
+ stateful-functions-examples
+ com.ververica
+ 1.0-SNAPSHOT
+ ..
+
+ 4.0.0
+
+ stateful-functions-greeter-example
+
+
+
+ com.ververica
+ stateful-functions-sdk
+ ${project.version}
+
+
+ com.ververica
+ stateful-functions-kafka-io
+ ${project.version}
+
+
+ com.google.protobuf
+ protobuf-java
+ 3.8.0
+
+
+
+
diff --git a/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/GreetRouter.java b/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/GreetRouter.java
new file mode 100644
index 00000000..c7d1d46e
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/GreetRouter.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.greeter;
+
+import com.ververica.statefun.examples.greeter.generated.GreetRequest;
+import com.ververica.statefun.sdk.io.Router;
+
+/**
+ * The greet router takes each message from an ingress and routes it to a greeter function based on
+ * the users id.
+ */
+final class GreetRouter implements Router {
+
+ @Override
+ public void route(GreetRequest message, Downstream downstream) {
+ downstream.forward(GreetStatefulFunction.TYPE, message.getWho(), message);
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/GreetStatefulFunction.java b/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/GreetStatefulFunction.java
new file mode 100644
index 00000000..fa194719
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/GreetStatefulFunction.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.greeter;
+
+import com.ververica.statefun.examples.greeter.generated.GreetRequest;
+import com.ververica.statefun.examples.greeter.generated.GreetResponse;
+import com.ververica.statefun.sdk.Context;
+import com.ververica.statefun.sdk.FunctionType;
+import com.ververica.statefun.sdk.StatefulFunction;
+import com.ververica.statefun.sdk.annotations.Persisted;
+import com.ververica.statefun.sdk.state.PersistedValue;
+
+/**
+ * A stateful function that generates a unique greeting for each user based on how many times that
+ * user has been seen by the system.
+ */
+final class GreetStatefulFunction implements StatefulFunction {
+
+ /**
+ * The function type is the unique identifier that identifies this type of function. The type, in
+ * conjunction with an identifier, is how routers and other functions can use to reference a
+ * particular instance of a greeter function.
+ *
+ *
If this was a multi-module application, the function type could be in different package so
+ * functions in other modules could message the greeter without a direct dependency on this class.
+ */
+ static final FunctionType TYPE = new FunctionType("ververica", "greeter");
+
+ /**
+ * The persisted value for maintaining state about a particular user. The value returned by this
+ * field is always scoped to the current user. seenCount is the number of times the user has been
+ * greeted.
+ */
+ @Persisted
+ private final PersistedValue seenCount = PersistedValue.of("seen-count", Integer.class);
+
+ @Override
+ public void invoke(Context context, Object input) {
+ GreetRequest greetMessage = (GreetRequest) input;
+ GreetResponse response = computePersonalizedGreeting(greetMessage);
+ context.send(GreetingIO.GREETING_EGRESS_ID, response);
+ }
+
+ private GreetResponse computePersonalizedGreeting(GreetRequest greetMessage) {
+ final String name = greetMessage.getWho();
+ final int seen = seenCount.getOrDefault(0);
+ seenCount.set(seen + 1);
+
+ String greeting = greetText(name, seen);
+
+ return GreetResponse.newBuilder().setWho(name).setGreeting(greeting).build();
+ }
+
+ private static String greetText(String name, int seen) {
+ switch (seen) {
+ case 0:
+ return String.format("Hello %s ! \uD83D\uDE0E", name);
+ case 1:
+ return String.format("Hello again %s ! \uD83E\uDD17", name);
+ case 2:
+ return String.format("Third time is a charm! %s! \uD83E\uDD73", name);
+ case 3:
+ return String.format("Happy to see you once again %s ! \uD83D\uDE32", name);
+ default:
+ return String.format("Hello at the %d-th time %s \uD83D\uDE4C", seen + 1, name);
+ }
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/GreetingIO.java b/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/GreetingIO.java
new file mode 100644
index 00000000..7e3d961d
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/GreetingIO.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.greeter;
+
+import com.ververica.statefun.examples.greeter.generated.GreetRequest;
+import com.ververica.statefun.examples.greeter.generated.GreetResponse;
+import com.ververica.statefun.sdk.io.EgressIdentifier;
+import com.ververica.statefun.sdk.io.EgressSpec;
+import com.ververica.statefun.sdk.io.IngressIdentifier;
+import com.ververica.statefun.sdk.io.IngressSpec;
+import com.ververica.statefun.sdk.kafka.KafkaEgressBuilder;
+import com.ververica.statefun.sdk.kafka.KafkaEgressSerializer;
+import com.ververica.statefun.sdk.kafka.KafkaIngressBuilder;
+import com.ververica.statefun.sdk.kafka.KafkaIngressDeserializer;
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+import org.apache.kafka.clients.consumer.ConsumerConfig;
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.apache.kafka.clients.producer.ProducerRecord;
+
+/**
+ * A collection of all the components necessary to consume from and write to an external system, in
+ * this case Apache Kafka.
+ *
+ *
The ingress and egress identifiers provide named references without exposing the underlying
+ * system. This way, in a multi-module deployment, functions can interact with IO modules through
+ * identifiers without depending on specific implementations.
+ */
+final class GreetingIO {
+
+ static final IngressIdentifier GREETING_INGRESS_ID =
+ new IngressIdentifier<>(GreetRequest.class, "ververica", "greet-ingress");
+
+ static final EgressIdentifier GREETING_EGRESS_ID =
+ new EgressIdentifier<>("ververica", "kafka-greeting-output", GreetResponse.class);
+
+ private final String kafkaAddress;
+
+ GreetingIO(String kafkaAddress) {
+ this.kafkaAddress = Objects.requireNonNull(kafkaAddress);
+ }
+
+ IngressSpec getIngressSpec() {
+ return KafkaIngressBuilder.forIdentifier(GREETING_INGRESS_ID)
+ .withKafkaAddress(kafkaAddress)
+ .withTopic("names")
+ .withDeserializer(GreetKafkaDeserializer.class)
+ .withProperty(ConsumerConfig.GROUP_ID_CONFIG, "greetings")
+ .build();
+ }
+
+ EgressSpec getEgressSpec() {
+ return KafkaEgressBuilder.forIdentifier(GREETING_EGRESS_ID)
+ .withKafkaAddress(kafkaAddress)
+ .withSerializer(GreetKafkaSerializer.class)
+ .build();
+ }
+
+ private static final class GreetKafkaDeserializer
+ implements KafkaIngressDeserializer {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public GreetRequest deserialize(ConsumerRecord input) {
+ String who = new String(input.value(), StandardCharsets.UTF_8);
+
+ return GreetRequest.newBuilder().setWho(who).build();
+ }
+ }
+
+ private static final class GreetKafkaSerializer implements KafkaEgressSerializer {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public ProducerRecord serialize(GreetResponse response) {
+ byte[] key = response.getWho().getBytes(StandardCharsets.UTF_8);
+ byte[] value = response.getGreeting().getBytes(StandardCharsets.UTF_8);
+
+ return new ProducerRecord<>("greetings", key, value);
+ }
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/GreetingModule.java b/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/GreetingModule.java
new file mode 100644
index 00000000..8da87c71
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/GreetingModule.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.greeter;
+
+import com.ververica.statefun.sdk.spi.StatefulFunctionModule;
+import java.util.Map;
+
+/**
+ * The top level entry point for this application.
+ *
+ *
On deployment, the address of the Kafka brokers can be configured by passing the flag
+ * `--kafka-address
`. If no flag is passed, then the default address will be used.
+ */
+public final class GreetingModule implements StatefulFunctionModule {
+
+ private static final String KAFKA_KEY = "kafka-address";
+
+ private static final String DEFAULT_KAFKA_ADDRESS = "kafka-broker:9092";
+
+ @Override
+ public void configure(Map globalConfiguration, Binder binder) {
+
+ // pull the configured kafka broker address, or default if none was passed.
+ String kafkaAddress = globalConfiguration.getOrDefault(KAFKA_KEY, DEFAULT_KAFKA_ADDRESS);
+ GreetingIO ioModule = new GreetingIO(kafkaAddress);
+
+ // bind an ingress to the system along with the router
+ binder.bindIngress(ioModule.getIngressSpec());
+ binder.bindIngressRouter(GreetingIO.GREETING_INGRESS_ID, new GreetRouter());
+
+ // bind an egress to the system
+ binder.bindEgress(ioModule.getEgressSpec());
+
+ // bind a function provider to a function type
+ binder.bindFunctionProvider(GreetStatefulFunction.TYPE, unused -> new GreetStatefulFunction());
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/generated/GreetRequest.java b/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/generated/GreetRequest.java
new file mode 100644
index 00000000..8dc21ac3
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/generated/GreetRequest.java
@@ -0,0 +1,533 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: src/main/protobuf/greeter.proto
+
+package com.ververica.statefun.examples.greeter.generated;
+
+/** Protobuf type {@code com.ververica.statefun.examples.kafka.GreetRequest} */
+public final class GreetRequest extends com.google.protobuf.GeneratedMessageV3
+ implements
+ // @@protoc_insertion_point(message_implements:com.ververica.statefun.examples.kafka.GreetRequest)
+ GreetRequestOrBuilder {
+ private static final long serialVersionUID = 0L;
+ // Use GreetRequest.newBuilder() to construct.
+ private GreetRequest(com.google.protobuf.GeneratedMessageV3.Builder> builder) {
+ super(builder);
+ }
+
+ private GreetRequest() {
+ who_ = "";
+ }
+
+ @java.lang.Override
+ public final com.google.protobuf.UnknownFieldSet getUnknownFields() {
+ return this.unknownFields;
+ }
+
+ private GreetRequest(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ this();
+ if (extensionRegistry == null) {
+ throw new java.lang.NullPointerException();
+ }
+ int mutable_bitField0_ = 0;
+ com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+ com.google.protobuf.UnknownFieldSet.newBuilder();
+ try {
+ boolean done = false;
+ while (!done) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ done = true;
+ break;
+ case 10:
+ {
+ java.lang.String s = input.readStringRequireUtf8();
+
+ who_ = s;
+ break;
+ }
+ default:
+ {
+ if (!parseUnknownField(input, unknownFields, extensionRegistry, tag)) {
+ done = true;
+ }
+ break;
+ }
+ }
+ }
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ throw e.setUnfinishedMessage(this);
+ } catch (java.io.IOException e) {
+ throw new com.google.protobuf.InvalidProtocolBufferException(e).setUnfinishedMessage(this);
+ } finally {
+ this.unknownFields = unknownFields.build();
+ makeExtensionsImmutable();
+ }
+ }
+
+ public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() {
+ return com.ververica.statefun.examples.greeter.generated.Greeter
+ .internal_static_com_ververica_statefun_examples_kafka_GreetRequest_descriptor;
+ }
+
+ @java.lang.Override
+ protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return com.ververica.statefun.examples.greeter.generated.Greeter
+ .internal_static_com_ververica_statefun_examples_kafka_GreetRequest_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ com.ververica.statefun.examples.greeter.generated.GreetRequest.class,
+ com.ververica.statefun.examples.greeter.generated.GreetRequest.Builder.class);
+ }
+
+ public static final int WHO_FIELD_NUMBER = 1;
+ private volatile java.lang.Object who_;
+ /** string who = 1; */
+ public java.lang.String getWho() {
+ java.lang.Object ref = who_;
+ if (ref instanceof java.lang.String) {
+ return (java.lang.String) ref;
+ } else {
+ com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
+ java.lang.String s = bs.toStringUtf8();
+ who_ = s;
+ return s;
+ }
+ }
+ /** string who = 1; */
+ public com.google.protobuf.ByteString getWhoBytes() {
+ java.lang.Object ref = who_;
+ if (ref instanceof java.lang.String) {
+ com.google.protobuf.ByteString b =
+ com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref);
+ who_ = b;
+ return b;
+ } else {
+ return (com.google.protobuf.ByteString) ref;
+ }
+ }
+
+ private byte memoizedIsInitialized = -1;
+
+ @java.lang.Override
+ public final boolean isInitialized() {
+ byte isInitialized = memoizedIsInitialized;
+ if (isInitialized == 1) return true;
+ if (isInitialized == 0) return false;
+
+ memoizedIsInitialized = 1;
+ return true;
+ }
+
+ @java.lang.Override
+ public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException {
+ if (!getWhoBytes().isEmpty()) {
+ com.google.protobuf.GeneratedMessageV3.writeString(output, 1, who_);
+ }
+ unknownFields.writeTo(output);
+ }
+
+ @java.lang.Override
+ public int getSerializedSize() {
+ int size = memoizedSize;
+ if (size != -1) return size;
+
+ size = 0;
+ if (!getWhoBytes().isEmpty()) {
+ size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, who_);
+ }
+ size += unknownFields.getSerializedSize();
+ memoizedSize = size;
+ return size;
+ }
+
+ @java.lang.Override
+ public boolean equals(final java.lang.Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof com.ververica.statefun.examples.greeter.generated.GreetRequest)) {
+ return super.equals(obj);
+ }
+ com.ververica.statefun.examples.greeter.generated.GreetRequest other =
+ (com.ververica.statefun.examples.greeter.generated.GreetRequest) obj;
+
+ if (!getWho().equals(other.getWho())) return false;
+ if (!unknownFields.equals(other.unknownFields)) return false;
+ return true;
+ }
+
+ @java.lang.Override
+ public int hashCode() {
+ if (memoizedHashCode != 0) {
+ return memoizedHashCode;
+ }
+ int hash = 41;
+ hash = (19 * hash) + getDescriptor().hashCode();
+ hash = (37 * hash) + WHO_FIELD_NUMBER;
+ hash = (53 * hash) + getWho().hashCode();
+ hash = (29 * hash) + unknownFields.hashCode();
+ memoizedHashCode = hash;
+ return hash;
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetRequest parseFrom(
+ java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetRequest parseFrom(
+ java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetRequest parseFrom(
+ com.google.protobuf.ByteString data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetRequest parseFrom(
+ com.google.protobuf.ByteString data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetRequest parseFrom(
+ byte[] data) throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetRequest parseFrom(
+ byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetRequest parseFrom(
+ java.io.InputStream input) throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetRequest parseFrom(
+ java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseWithIOException(
+ PARSER, input, extensionRegistry);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetRequest parseDelimitedFrom(
+ java.io.InputStream input) throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetRequest parseDelimitedFrom(
+ java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(
+ PARSER, input, extensionRegistry);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetRequest parseFrom(
+ com.google.protobuf.CodedInputStream input) throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetRequest parseFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseWithIOException(
+ PARSER, input, extensionRegistry);
+ }
+
+ @java.lang.Override
+ public Builder newBuilderForType() {
+ return newBuilder();
+ }
+
+ public static Builder newBuilder() {
+ return DEFAULT_INSTANCE.toBuilder();
+ }
+
+ public static Builder newBuilder(
+ com.ververica.statefun.examples.greeter.generated.GreetRequest prototype) {
+ return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+ }
+
+ @java.lang.Override
+ public Builder toBuilder() {
+ return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this);
+ }
+
+ @java.lang.Override
+ protected Builder newBuilderForType(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+ Builder builder = new Builder(parent);
+ return builder;
+ }
+ /** Protobuf type {@code com.ververica.statefun.examples.kafka.GreetRequest} */
+ public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder
+ implements
+ // @@protoc_insertion_point(builder_implements:com.ververica.statefun.examples.kafka.GreetRequest)
+ com.ververica.statefun.examples.greeter.generated.GreetRequestOrBuilder {
+ public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() {
+ return com.ververica.statefun.examples.greeter.generated.Greeter
+ .internal_static_com_ververica_statefun_examples_kafka_GreetRequest_descriptor;
+ }
+
+ @java.lang.Override
+ protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return com.ververica.statefun.examples.greeter.generated.Greeter
+ .internal_static_com_ververica_statefun_examples_kafka_GreetRequest_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ com.ververica.statefun.examples.greeter.generated.GreetRequest.class,
+ com.ververica.statefun.examples.greeter.generated.GreetRequest.Builder.class);
+ }
+
+ // Construct using com.ververica.statefun.examples.greeter.generated.GreetRequest.newBuilder()
+ private Builder() {
+ maybeForceBuilderInitialization();
+ }
+
+ private Builder(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+ super(parent);
+ maybeForceBuilderInitialization();
+ }
+
+ private void maybeForceBuilderInitialization() {
+ if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) {}
+ }
+
+ @java.lang.Override
+ public Builder clear() {
+ super.clear();
+ who_ = "";
+
+ return this;
+ }
+
+ @java.lang.Override
+ public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() {
+ return com.ververica.statefun.examples.greeter.generated.Greeter
+ .internal_static_com_ververica_statefun_examples_kafka_GreetRequest_descriptor;
+ }
+
+ @java.lang.Override
+ public com.ververica.statefun.examples.greeter.generated.GreetRequest
+ getDefaultInstanceForType() {
+ return com.ververica.statefun.examples.greeter.generated.GreetRequest.getDefaultInstance();
+ }
+
+ @java.lang.Override
+ public com.ververica.statefun.examples.greeter.generated.GreetRequest build() {
+ com.ververica.statefun.examples.greeter.generated.GreetRequest result = buildPartial();
+ if (!result.isInitialized()) {
+ throw newUninitializedMessageException(result);
+ }
+ return result;
+ }
+
+ @java.lang.Override
+ public com.ververica.statefun.examples.greeter.generated.GreetRequest buildPartial() {
+ com.ververica.statefun.examples.greeter.generated.GreetRequest result =
+ new com.ververica.statefun.examples.greeter.generated.GreetRequest(this);
+ result.who_ = who_;
+ onBuilt();
+ return result;
+ }
+
+ @java.lang.Override
+ public Builder clone() {
+ return super.clone();
+ }
+
+ @java.lang.Override
+ public Builder setField(
+ com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) {
+ return super.setField(field, value);
+ }
+
+ @java.lang.Override
+ public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) {
+ return super.clearField(field);
+ }
+
+ @java.lang.Override
+ public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+ return super.clearOneof(oneof);
+ }
+
+ @java.lang.Override
+ public Builder setRepeatedField(
+ com.google.protobuf.Descriptors.FieldDescriptor field, int index, java.lang.Object value) {
+ return super.setRepeatedField(field, index, value);
+ }
+
+ @java.lang.Override
+ public Builder addRepeatedField(
+ com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) {
+ return super.addRepeatedField(field, value);
+ }
+
+ @java.lang.Override
+ public Builder mergeFrom(com.google.protobuf.Message other) {
+ if (other instanceof com.ververica.statefun.examples.greeter.generated.GreetRequest) {
+ return mergeFrom((com.ververica.statefun.examples.greeter.generated.GreetRequest) other);
+ } else {
+ super.mergeFrom(other);
+ return this;
+ }
+ }
+
+ public Builder mergeFrom(com.ververica.statefun.examples.greeter.generated.GreetRequest other) {
+ if (other
+ == com.ververica.statefun.examples.greeter.generated.GreetRequest.getDefaultInstance())
+ return this;
+ if (!other.getWho().isEmpty()) {
+ who_ = other.who_;
+ onChanged();
+ }
+ this.mergeUnknownFields(other.unknownFields);
+ onChanged();
+ return this;
+ }
+
+ @java.lang.Override
+ public final boolean isInitialized() {
+ return true;
+ }
+
+ @java.lang.Override
+ public Builder mergeFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ com.ververica.statefun.examples.greeter.generated.GreetRequest parsedMessage = null;
+ try {
+ parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ parsedMessage =
+ (com.ververica.statefun.examples.greeter.generated.GreetRequest)
+ e.getUnfinishedMessage();
+ throw e.unwrapIOException();
+ } finally {
+ if (parsedMessage != null) {
+ mergeFrom(parsedMessage);
+ }
+ }
+ return this;
+ }
+
+ private java.lang.Object who_ = "";
+ /** string who = 1; */
+ public java.lang.String getWho() {
+ java.lang.Object ref = who_;
+ if (!(ref instanceof java.lang.String)) {
+ com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
+ java.lang.String s = bs.toStringUtf8();
+ who_ = s;
+ return s;
+ } else {
+ return (java.lang.String) ref;
+ }
+ }
+ /** string who = 1; */
+ public com.google.protobuf.ByteString getWhoBytes() {
+ java.lang.Object ref = who_;
+ if (ref instanceof String) {
+ com.google.protobuf.ByteString b =
+ com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref);
+ who_ = b;
+ return b;
+ } else {
+ return (com.google.protobuf.ByteString) ref;
+ }
+ }
+ /** string who = 1; */
+ public Builder setWho(java.lang.String value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+
+ who_ = value;
+ onChanged();
+ return this;
+ }
+ /** string who = 1; */
+ public Builder clearWho() {
+
+ who_ = getDefaultInstance().getWho();
+ onChanged();
+ return this;
+ }
+ /** string who = 1; */
+ public Builder setWhoBytes(com.google.protobuf.ByteString value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ checkByteStringIsUtf8(value);
+
+ who_ = value;
+ onChanged();
+ return this;
+ }
+
+ @java.lang.Override
+ public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) {
+ return super.setUnknownFields(unknownFields);
+ }
+
+ @java.lang.Override
+ public final Builder mergeUnknownFields(
+ final com.google.protobuf.UnknownFieldSet unknownFields) {
+ return super.mergeUnknownFields(unknownFields);
+ }
+
+ // @@protoc_insertion_point(builder_scope:com.ververica.statefun.examples.kafka.GreetRequest)
+ }
+
+ // @@protoc_insertion_point(class_scope:com.ververica.statefun.examples.kafka.GreetRequest)
+ private static final com.ververica.statefun.examples.greeter.generated.GreetRequest
+ DEFAULT_INSTANCE;
+
+ static {
+ DEFAULT_INSTANCE = new com.ververica.statefun.examples.greeter.generated.GreetRequest();
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetRequest
+ getDefaultInstance() {
+ return DEFAULT_INSTANCE;
+ }
+
+ private static final com.google.protobuf.Parser PARSER =
+ new com.google.protobuf.AbstractParser() {
+ @java.lang.Override
+ public GreetRequest parsePartialFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return new GreetRequest(input, extensionRegistry);
+ }
+ };
+
+ public static com.google.protobuf.Parser parser() {
+ return PARSER;
+ }
+
+ @java.lang.Override
+ public com.google.protobuf.Parser getParserForType() {
+ return PARSER;
+ }
+
+ @java.lang.Override
+ public com.ververica.statefun.examples.greeter.generated.GreetRequest
+ getDefaultInstanceForType() {
+ return DEFAULT_INSTANCE;
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/generated/GreetRequestOrBuilder.java b/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/generated/GreetRequestOrBuilder.java
new file mode 100644
index 00000000..b2dcf787
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/generated/GreetRequestOrBuilder.java
@@ -0,0 +1,15 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: src/main/protobuf/greeter.proto
+
+package com.ververica.statefun.examples.greeter.generated;
+
+public interface GreetRequestOrBuilder
+ extends
+ // @@protoc_insertion_point(interface_extends:com.ververica.statefun.examples.kafka.GreetRequest)
+ com.google.protobuf.MessageOrBuilder {
+
+ /** string who = 1; */
+ java.lang.String getWho();
+ /** string who = 1; */
+ com.google.protobuf.ByteString getWhoBytes();
+}
diff --git a/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/generated/GreetResponse.java b/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/generated/GreetResponse.java
new file mode 100644
index 00000000..c9bea49d
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/generated/GreetResponse.java
@@ -0,0 +1,639 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: src/main/protobuf/greeter.proto
+
+package com.ververica.statefun.examples.greeter.generated;
+
+/** Protobuf type {@code com.ververica.statefun.examples.kafka.GreetResponse} */
+public final class GreetResponse extends com.google.protobuf.GeneratedMessageV3
+ implements
+ // @@protoc_insertion_point(message_implements:com.ververica.statefun.examples.kafka.GreetResponse)
+ GreetResponseOrBuilder {
+ private static final long serialVersionUID = 0L;
+ // Use GreetResponse.newBuilder() to construct.
+ private GreetResponse(com.google.protobuf.GeneratedMessageV3.Builder> builder) {
+ super(builder);
+ }
+
+ private GreetResponse() {
+ who_ = "";
+ greeting_ = "";
+ }
+
+ @java.lang.Override
+ public final com.google.protobuf.UnknownFieldSet getUnknownFields() {
+ return this.unknownFields;
+ }
+
+ private GreetResponse(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ this();
+ if (extensionRegistry == null) {
+ throw new java.lang.NullPointerException();
+ }
+ int mutable_bitField0_ = 0;
+ com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+ com.google.protobuf.UnknownFieldSet.newBuilder();
+ try {
+ boolean done = false;
+ while (!done) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ done = true;
+ break;
+ case 10:
+ {
+ java.lang.String s = input.readStringRequireUtf8();
+
+ who_ = s;
+ break;
+ }
+ case 18:
+ {
+ java.lang.String s = input.readStringRequireUtf8();
+
+ greeting_ = s;
+ break;
+ }
+ default:
+ {
+ if (!parseUnknownField(input, unknownFields, extensionRegistry, tag)) {
+ done = true;
+ }
+ break;
+ }
+ }
+ }
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ throw e.setUnfinishedMessage(this);
+ } catch (java.io.IOException e) {
+ throw new com.google.protobuf.InvalidProtocolBufferException(e).setUnfinishedMessage(this);
+ } finally {
+ this.unknownFields = unknownFields.build();
+ makeExtensionsImmutable();
+ }
+ }
+
+ public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() {
+ return com.ververica.statefun.examples.greeter.generated.Greeter
+ .internal_static_com_ververica_statefun_examples_kafka_GreetResponse_descriptor;
+ }
+
+ @java.lang.Override
+ protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return com.ververica.statefun.examples.greeter.generated.Greeter
+ .internal_static_com_ververica_statefun_examples_kafka_GreetResponse_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ com.ververica.statefun.examples.greeter.generated.GreetResponse.class,
+ com.ververica.statefun.examples.greeter.generated.GreetResponse.Builder.class);
+ }
+
+ public static final int WHO_FIELD_NUMBER = 1;
+ private volatile java.lang.Object who_;
+ /** string who = 1; */
+ public java.lang.String getWho() {
+ java.lang.Object ref = who_;
+ if (ref instanceof java.lang.String) {
+ return (java.lang.String) ref;
+ } else {
+ com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
+ java.lang.String s = bs.toStringUtf8();
+ who_ = s;
+ return s;
+ }
+ }
+ /** string who = 1; */
+ public com.google.protobuf.ByteString getWhoBytes() {
+ java.lang.Object ref = who_;
+ if (ref instanceof java.lang.String) {
+ com.google.protobuf.ByteString b =
+ com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref);
+ who_ = b;
+ return b;
+ } else {
+ return (com.google.protobuf.ByteString) ref;
+ }
+ }
+
+ public static final int GREETING_FIELD_NUMBER = 2;
+ private volatile java.lang.Object greeting_;
+ /** string greeting = 2; */
+ public java.lang.String getGreeting() {
+ java.lang.Object ref = greeting_;
+ if (ref instanceof java.lang.String) {
+ return (java.lang.String) ref;
+ } else {
+ com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
+ java.lang.String s = bs.toStringUtf8();
+ greeting_ = s;
+ return s;
+ }
+ }
+ /** string greeting = 2; */
+ public com.google.protobuf.ByteString getGreetingBytes() {
+ java.lang.Object ref = greeting_;
+ if (ref instanceof java.lang.String) {
+ com.google.protobuf.ByteString b =
+ com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref);
+ greeting_ = b;
+ return b;
+ } else {
+ return (com.google.protobuf.ByteString) ref;
+ }
+ }
+
+ private byte memoizedIsInitialized = -1;
+
+ @java.lang.Override
+ public final boolean isInitialized() {
+ byte isInitialized = memoizedIsInitialized;
+ if (isInitialized == 1) return true;
+ if (isInitialized == 0) return false;
+
+ memoizedIsInitialized = 1;
+ return true;
+ }
+
+ @java.lang.Override
+ public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException {
+ if (!getWhoBytes().isEmpty()) {
+ com.google.protobuf.GeneratedMessageV3.writeString(output, 1, who_);
+ }
+ if (!getGreetingBytes().isEmpty()) {
+ com.google.protobuf.GeneratedMessageV3.writeString(output, 2, greeting_);
+ }
+ unknownFields.writeTo(output);
+ }
+
+ @java.lang.Override
+ public int getSerializedSize() {
+ int size = memoizedSize;
+ if (size != -1) return size;
+
+ size = 0;
+ if (!getWhoBytes().isEmpty()) {
+ size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, who_);
+ }
+ if (!getGreetingBytes().isEmpty()) {
+ size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, greeting_);
+ }
+ size += unknownFields.getSerializedSize();
+ memoizedSize = size;
+ return size;
+ }
+
+ @java.lang.Override
+ public boolean equals(final java.lang.Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof com.ververica.statefun.examples.greeter.generated.GreetResponse)) {
+ return super.equals(obj);
+ }
+ com.ververica.statefun.examples.greeter.generated.GreetResponse other =
+ (com.ververica.statefun.examples.greeter.generated.GreetResponse) obj;
+
+ if (!getWho().equals(other.getWho())) return false;
+ if (!getGreeting().equals(other.getGreeting())) return false;
+ if (!unknownFields.equals(other.unknownFields)) return false;
+ return true;
+ }
+
+ @java.lang.Override
+ public int hashCode() {
+ if (memoizedHashCode != 0) {
+ return memoizedHashCode;
+ }
+ int hash = 41;
+ hash = (19 * hash) + getDescriptor().hashCode();
+ hash = (37 * hash) + WHO_FIELD_NUMBER;
+ hash = (53 * hash) + getWho().hashCode();
+ hash = (37 * hash) + GREETING_FIELD_NUMBER;
+ hash = (53 * hash) + getGreeting().hashCode();
+ hash = (29 * hash) + unknownFields.hashCode();
+ memoizedHashCode = hash;
+ return hash;
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetResponse parseFrom(
+ java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetResponse parseFrom(
+ java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetResponse parseFrom(
+ com.google.protobuf.ByteString data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetResponse parseFrom(
+ com.google.protobuf.ByteString data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetResponse parseFrom(
+ byte[] data) throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetResponse parseFrom(
+ byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetResponse parseFrom(
+ java.io.InputStream input) throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetResponse parseFrom(
+ java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseWithIOException(
+ PARSER, input, extensionRegistry);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetResponse parseDelimitedFrom(
+ java.io.InputStream input) throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetResponse parseDelimitedFrom(
+ java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(
+ PARSER, input, extensionRegistry);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetResponse parseFrom(
+ com.google.protobuf.CodedInputStream input) throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input);
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetResponse parseFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseWithIOException(
+ PARSER, input, extensionRegistry);
+ }
+
+ @java.lang.Override
+ public Builder newBuilderForType() {
+ return newBuilder();
+ }
+
+ public static Builder newBuilder() {
+ return DEFAULT_INSTANCE.toBuilder();
+ }
+
+ public static Builder newBuilder(
+ com.ververica.statefun.examples.greeter.generated.GreetResponse prototype) {
+ return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+ }
+
+ @java.lang.Override
+ public Builder toBuilder() {
+ return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this);
+ }
+
+ @java.lang.Override
+ protected Builder newBuilderForType(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+ Builder builder = new Builder(parent);
+ return builder;
+ }
+ /** Protobuf type {@code com.ververica.statefun.examples.kafka.GreetResponse} */
+ public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder
+ implements
+ // @@protoc_insertion_point(builder_implements:com.ververica.statefun.examples.kafka.GreetResponse)
+ com.ververica.statefun.examples.greeter.generated.GreetResponseOrBuilder {
+ public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() {
+ return com.ververica.statefun.examples.greeter.generated.Greeter
+ .internal_static_com_ververica_statefun_examples_kafka_GreetResponse_descriptor;
+ }
+
+ @java.lang.Override
+ protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return com.ververica.statefun.examples.greeter.generated.Greeter
+ .internal_static_com_ververica_statefun_examples_kafka_GreetResponse_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ com.ververica.statefun.examples.greeter.generated.GreetResponse.class,
+ com.ververica.statefun.examples.greeter.generated.GreetResponse.Builder.class);
+ }
+
+ // Construct using com.ververica.statefun.examples.greeter.generated.GreetResponse.newBuilder()
+ private Builder() {
+ maybeForceBuilderInitialization();
+ }
+
+ private Builder(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+ super(parent);
+ maybeForceBuilderInitialization();
+ }
+
+ private void maybeForceBuilderInitialization() {
+ if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) {}
+ }
+
+ @java.lang.Override
+ public Builder clear() {
+ super.clear();
+ who_ = "";
+
+ greeting_ = "";
+
+ return this;
+ }
+
+ @java.lang.Override
+ public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() {
+ return com.ververica.statefun.examples.greeter.generated.Greeter
+ .internal_static_com_ververica_statefun_examples_kafka_GreetResponse_descriptor;
+ }
+
+ @java.lang.Override
+ public com.ververica.statefun.examples.greeter.generated.GreetResponse
+ getDefaultInstanceForType() {
+ return com.ververica.statefun.examples.greeter.generated.GreetResponse.getDefaultInstance();
+ }
+
+ @java.lang.Override
+ public com.ververica.statefun.examples.greeter.generated.GreetResponse build() {
+ com.ververica.statefun.examples.greeter.generated.GreetResponse result = buildPartial();
+ if (!result.isInitialized()) {
+ throw newUninitializedMessageException(result);
+ }
+ return result;
+ }
+
+ @java.lang.Override
+ public com.ververica.statefun.examples.greeter.generated.GreetResponse buildPartial() {
+ com.ververica.statefun.examples.greeter.generated.GreetResponse result =
+ new com.ververica.statefun.examples.greeter.generated.GreetResponse(this);
+ result.who_ = who_;
+ result.greeting_ = greeting_;
+ onBuilt();
+ return result;
+ }
+
+ @java.lang.Override
+ public Builder clone() {
+ return super.clone();
+ }
+
+ @java.lang.Override
+ public Builder setField(
+ com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) {
+ return super.setField(field, value);
+ }
+
+ @java.lang.Override
+ public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) {
+ return super.clearField(field);
+ }
+
+ @java.lang.Override
+ public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+ return super.clearOneof(oneof);
+ }
+
+ @java.lang.Override
+ public Builder setRepeatedField(
+ com.google.protobuf.Descriptors.FieldDescriptor field, int index, java.lang.Object value) {
+ return super.setRepeatedField(field, index, value);
+ }
+
+ @java.lang.Override
+ public Builder addRepeatedField(
+ com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) {
+ return super.addRepeatedField(field, value);
+ }
+
+ @java.lang.Override
+ public Builder mergeFrom(com.google.protobuf.Message other) {
+ if (other instanceof com.ververica.statefun.examples.greeter.generated.GreetResponse) {
+ return mergeFrom((com.ververica.statefun.examples.greeter.generated.GreetResponse) other);
+ } else {
+ super.mergeFrom(other);
+ return this;
+ }
+ }
+
+ public Builder mergeFrom(
+ com.ververica.statefun.examples.greeter.generated.GreetResponse other) {
+ if (other
+ == com.ververica.statefun.examples.greeter.generated.GreetResponse.getDefaultInstance())
+ return this;
+ if (!other.getWho().isEmpty()) {
+ who_ = other.who_;
+ onChanged();
+ }
+ if (!other.getGreeting().isEmpty()) {
+ greeting_ = other.greeting_;
+ onChanged();
+ }
+ this.mergeUnknownFields(other.unknownFields);
+ onChanged();
+ return this;
+ }
+
+ @java.lang.Override
+ public final boolean isInitialized() {
+ return true;
+ }
+
+ @java.lang.Override
+ public Builder mergeFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ com.ververica.statefun.examples.greeter.generated.GreetResponse parsedMessage = null;
+ try {
+ parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ parsedMessage =
+ (com.ververica.statefun.examples.greeter.generated.GreetResponse)
+ e.getUnfinishedMessage();
+ throw e.unwrapIOException();
+ } finally {
+ if (parsedMessage != null) {
+ mergeFrom(parsedMessage);
+ }
+ }
+ return this;
+ }
+
+ private java.lang.Object who_ = "";
+ /** string who = 1; */
+ public java.lang.String getWho() {
+ java.lang.Object ref = who_;
+ if (!(ref instanceof java.lang.String)) {
+ com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
+ java.lang.String s = bs.toStringUtf8();
+ who_ = s;
+ return s;
+ } else {
+ return (java.lang.String) ref;
+ }
+ }
+ /** string who = 1; */
+ public com.google.protobuf.ByteString getWhoBytes() {
+ java.lang.Object ref = who_;
+ if (ref instanceof String) {
+ com.google.protobuf.ByteString b =
+ com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref);
+ who_ = b;
+ return b;
+ } else {
+ return (com.google.protobuf.ByteString) ref;
+ }
+ }
+ /** string who = 1; */
+ public Builder setWho(java.lang.String value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+
+ who_ = value;
+ onChanged();
+ return this;
+ }
+ /** string who = 1; */
+ public Builder clearWho() {
+
+ who_ = getDefaultInstance().getWho();
+ onChanged();
+ return this;
+ }
+ /** string who = 1; */
+ public Builder setWhoBytes(com.google.protobuf.ByteString value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ checkByteStringIsUtf8(value);
+
+ who_ = value;
+ onChanged();
+ return this;
+ }
+
+ private java.lang.Object greeting_ = "";
+ /** string greeting = 2; */
+ public java.lang.String getGreeting() {
+ java.lang.Object ref = greeting_;
+ if (!(ref instanceof java.lang.String)) {
+ com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
+ java.lang.String s = bs.toStringUtf8();
+ greeting_ = s;
+ return s;
+ } else {
+ return (java.lang.String) ref;
+ }
+ }
+ /** string greeting = 2; */
+ public com.google.protobuf.ByteString getGreetingBytes() {
+ java.lang.Object ref = greeting_;
+ if (ref instanceof String) {
+ com.google.protobuf.ByteString b =
+ com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref);
+ greeting_ = b;
+ return b;
+ } else {
+ return (com.google.protobuf.ByteString) ref;
+ }
+ }
+ /** string greeting = 2; */
+ public Builder setGreeting(java.lang.String value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+
+ greeting_ = value;
+ onChanged();
+ return this;
+ }
+ /** string greeting = 2; */
+ public Builder clearGreeting() {
+
+ greeting_ = getDefaultInstance().getGreeting();
+ onChanged();
+ return this;
+ }
+ /** string greeting = 2; */
+ public Builder setGreetingBytes(com.google.protobuf.ByteString value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ checkByteStringIsUtf8(value);
+
+ greeting_ = value;
+ onChanged();
+ return this;
+ }
+
+ @java.lang.Override
+ public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) {
+ return super.setUnknownFields(unknownFields);
+ }
+
+ @java.lang.Override
+ public final Builder mergeUnknownFields(
+ final com.google.protobuf.UnknownFieldSet unknownFields) {
+ return super.mergeUnknownFields(unknownFields);
+ }
+
+ // @@protoc_insertion_point(builder_scope:com.ververica.statefun.examples.kafka.GreetResponse)
+ }
+
+ // @@protoc_insertion_point(class_scope:com.ververica.statefun.examples.kafka.GreetResponse)
+ private static final com.ververica.statefun.examples.greeter.generated.GreetResponse
+ DEFAULT_INSTANCE;
+
+ static {
+ DEFAULT_INSTANCE = new com.ververica.statefun.examples.greeter.generated.GreetResponse();
+ }
+
+ public static com.ververica.statefun.examples.greeter.generated.GreetResponse
+ getDefaultInstance() {
+ return DEFAULT_INSTANCE;
+ }
+
+ private static final com.google.protobuf.Parser PARSER =
+ new com.google.protobuf.AbstractParser() {
+ @java.lang.Override
+ public GreetResponse parsePartialFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return new GreetResponse(input, extensionRegistry);
+ }
+ };
+
+ public static com.google.protobuf.Parser parser() {
+ return PARSER;
+ }
+
+ @java.lang.Override
+ public com.google.protobuf.Parser getParserForType() {
+ return PARSER;
+ }
+
+ @java.lang.Override
+ public com.ververica.statefun.examples.greeter.generated.GreetResponse
+ getDefaultInstanceForType() {
+ return DEFAULT_INSTANCE;
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/generated/GreetResponseOrBuilder.java b/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/generated/GreetResponseOrBuilder.java
new file mode 100644
index 00000000..b57a8945
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/generated/GreetResponseOrBuilder.java
@@ -0,0 +1,20 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: src/main/protobuf/greeter.proto
+
+package com.ververica.statefun.examples.greeter.generated;
+
+public interface GreetResponseOrBuilder
+ extends
+ // @@protoc_insertion_point(interface_extends:com.ververica.statefun.examples.kafka.GreetResponse)
+ com.google.protobuf.MessageOrBuilder {
+
+ /** string who = 1; */
+ java.lang.String getWho();
+ /** string who = 1; */
+ com.google.protobuf.ByteString getWhoBytes();
+
+ /** string greeting = 2; */
+ java.lang.String getGreeting();
+ /** string greeting = 2; */
+ com.google.protobuf.ByteString getGreetingBytes();
+}
diff --git a/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/generated/Greeter.java b/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/generated/Greeter.java
new file mode 100644
index 00000000..ae2c4839
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-greeter-example/src/main/java/com/ververica/statefun/examples/greeter/generated/Greeter.java
@@ -0,0 +1,68 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: src/main/protobuf/greeter.proto
+
+package com.ververica.statefun.examples.greeter.generated;
+
+public final class Greeter {
+ private Greeter() {}
+
+ public static void registerAllExtensions(com.google.protobuf.ExtensionRegistryLite registry) {}
+
+ public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry registry) {
+ registerAllExtensions((com.google.protobuf.ExtensionRegistryLite) registry);
+ }
+
+ static final com.google.protobuf.Descriptors.Descriptor
+ internal_static_com_ververica_statefun_examples_kafka_GreetRequest_descriptor;
+ static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+ internal_static_com_ververica_statefun_examples_kafka_GreetRequest_fieldAccessorTable;
+ static final com.google.protobuf.Descriptors.Descriptor
+ internal_static_com_ververica_statefun_examples_kafka_GreetResponse_descriptor;
+ static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+ internal_static_com_ververica_statefun_examples_kafka_GreetResponse_fieldAccessorTable;
+
+ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() {
+ return descriptor;
+ }
+
+ private static com.google.protobuf.Descriptors.FileDescriptor descriptor;
+
+ static {
+ java.lang.String[] descriptorData = {
+ "\n\037src/main/protobuf/greeter.proto\022%com.v"
+ + "erverica.statefun.examples.kafka\"\033\n\014Gree"
+ + "tRequest\022\013\n\003who\030\001 \001(\t\".\n\rGreetResponse\022\013"
+ + "\n\003who\030\001 \001(\t\022\020\n\010greeting\030\002 \001(\tB5\n1com.ver"
+ + "verica.statefun.examples.greeter.generat"
+ + "edP\001b\006proto3"
+ };
+ com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
+ new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
+ public com.google.protobuf.ExtensionRegistry assignDescriptors(
+ com.google.protobuf.Descriptors.FileDescriptor root) {
+ descriptor = root;
+ return null;
+ }
+ };
+ com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom(
+ descriptorData, new com.google.protobuf.Descriptors.FileDescriptor[] {}, assigner);
+ internal_static_com_ververica_statefun_examples_kafka_GreetRequest_descriptor =
+ getDescriptor().getMessageTypes().get(0);
+ internal_static_com_ververica_statefun_examples_kafka_GreetRequest_fieldAccessorTable =
+ new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+ internal_static_com_ververica_statefun_examples_kafka_GreetRequest_descriptor,
+ new java.lang.String[] {
+ "Who",
+ });
+ internal_static_com_ververica_statefun_examples_kafka_GreetResponse_descriptor =
+ getDescriptor().getMessageTypes().get(1);
+ internal_static_com_ververica_statefun_examples_kafka_GreetResponse_fieldAccessorTable =
+ new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+ internal_static_com_ververica_statefun_examples_kafka_GreetResponse_descriptor,
+ new java.lang.String[] {
+ "Who", "Greeting",
+ });
+ }
+
+ // @@protoc_insertion_point(outer_class_scope)
+}
diff --git a/stateful-functions-examples/stateful-functions-greeter-example/src/main/protobuf/greeter.proto b/stateful-functions-examples/stateful-functions-greeter-example/src/main/protobuf/greeter.proto
new file mode 100644
index 00000000..01b0a604
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-greeter-example/src/main/protobuf/greeter.proto
@@ -0,0 +1,32 @@
+//
+// Copyright 2019 Ververica GmbH.
+//
+// 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.
+//
+
+syntax = "proto3";
+
+package com.ververica.statefun.examples.kafka;
+option java_package = "com.ververica.statefun.examples.greeter.generated";
+option java_multiple_files = true;
+
+message GreetRequest {
+ string who = 1;
+}
+
+message GreetResponse {
+ string who = 1;
+ string greeting = 2;
+}
+
+
diff --git a/stateful-functions-examples/stateful-functions-greeter-example/src/main/resources/META-INF/services/com.ververica.statefun.sdk.spi.StatefulFunctionModule b/stateful-functions-examples/stateful-functions-greeter-example/src/main/resources/META-INF/services/com.ververica.statefun.sdk.spi.StatefulFunctionModule
new file mode 100644
index 00000000..c22c3e67
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-greeter-example/src/main/resources/META-INF/services/com.ververica.statefun.sdk.spi.StatefulFunctionModule
@@ -0,0 +1,19 @@
+#
+# Copyright 2019 Ververica GmbH.
+#
+# 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 service loader entry so this module will be discovered
+# by the runtime.
+com.ververica.statefun.examples.greeter.GreetingModule
\ No newline at end of file
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/Dockerfile.functions b/stateful-functions-examples/stateful-functions-ridesharing-example/Dockerfile.functions
new file mode 100644
index 00000000..8bd1f85b
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/Dockerfile.functions
@@ -0,0 +1,19 @@
+# Copyright 2019 Ververica GmbH.
+#
+# 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 stateful-functions
+
+RUN mkdir -p /opt/stateful-functions/modules/stateful-functions-ridesharing-example
+COPY stateful-functions-ridesharing-example-functions/target/stateful-functions-ridesharing-example*jar /opt/stateful-functions/modules/stateful-functions-ridesharing-example/
+COPY stateful-functions-ridesharing-protocol/target/stateful-functions-ridesharing-protocol*jar /opt/stateful-functions/modules/stateful-functions-ridesharing-example/
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/README.md b/stateful-functions-examples/stateful-functions-ridesharing-example/README.md
new file mode 100644
index 00000000..7655d5f9
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/README.md
@@ -0,0 +1,28 @@
+# The Ridesharing Example
+
+The Ridesharing example is a more complicated Stateful Functions example consisting of 4 different functions, each
+corresponding to a real-world entity in a ridesharing scenario: `FnDriver`, `FnGeoCell`, `FnPassenger`, and `FnRide`.
+
+The whole example also includes a simulator program, which simulates real-world drivers and passengers sending
+events to the Stateful Functions application. Driver simulations will be sending their location updates to the
+application, while passenger simulations will be sending ride requests.
+
+## Running the example
+
+To run the example:
+
+```
+docker-compose build
+docker-compose up
+```
+
+This starts both the simulator program and Stateful Functions ridesharing example application.
+
+After all the components have fully started, you can take a look at the web UI of the Flink Jobmanager to see the
+application running, at `localhost:8081`.
+
+Then, you need to issue a request to the simulator program to start the simulation:
+
+```
+curl -X POST localhost:5656/api/start
+```
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/docker-compose.yml b/stateful-functions-examples/stateful-functions-ridesharing-example/docker-compose.yml
new file mode 100644
index 00000000..11c567fd
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/docker-compose.yml
@@ -0,0 +1,81 @@
+################################################################################
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+################################################################################
+version: "2.1"
+services:
+ zookeeper:
+ image: wurstmeister/zookeeper
+ ports:
+ - "2181:2181"
+ kafka-broker:
+ image: wurstmeister/kafka:2.12-2.0.1
+ ports:
+ - "9092:9092"
+ environment:
+ KAFKA_BROKER_ID: 1
+ HOSTNAME_COMMAND: "route -n | awk '/UG[ \t]/{print $$2}'"
+ KAFKA_CREATE_TOPICS: "to-driver:1:1,to-passenger:1:1,from-driver:1:1,from-passenger:1:1"
+ KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
+ depends_on:
+ - zookeeper
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock
+ - ./kafka:/kafka
+ master:
+ build:
+ dockerfile: Dockerfile.functions
+ context: .
+ # uncomment to start from a savepoint
+ #command: -s /checkpoint-dir/savepoint/savepoint-bf101a-4e99820085fe
+ expose:
+ - "6123"
+ ports:
+ - "8081:8081"
+ environment:
+ - ROLE=master
+ - MASTER_HOST=master
+ volumes:
+ - ./checkpoint-dir:/checkpoint-dir
+ worker:
+ build:
+ dockerfile: Dockerfile.functions
+ context: .
+ expose:
+ - "6121"
+ - "6122"
+ depends_on:
+ - master
+ - kafka-broker
+ links:
+ - "master:master"
+ - "kafka-broker:kafka-broker"
+ environment:
+ - ROLE=worker
+ - MASTER_HOST=master
+ volumes:
+ - ./checkpoint-dir:/checkpoint-dir
+ simulator:
+ build:
+ context: ./stateful-functions-ridesharing-example-simulator
+ expose:
+ - "5656"
+ ports:
+ - "5656:5656"
+ depends_on:
+ - kafka-broker
+ - worker
+
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/pom.xml b/stateful-functions-examples/stateful-functions-ridesharing-example/pom.xml
new file mode 100644
index 00000000..02e504bf
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/pom.xml
@@ -0,0 +1,38 @@
+
+
+
+
+ stateful-functions-examples
+ com.ververica
+ 1.0-SNAPSHOT
+ ..
+
+ 4.0.0
+ pom
+
+ stateful-functions-ridesharing-example
+
+
+ stateful-functions-ridesharing-protocol
+ stateful-functions-ridesharing-example-functions
+ stateful-functions-ridesharing-example-simulator
+
+
+
+
\ No newline at end of file
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/pom.xml b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/pom.xml
new file mode 100644
index 00000000..2bdef6bf
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/pom.xml
@@ -0,0 +1,59 @@
+
+
+
+
+ stateful-functions-ridesharing-example
+ com.ververica
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ stateful-functions-ridesharing-example-functions
+
+
+
+
+ com.ververica
+ stateful-functions-ridesharing-protocol
+ 1.0-SNAPSHOT
+
+
+
+
+ com.ververica
+ stateful-functions-sdk
+ ${project.version}
+
+
+ com.ververica
+ stateful-functions-kafka-io
+ ${project.version}
+
+
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
+
+
+
\ No newline at end of file
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/FnDriver.java b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/FnDriver.java
new file mode 100644
index 00000000..3fa305a3
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/FnDriver.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.ridesharing;
+
+import com.ververica.statefun.examples.ridesharing.generated.DriverJoinsRide;
+import com.ververica.statefun.examples.ridesharing.generated.DriverRejectsPickup;
+import com.ververica.statefun.examples.ridesharing.generated.InboundDriverMessage;
+import com.ververica.statefun.examples.ridesharing.generated.JoinCell;
+import com.ververica.statefun.examples.ridesharing.generated.LeaveCell;
+import com.ververica.statefun.examples.ridesharing.generated.OutboundDriverMessage;
+import com.ververica.statefun.examples.ridesharing.generated.PickupPassenger;
+import com.ververica.statefun.examples.ridesharing.generated.RideEnded;
+import com.ververica.statefun.examples.ridesharing.generated.RideStarted;
+import com.ververica.statefun.sdk.Context;
+import com.ververica.statefun.sdk.FunctionType;
+import com.ververica.statefun.sdk.annotations.Persisted;
+import com.ververica.statefun.sdk.match.MatchBinder;
+import com.ververica.statefun.sdk.match.StatefulMatchFunction;
+import com.ververica.statefun.sdk.state.PersistedValue;
+
+public class FnDriver extends StatefulMatchFunction {
+
+ static final FunctionType TYPE = new FunctionType(Identifiers.NAMESPACE, "driver");
+
+ @Persisted
+ private final PersistedValue currentRideId = PersistedValue.of("ride", String.class);
+
+ @Persisted
+ private final PersistedValue location = PersistedValue.of("location", Integer.class);
+
+ @Override
+ public void configure(MatchBinder binder) {
+ binder
+ .predicate(PickupPassenger.class, this::whenPickupIsNeeded)
+ .predicate(
+ InboundDriverMessage.class,
+ InboundDriverMessage::hasRideStarted,
+ this::whenRideHasStarted)
+ .predicate(
+ InboundDriverMessage.class, InboundDriverMessage::hasRideEnded, this::whenRideHasEnded)
+ .predicate(
+ InboundDriverMessage.class,
+ InboundDriverMessage::hasLocationUpdate,
+ this::whenLocationIsUpdated);
+ }
+
+ private void whenPickupIsNeeded(Context context, PickupPassenger pickupPassenger) {
+ if (isTaken()) {
+ // this driver is currently in a ride, and therefore can't take any more
+ // passengers.
+ context.reply(
+ DriverRejectsPickup.newBuilder()
+ .setDriverId(context.self().id())
+ .setRideId(context.caller().id())
+ .build());
+ return;
+ }
+ // We are called by the ride function, so we remember it's id for future communication.
+ currentRideId.set(context.caller().id());
+
+ // We also need to unregister ourselves from the current geo cell we belong to.
+ final int currentLocation =
+ location.getOrDefault(0); // drivers should have a location at this point.
+ context.send(FnGeoCell.TYPE, String.valueOf(currentLocation), LeaveCell.getDefaultInstance());
+
+ // reply to the ride, saying we are taking this passenger
+ context.reply(
+ DriverJoinsRide.newBuilder()
+ .setDriverId(context.self().id())
+ .setDriverLocation(currentLocation)
+ .build());
+
+ // also send a command to the physical driver to pickup the passenger
+ context.send(
+ Identifiers.TO_OUTBOUND_DRIVER,
+ OutboundDriverMessage.newBuilder()
+ .setDriverId(context.self().id())
+ .setPickupPassenger(
+ OutboundDriverMessage.PickupPassenger.newBuilder()
+ .setRideId(pickupPassenger.getPassengerId())
+ .setStartGeoLocation(pickupPassenger.getPassengerStartCell())
+ .setEndGeoLocation(pickupPassenger.getPassengerEndCell())
+ .build())
+ .build());
+ }
+
+ private void whenRideHasStarted(Context context, InboundDriverMessage ignored) {
+ context.send(
+ FnRide.TYPE,
+ currentRideId.get(),
+ RideStarted.newBuilder()
+ .setDriverId(context.self().id())
+ .setDriverGeoCell(location.get())
+ .build());
+ }
+
+ private void whenRideHasEnded(Context context, InboundDriverMessage ignored) {
+ context.send(FnRide.TYPE, currentRideId.get(), RideEnded.getDefaultInstance());
+ currentRideId.clear();
+
+ // register at the current location as free driver.
+ Integer currentLocation = location.get();
+ context.send(FnGeoCell.TYPE, String.valueOf(currentLocation), JoinCell.getDefaultInstance());
+ }
+
+ private void whenLocationIsUpdated(Context context, InboundDriverMessage locationUpdate) {
+ final int updated = locationUpdate.getLocationUpdate().getCurrentGeoCell();
+ final int last = location.getOrDefault(-1);
+ if (last == -1) {
+ // this is the first time this driver gets a location update.
+ // so we notify the relevant geo cell function.
+ location.set(updated);
+ context.send(FnGeoCell.TYPE, String.valueOf(updated), JoinCell.getDefaultInstance());
+ return;
+ }
+ if (last == updated) {
+ return;
+ }
+ location.set(updated);
+ }
+
+ private boolean isTaken() {
+ String rideId = currentRideId.get();
+ return rideId != null;
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/FnGeoCell.java b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/FnGeoCell.java
new file mode 100644
index 00000000..e7846760
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/FnGeoCell.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.ridesharing;
+
+import com.ververica.statefun.examples.ridesharing.generated.*;
+import com.ververica.statefun.sdk.Address;
+import com.ververica.statefun.sdk.Context;
+import com.ververica.statefun.sdk.FunctionType;
+import com.ververica.statefun.sdk.StatefulFunction;
+import com.ververica.statefun.sdk.annotations.Persisted;
+import com.ververica.statefun.sdk.state.PersistedValue;
+
+public class FnGeoCell implements StatefulFunction {
+ static final FunctionType TYPE = new FunctionType(Identifiers.NAMESPACE, "geo-cell");
+
+ @Persisted
+ private final PersistedValue drivers =
+ PersistedValue.of("drivers", GeoCellState.class);
+
+ @Override
+ public void invoke(Context context, Object input) {
+ Address caller = context.caller();
+ if (input instanceof JoinCell) {
+ addDriver(caller);
+ } else if (input instanceof LeaveCell) {
+ removeDriver(caller);
+ } else if (input instanceof GetDriver) {
+ getDriver(context);
+ } else {
+ throw new IllegalStateException("Unknown message type " + input);
+ }
+ }
+
+ private void getDriver(Context context) {
+ final GeoCellState state = drivers.get();
+
+ if (hasDriver(state)) {
+ String nextDriverId = state.getDriverIdList().get(0);
+ context.reply(DriverInCell.newBuilder().setDriverId(nextDriverId).build());
+ } else {
+ context.reply(DriverInCell.newBuilder().build());
+ }
+ }
+
+ private void addDriver(Address driver) {
+ GeoCellState state = drivers.get();
+ if (state == null) {
+ state = GeoCellState.newBuilder().addDriverId(driver.id()).build();
+ } else {
+ state = state.toBuilder().addDriverId(driver.id()).build();
+ }
+ drivers.set(state);
+ }
+
+ private void removeDriver(Address driver) {
+ GeoCellState state = drivers.get();
+ if (state == null) {
+ return;
+ }
+ GeoCellState.Builder nextState = state.toBuilder();
+ nextState.clearDriverId();
+
+ for (String otherDriverID : state.getDriverIdList()) {
+ if (!otherDriverID.equals(driver.id())) {
+ nextState.addDriverId(otherDriverID);
+ }
+ }
+ drivers.set(nextState.build());
+ }
+
+ private boolean hasDriver(GeoCellState registeredDrivers) {
+ return registeredDrivers != null && !registeredDrivers.getDriverIdList().isEmpty();
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/FnPassenger.java b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/FnPassenger.java
new file mode 100644
index 00000000..c6e2b642
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/FnPassenger.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.ridesharing;
+
+import com.ververica.statefun.examples.ridesharing.generated.DriverJoinsRide;
+import com.ververica.statefun.examples.ridesharing.generated.InboundPassengerMessage;
+import com.ververica.statefun.examples.ridesharing.generated.InboundPassengerMessage.RequestRide;
+import com.ververica.statefun.examples.ridesharing.generated.OutboundPassengerMessage;
+import com.ververica.statefun.examples.ridesharing.generated.PassengerJoinsRide;
+import com.ververica.statefun.examples.ridesharing.generated.RideEnded;
+import com.ververica.statefun.examples.ridesharing.generated.RideFailed;
+import com.ververica.statefun.examples.ridesharing.generated.RideStarted;
+import com.ververica.statefun.sdk.Context;
+import com.ververica.statefun.sdk.FunctionType;
+import com.ververica.statefun.sdk.match.MatchBinder;
+import com.ververica.statefun.sdk.match.StatefulMatchFunction;
+import java.util.concurrent.ThreadLocalRandom;
+
+public class FnPassenger extends StatefulMatchFunction {
+
+ static final FunctionType TYPE = new FunctionType(Identifiers.NAMESPACE, "passenger");
+
+ @Override
+ public void configure(MatchBinder binder) {
+ binder
+ .predicate(
+ InboundPassengerMessage.class,
+ InboundPassengerMessage::hasRequestRide,
+ this::whenRideIsRequested)
+ .predicate(DriverJoinsRide.class, this::whenDriverJoins)
+ .predicate(RideFailed.class, this::whenRideFails)
+ .predicate(RideStarted.class, this::whenRideHasStarted)
+ .predicate(RideEnded.class, this::whenRideHasEnded);
+ }
+
+ private void whenRideIsRequested(Context context, InboundPassengerMessage request) {
+ String passengerID = context.self().id();
+ String rideId = "ride-" + ThreadLocalRandom.current().nextLong();
+
+ RequestRide rideRequest = request.getRequestRide();
+ PassengerJoinsRide joinRide =
+ PassengerJoinsRide.newBuilder()
+ .setPassengerId(passengerID)
+ .setStartGeoCell(rideRequest.getStartGeoCell())
+ .setEndGeoCell(rideRequest.getEndGeoCell())
+ .build();
+
+ context.send(FnRide.TYPE, rideId, joinRide);
+ }
+
+ private void whenRideHasEnded(Context context, RideEnded ignored) {
+ final OutboundPassengerMessage out =
+ OutboundPassengerMessage.newBuilder()
+ .setPassengerId(context.self().id())
+ .setRideEnded(OutboundPassengerMessage.RideEnded.newBuilder().build())
+ .build();
+
+ context.send(Identifiers.TO_PASSENGER_EGRESS, out);
+ }
+
+ private void whenRideHasStarted(Context context, RideStarted rideStarted) {
+ final OutboundPassengerMessage out =
+ OutboundPassengerMessage.newBuilder()
+ .setPassengerId(context.self().id())
+ .setRideStarted(
+ OutboundPassengerMessage.RideStarted.newBuilder()
+ .setDriverId(rideStarted.getDriverId())
+ .build())
+ .build();
+
+ context.send(Identifiers.TO_PASSENGER_EGRESS, out);
+ }
+
+ private void whenDriverJoins(Context context, DriverJoinsRide message) {
+ final OutboundPassengerMessage out =
+ OutboundPassengerMessage.newBuilder()
+ .setPassengerId(context.self().id())
+ .setDriverFound(
+ OutboundPassengerMessage.DriverHasBeenFound.newBuilder()
+ .setDriverId(message.getDriverId())
+ .setDriverGeoCell(message.getDriverLocation())
+ .build())
+ .build();
+
+ context.send(Identifiers.TO_PASSENGER_EGRESS, out);
+ }
+
+ private void whenRideFails(Context context, RideFailed rideFailed) {
+ final OutboundPassengerMessage out =
+ OutboundPassengerMessage.newBuilder()
+ .setPassengerId(context.self().id())
+ .setRideFailed(
+ OutboundPassengerMessage.RideFailed.newBuilder()
+ .setRideId(rideFailed.getRideId())
+ .build())
+ .build();
+
+ context.send(Identifiers.TO_PASSENGER_EGRESS, out);
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/FnRide.java b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/FnRide.java
new file mode 100644
index 00000000..7164ca56
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/FnRide.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.ridesharing;
+
+import com.ververica.statefun.examples.ridesharing.generated.DriverInCell;
+import com.ververica.statefun.examples.ridesharing.generated.DriverJoinsRide;
+import com.ververica.statefun.examples.ridesharing.generated.DriverRejectsPickup;
+import com.ververica.statefun.examples.ridesharing.generated.GetDriver;
+import com.ververica.statefun.examples.ridesharing.generated.PassengerJoinsRide;
+import com.ververica.statefun.examples.ridesharing.generated.PickupPassenger;
+import com.ververica.statefun.examples.ridesharing.generated.RideEnded;
+import com.ververica.statefun.examples.ridesharing.generated.RideFailed;
+import com.ververica.statefun.examples.ridesharing.generated.RideStarted;
+import com.ververica.statefun.sdk.Context;
+import com.ververica.statefun.sdk.FunctionType;
+import com.ververica.statefun.sdk.annotations.Persisted;
+import com.ververica.statefun.sdk.match.MatchBinder;
+import com.ververica.statefun.sdk.match.StatefulMatchFunction;
+import com.ververica.statefun.sdk.state.PersistedValue;
+
+final class FnRide extends StatefulMatchFunction {
+
+ static final FunctionType TYPE = new FunctionType(Identifiers.NAMESPACE, "ride");
+
+ @Persisted
+ private final PersistedValue passenger =
+ PersistedValue.of("passenger", PassengerJoinsRide.class);
+
+ @Persisted
+ private final PersistedValue driver = PersistedValue.of("driver", String.class);
+
+ public void configure(MatchBinder binder) {
+ binder
+ .predicate(PassengerJoinsRide.class, this::whenPassengerJoins)
+ .predicate(DriverInCell.class, this::whenGeoCellResponds)
+ .predicate(DriverRejectsPickup.class, this::whenDriverRejectsPickup)
+ .predicate(DriverJoinsRide.class, this::whenDriverJoins)
+ .predicate(RideStarted.class, this::whenRideHasStarted)
+ .predicate(RideEnded.class, this::whenRideHasEnded);
+ }
+
+ /**
+ * When a user joins a ride, we have to: 1. remember that user id 2. remember the starting
+ * location of that ride 3. contact the geo cell of the starting location, and ask for a free
+ * driver
+ */
+ private void whenPassengerJoins(Context context, PassengerJoinsRide in) {
+ final String cellKey = String.valueOf(in.getStartGeoCell());
+ passenger.set(in);
+
+ context.send(FnGeoCell.TYPE, cellKey, GetDriver.getDefaultInstance());
+ }
+
+ /**
+ * Geo cell responds, it might respond with: - there is no driver, in that case we fail the ride -
+ * there is a driver, let's ask them to pickup the passenger.
+ */
+ private void whenGeoCellResponds(Context context, DriverInCell in) {
+ final String driverId = in.getDriverId();
+ final PassengerJoinsRide rideRequest = passenger.get();
+ if (driverId != null && !driverId.isEmpty()) {
+ context.send(
+ FnDriver.TYPE,
+ driverId,
+ PickupPassenger.newBuilder()
+ .setPassengerId(rideRequest.getPassengerId())
+ .setPassengerStartCell(rideRequest.getStartGeoCell())
+ .setPassengerEndCell(rideRequest.getEndGeoCell())
+ .build());
+ return;
+ }
+ // no free drivers in this geo cell, at this example we just fail the ride
+ // but we can imagine that this is where we will expand our search to near geo cells
+ context.send(FnPassenger.TYPE, rideRequest.getPassengerId(), RideFailed.getDefaultInstance());
+
+ // by clearing our state, we essentially delete this instance of the ride actor
+ passenger.clear();
+ }
+
+ /**
+ * A driver might not be free, or for some other reason they can't take this ride, so we try
+ * another driver in that cell.
+ */
+ @SuppressWarnings("unused")
+ private void whenDriverRejectsPickup(Context context, DriverRejectsPickup ignored) {
+ // try another driver, realistically we need to pass in a list of 'banned' drivers,
+ // so that the GeoCell will not offer us these drivers again, but in this example
+ // if a driver rejects a ride, it means that he is currently busy (and it would soon delete
+ // itself from the geo cell)
+ final int startGeoCell = passenger.get().getStartGeoCell();
+ String cellKey = String.valueOf(startGeoCell);
+ context.send(FnGeoCell.TYPE, cellKey, GetDriver.getDefaultInstance());
+ }
+
+ /** A driver has taken this ride. */
+ private void whenDriverJoins(Context context, DriverJoinsRide driverJoinRide) {
+ driver.set(context.caller().id());
+ context.send(FnPassenger.TYPE, passenger.get().getPassengerId(), driverJoinRide);
+ }
+
+ /** A driver has successfully picked up the passenger */
+ private void whenRideHasStarted(Context context, RideStarted rideStarted) {
+ context.send(FnPassenger.TYPE, passenger.get().getPassengerId(), rideStarted);
+ }
+
+ /** The driver has successfully reached the destination. */
+ private void whenRideHasEnded(Context context, RideEnded rideEnded) {
+ context.send(FnPassenger.TYPE, passenger.get().getPassengerId(), rideEnded);
+ passenger.clear();
+ driver.clear();
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/FunctionProvider.java b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/FunctionProvider.java
new file mode 100644
index 00000000..8f8f0776
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/FunctionProvider.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.ridesharing;
+
+import com.ververica.statefun.sdk.FunctionType;
+import com.ververica.statefun.sdk.StatefulFunction;
+import com.ververica.statefun.sdk.StatefulFunctionProvider;
+
+public class FunctionProvider implements StatefulFunctionProvider {
+
+ @Override
+ public StatefulFunction functionOfType(FunctionType type) {
+ if (type.equals(FnPassenger.TYPE)) {
+ return new FnPassenger();
+ } else if (type.equals(FnDriver.TYPE)) {
+ return new FnDriver();
+ } else if (type.equals(FnRide.TYPE)) {
+ return new FnRide();
+ } else if (type.equals(FnGeoCell.TYPE)) {
+ return new FnGeoCell();
+ } else {
+ throw new IllegalArgumentException("Unknown type " + type);
+ }
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/Identifiers.java b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/Identifiers.java
new file mode 100644
index 00000000..7c933af8
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/Identifiers.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.ridesharing;
+
+import com.ververica.statefun.examples.ridesharing.generated.InboundDriverMessage;
+import com.ververica.statefun.examples.ridesharing.generated.InboundPassengerMessage;
+import com.ververica.statefun.examples.ridesharing.generated.OutboundDriverMessage;
+import com.ververica.statefun.examples.ridesharing.generated.OutboundPassengerMessage;
+import com.ververica.statefun.sdk.io.EgressIdentifier;
+import com.ververica.statefun.sdk.io.IngressIdentifier;
+
+final class Identifiers {
+
+ static final String NAMESPACE = "com.ververica.statefun.examples.ridesharing";
+
+ static final IngressIdentifier FROM_PASSENGERS =
+ new IngressIdentifier<>(InboundPassengerMessage.class, NAMESPACE, "from-passenger");
+
+ static final IngressIdentifier FROM_DRIVER =
+ new IngressIdentifier<>(InboundDriverMessage.class, NAMESPACE, "from-driver");
+
+ static EgressIdentifier TO_PASSENGER_EGRESS =
+ new EgressIdentifier<>(NAMESPACE, "to-passenger", OutboundPassengerMessage.class);
+
+ static EgressIdentifier TO_OUTBOUND_DRIVER =
+ new EgressIdentifier<>(NAMESPACE, "to-driver", OutboundDriverMessage.class);
+}
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/InboundDriverRouter.java b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/InboundDriverRouter.java
new file mode 100644
index 00000000..8b79663b
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/InboundDriverRouter.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.ridesharing;
+
+import com.ververica.statefun.examples.ridesharing.generated.InboundDriverMessage;
+import com.ververica.statefun.sdk.io.Router;
+
+public class InboundDriverRouter implements Router {
+
+ @Override
+ public void route(InboundDriverMessage message, Downstream downstream) {
+ downstream.forward(FnDriver.TYPE, message.getDriverId(), message);
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/InboundPassengerRouter.java b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/InboundPassengerRouter.java
new file mode 100644
index 00000000..dc6b0380
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/InboundPassengerRouter.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.ridesharing;
+
+import com.ververica.statefun.examples.ridesharing.generated.InboundPassengerMessage;
+import com.ververica.statefun.sdk.io.Router;
+
+public class InboundPassengerRouter implements Router {
+
+ @Override
+ public void route(
+ InboundPassengerMessage message, Downstream downstream) {
+ downstream.forward(FnPassenger.TYPE, message.getPassengerId(), message);
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/KafkaSpecs.java b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/KafkaSpecs.java
new file mode 100644
index 00000000..833b7352
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/KafkaSpecs.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.ridesharing;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.ververica.statefun.examples.ridesharing.generated.InboundDriverMessage;
+import com.ververica.statefun.examples.ridesharing.generated.InboundPassengerMessage;
+import com.ververica.statefun.examples.ridesharing.generated.OutboundDriverMessage;
+import com.ververica.statefun.examples.ridesharing.generated.OutboundPassengerMessage;
+import com.ververica.statefun.sdk.io.EgressSpec;
+import com.ververica.statefun.sdk.io.IngressSpec;
+import com.ververica.statefun.sdk.kafka.KafkaEgressBuilder;
+import com.ververica.statefun.sdk.kafka.KafkaEgressSerializer;
+import com.ververica.statefun.sdk.kafka.KafkaIngressBuilder;
+import com.ververica.statefun.sdk.kafka.KafkaIngressDeserializer;
+import java.nio.charset.StandardCharsets;
+import org.apache.kafka.clients.consumer.ConsumerConfig;
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.apache.kafka.clients.producer.ProducerRecord;
+
+final class KafkaSpecs {
+
+ private static final String KAFKA_SERVER = "kafka-broker:9092";
+ private static final String TO_PASSENGER_KAFKA_TOPIC_NAME = "to-passenger";
+ private static final String TO_DRIVER_TOPIC_NAME = "to-driver";
+ private static final String FROM_DRIVER_TOPIC_NAME = "from-driver";
+ private static final String FROM_PASSENGER_TOPIC_NAME = "from-passenger";
+
+ static IngressSpec FROM_DRIVER_SPEC =
+ KafkaIngressBuilder.forIdentifier(Identifiers.FROM_DRIVER)
+ .withKafkaAddress(KAFKA_SERVER)
+ .withTopic(FROM_DRIVER_TOPIC_NAME)
+ .withProperty(ConsumerConfig.GROUP_ID_CONFIG, "statefun-from-driver-group")
+ .withDeserializer(FromDriverDeserializer.class)
+ .build();
+
+ static IngressSpec FROM_PASSENGER_SPEC =
+ KafkaIngressBuilder.forIdentifier(Identifiers.FROM_PASSENGERS)
+ .withKafkaAddress(KAFKA_SERVER)
+ .withTopic(FROM_PASSENGER_TOPIC_NAME)
+ .withProperty(ConsumerConfig.GROUP_ID_CONFIG, "statefun-from-passenger-group")
+ .withDeserializer(FromPassengersDeserializer.class)
+ .build();
+
+ static EgressSpec TO_PASSENGER_SPEC =
+ KafkaEgressBuilder.forIdentifier(Identifiers.TO_PASSENGER_EGRESS)
+ .withKafkaAddress(KAFKA_SERVER)
+ .withSerializer(ToPassengersSerializer.class)
+ .build();
+
+ static EgressSpec TO_DRIVER_SPEC =
+ KafkaEgressBuilder.forIdentifier(Identifiers.TO_OUTBOUND_DRIVER)
+ .withKafkaAddress(KAFKA_SERVER)
+ .withSerializer(ToDriverSerializer.class)
+ .build();
+
+ private static final class FromDriverDeserializer
+ implements KafkaIngressDeserializer {
+
+ private static final long serialVersionUID = 1;
+
+ @Override
+ public InboundDriverMessage deserialize(ConsumerRecord input) {
+ try {
+ return InboundDriverMessage.parseFrom(input.value());
+ } catch (InvalidProtocolBufferException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ private static final class FromPassengersDeserializer
+ implements KafkaIngressDeserializer {
+
+ private static final long serialVersionUID = 1;
+
+ @Override
+ public InboundPassengerMessage deserialize(ConsumerRecord input) {
+ try {
+ return InboundPassengerMessage.parseFrom(input.value());
+ } catch (InvalidProtocolBufferException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ private static final class ToPassengersSerializer
+ implements KafkaEgressSerializer {
+
+ private static final long serialVersionUID = 1;
+
+ @Override
+ public ProducerRecord serialize(OutboundPassengerMessage message) {
+ byte[] key = message.getPassengerId().getBytes(StandardCharsets.UTF_8);
+ byte[] value = message.toByteArray();
+ return new ProducerRecord<>(TO_PASSENGER_KAFKA_TOPIC_NAME, key, value);
+ }
+ }
+
+ private static final class ToDriverSerializer
+ implements KafkaEgressSerializer {
+
+ private static final long serialVersionUID = 1;
+
+ @Override
+ public ProducerRecord serialize(OutboundDriverMessage message) {
+ byte[] key = message.getDriverId().getBytes(StandardCharsets.UTF_8);
+ byte[] value = message.toByteArray();
+ return new ProducerRecord<>(TO_DRIVER_TOPIC_NAME, key, value);
+ }
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/Module.java b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/Module.java
new file mode 100644
index 00000000..b383dc7b
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/java/com/ververica/statefun/examples/ridesharing/Module.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.ridesharing;
+
+import com.ververica.statefun.sdk.spi.StatefulFunctionModule;
+import java.util.Map;
+
+public class Module implements StatefulFunctionModule {
+
+ @Override
+ public void configure(Map globalConfiguration, Binder binder) {
+ FunctionProvider provider = new FunctionProvider();
+ binder.bindFunctionProvider(FnPassenger.TYPE, provider);
+ binder.bindFunctionProvider(FnDriver.TYPE, provider);
+ binder.bindFunctionProvider(FnRide.TYPE, provider);
+ binder.bindFunctionProvider(FnGeoCell.TYPE, provider);
+
+ binder.bindIngress(KafkaSpecs.FROM_DRIVER_SPEC);
+ binder.bindIngressRouter(Identifiers.FROM_DRIVER, new InboundDriverRouter());
+ binder.bindEgress(KafkaSpecs.TO_DRIVER_SPEC);
+
+ binder.bindIngress(KafkaSpecs.FROM_PASSENGER_SPEC);
+ binder.bindIngressRouter(Identifiers.FROM_PASSENGERS, new InboundPassengerRouter());
+ binder.bindEgress(KafkaSpecs.TO_PASSENGER_SPEC);
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/resources/META-INF/services/com.ververica.statefun.sdk.spi.StatefulFunctionModule b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/resources/META-INF/services/com.ververica.statefun.sdk.spi.StatefulFunctionModule
new file mode 100644
index 00000000..3c2a4af7
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-functions/src/main/resources/META-INF/services/com.ververica.statefun.sdk.spi.StatefulFunctionModule
@@ -0,0 +1,15 @@
+# Copyright 2019 Ververica GmbH.
+#
+# 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.
+
+com.ververica.statefun.examples.ridesharing.Module
\ No newline at end of file
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-simulator/Dockerfile b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-simulator/Dockerfile
new file mode 100644
index 00000000..7d803bcb
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-simulator/Dockerfile
@@ -0,0 +1,14 @@
+# Start with a base image containing Java runtime
+FROM openjdk:8-jdk-alpine
+
+# Add a volume pointing to /tmp
+VOLUME /tmp
+
+# Make port 5656 available to the world outside this container
+EXPOSE 5656
+
+ADD target/*-simulator-*.jar simulator.jar
+ADD application.yaml application.yaml
+
+# Run the jar file
+ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-Dspring.profiles.active=dev","-Dspring.config.location=file:/application.yaml","-jar","/simulator.jar"]
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-simulator/application.yaml b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-simulator/application.yaml
new file mode 100644
index 00000000..4762011e
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-simulator/application.yaml
@@ -0,0 +1,43 @@
+# Copyright 2019 Ververica GmbH.
+#
+# 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.
+
+server:
+ port: 5656
+
+security.auth.enabled: false
+security.basic.enable: false
+security.ignored: /**
+
+simulation:
+ grid: 50
+ drivers: 2500
+ passengers: 10
+
+kafka:
+ topic:
+ from-driver: from-driver
+ to-driver: to-driver
+ from-passenger: from-passenger
+ to-passenger: to-passenger
+ listeners:
+ transactions.id: simulator-java-listener
+ bootstrap-servers: kafka-broker:9092
+
+web-socket:
+ topic:
+ passenger: /topic/passenger
+ driver: /topic/driver
+
+management.endpoints.web.exposure.include: mappings, loggers
+
\ No newline at end of file
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-simulator/pom.xml b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-simulator/pom.xml
new file mode 100644
index 00000000..a62de0a6
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-simulator/pom.xml
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.6.RELEASE
+
+
+
+ 4.0.0
+
+ stateful-functions-ridesharing-example-simulator
+
+
+ UTF-8
+ UTF-8
+ 1.8
+ 1.2.3
+ 2.9.2
+
+
+
+
+
+ com.ververica
+ stateful-functions-ridesharing-protocol
+ 1.0-SNAPSHOT
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-websocket
+
+
+
+ org.springframework.kafka
+ spring-kafka
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ true
+
+
+
+
+ org.webjars
+ sockjs-client
+ 1.0.2
+
+
+ org.webjars
+ stomp-websocket
+ 2.3.3
+
+
+
+
+
+ com.google.protobuf
+ protobuf-java
+ 3.8.0
+
+
+ com.google.guava
+ guava
+ 28.0-jre
+
+
+
+ org.projectlombok
+ lombok
+ 1.16.16
+ provided
+
+
+ ch.qos.logback
+ logback-classic
+ ${logback.version}
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ maven-resources-plugin
+
+
+ copy-resources
+ process-classes
+
+ copy-resources
+
+
+ ${basedir}/target/classes/static
+
+
+ build
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+
+ 1.8
+
+
+
+
+
+ com.diffplug.spotless
+ spotless-maven-plugin
+ 1.20.0
+
+
+
+ 1.7
+
+
+
+
+
+
+
+ spotless-check
+ verify
+
+ check
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-simulator/src/main/java/com/ververica/statefun/examples/ridesharing/simulator/Main.java b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-simulator/src/main/java/com/ververica/statefun/examples/ridesharing/simulator/Main.java
new file mode 100644
index 00000000..a658aba7
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-simulator/src/main/java/com/ververica/statefun/examples/ridesharing/simulator/Main.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.ridesharing.simulator;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.ComponentScan;
+
+@SpringBootApplication
+@EnableAutoConfiguration
+@ComponentScan("com.ververica")
+public class Main {
+ public static void main(String[] args) {
+ SpringApplication.run(Main.class, args);
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-simulator/src/main/java/com/ververica/statefun/examples/ridesharing/simulator/configurations/KafkaConsumerConfig.java b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-simulator/src/main/java/com/ververica/statefun/examples/ridesharing/simulator/configurations/KafkaConsumerConfig.java
new file mode 100644
index 00000000..364e093c
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-simulator/src/main/java/com/ververica/statefun/examples/ridesharing/simulator/configurations/KafkaConsumerConfig.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.ridesharing.simulator.configurations;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import org.apache.kafka.clients.consumer.ConsumerConfig;
+import org.apache.kafka.common.serialization.ByteArrayDeserializer;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.kafka.annotation.EnableKafka;
+import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
+import org.springframework.kafka.config.KafkaListenerContainerFactory;
+import org.springframework.kafka.core.ConsumerFactory;
+import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
+import org.springframework.kafka.listener.ConcurrentMessageListenerContainer;
+
+@EnableKafka
+@Configuration
+public class KafkaConsumerConfig {
+ private final String bootstrapServer;
+
+ public KafkaConsumerConfig(@Value("${kafka.bootstrap-servers}") String bootstrapServer) {
+ this.bootstrapServer = Objects.requireNonNull(bootstrapServer);
+ }
+
+ @Bean
+ public ConsumerFactory consumerFactory() {
+ Map props = new HashMap<>();
+ props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServer);
+ props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class);
+ props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class);
+ props.put(ConsumerConfig.GROUP_ID_CONFIG, "simulator-java.group");
+ props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest");
+ return new DefaultKafkaConsumerFactory<>(props);
+ }
+
+ @Bean
+ public KafkaListenerContainerFactory>
+ kafkaListenerContainerFactory(ConsumerFactory consumerFactory) {
+ ConcurrentKafkaListenerContainerFactory factory =
+ new ConcurrentKafkaListenerContainerFactory<>();
+ factory.setConsumerFactory(consumerFactory);
+ return factory;
+ }
+}
diff --git a/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-simulator/src/main/java/com/ververica/statefun/examples/ridesharing/simulator/configurations/KafkaProducerConfig.java b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-simulator/src/main/java/com/ververica/statefun/examples/ridesharing/simulator/configurations/KafkaProducerConfig.java
new file mode 100644
index 00000000..ecac02cc
--- /dev/null
+++ b/stateful-functions-examples/stateful-functions-ridesharing-example/stateful-functions-ridesharing-example-simulator/src/main/java/com/ververica/statefun/examples/ridesharing/simulator/configurations/KafkaProducerConfig.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2019 Ververica GmbH.
+ *
+ * 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.ververica.statefun.examples.ridesharing.simulator.configurations;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import org.apache.kafka.clients.producer.ProducerConfig;
+import org.apache.kafka.common.serialization.ByteArraySerializer;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.kafka.core.DefaultKafkaProducerFactory;
+import org.springframework.kafka.core.KafkaTemplate;
+import org.springframework.kafka.core.ProducerFactory;
+
+@Configuration
+public class KafkaProducerConfig {
+ private final String bootstrapServers;
+
+ public KafkaProducerConfig(@Value("${kafka.bootstrap-servers}") String bootstrapServers) {
+ this.bootstrapServers = Objects.requireNonNull(bootstrapServers);
+ }
+
+ @Bean
+ public ProducerFactory