diff --git a/README.html b/README.html new file mode 100644 index 000000000..78e4606ba --- /dev/null +++ b/README.html @@ -0,0 +1,746 @@ + + + + + + + +Introduction + + + + + + + + + +
+
+
+
+
+Build Status +
+
+
+
+
+

Introduction

+
+
+

Spring Cloud Function is a project with the following high-level goals:

+
+
+
    +
  • +

    Promote the implementation of business logic via functions.

    +
  • +
  • +

    Decouple the development lifecycle of business logic from any specific runtime target so that the same code can run as a web endpoint, a stream processor, or a task.

    +
  • +
  • +

    Support a uniform programming model across serverless providers, as well as the ability to run standalone (locally or in a PaaS).

    +
  • +
  • +

    Enable Spring Boot features (auto-configuration, dependency injection, metrics) on serverless providers.

    +
  • +
+
+
+

It abstracts away all of the transport details and +infrastructure, allowing the developer to keep all the familiar tools +and processes, and focus firmly on business logic.

+
+
+

Here’s a complete, executable, testable Spring Boot application +(implementing a simple string manipulation):

+
+
+
+
@SpringBootApplication
+public class Application {
+
+  @Bean
+  public Function<Flux<String>, Flux<String>> uppercase() {
+    return flux -> flux.map(value -> value.toUpperCase());
+  }
+
+  public static void main(String[] args) {
+    SpringApplication.run(Application.class, args);
+  }
+}
+
+
+
+

It’s just a Spring Boot application, so it can be built, run and +tested, locally and in a CI build, the same way as any other Spring +Boot application. The Function is from java.util and Flux is a +Reactive Streams Publisher from +Project Reactor. The function can be +accessed over HTTP or messaging.

+
+
+

Spring Cloud Function has 4 main features:

+
+
+
    +
  1. +

    Wrappers for @Beans of type Function, Consumer and +Supplier, exposing them to the outside world as either HTTP +endpoints and/or message stream listeners/publishers with RabbitMQ, Kafka etc.

    +
  2. +
  3. +

    Compiling strings which are Java function bodies into bytecode, and +then turning them into @Beans that can be wrapped as above.

    +
  4. +
  5. +

    Deploying a JAR file containing such an application context with an +isolated classloader, so that you can pack them together in a single +JVM.

    +
  6. +
  7. +

    Adapters for AWS Lambda, Azure, Apache OpenWhisk and possibly other "serverless" service providers.

    +
  8. +
+
+
+
+
+

Getting Started

+
+
+

Build from the command line (and "install" the samples):

+
+
+
+
$ ./mvnw clean install
+
+
+
+

(If you like to YOLO add -DskipTests.)

+
+
+

Run one of the samples, e.g.

+
+
+
+
$ java -jar spring-cloud-function-samples/function-sample/target/*.jar
+
+
+
+

This runs the app and exposes its functions over HTTP, so you can +convert a string to uppercase, like this:

+
+
+
+
$ curl -H "Content-Type: text/plain" localhost:8080/uppercase -d Hello
+HELLO
+
+
+
+

You can convert multiple strings (a Flux<String>) by separating them +with new lines

+
+
+
+
$ curl -H "Content-Type: text/plain" localhost:8080/uppercase -d 'Hello
+> World'
+HELLOWORLD
+
+
+
+

(You can use QJ in a terminal to insert a new line in a literal +string like that.)

+
+
+
+
+

Building and Running a Function

+
+
+

The sample @SpringBootApplication above has a function that can be +decorated at runtime by Spring Cloud Function to be an HTTP endpoint, +or a Stream processor, for instance with RabbitMQ, Apache Kafka or +JMS.

+
+
+

The @Beans can be Function, Consumer or Supplier (all from +java.util), and their parametric types can be String or POJO.

+
+
+

Functions can also be of Flux<String> or Flux<Pojo> and Spring +Cloud Function takes care of converting the data to and from the +desired types, as long as it comes in as plain text or (in the case of +the POJO) JSON. There is also support for Message<Pojo> where the +message headers are copied from the incoming event, depending on the +adapter. The web adapter also supports conversion from form-encoded +data to a Map, and if you are using the function with Spring Cloud +Stream then all the conversion and coercion features for message +payloads will be applicable as well.

+
+
+

Functions can be grouped together in a single application, or deployed +one-per-jar. It’s up to the developer to choose. An app with multiple +functions can be deployed multiple times in different "personalities", +exposing different functions over different physical transports.

+
+
+
+
+

Building

+
+
+

Basic Compile and Test

+
+

To build the source you will need to install JDK 1.7.

+
+
+

Spring Cloud uses Maven for most build-related activities, and you +should be able to get off the ground quite quickly by cloning the +project you are interested in and typing

+
+
+
+
$ ./mvnw install
+
+
+
+ + + + + +
+ + +You can also install Maven (>=3.3.3) yourself and run the mvn command +in place of ./mvnw in the examples below. If you do that you also +might need to add -P spring if your local Maven settings do not +contain repository declarations for spring pre-release artifacts. +
+
+
+ + + + + +
+ + +Be aware that you might need to increase the amount of memory +available to Maven by setting a MAVEN_OPTS environment variable with +a value like -Xmx512m -XX:MaxPermSize=128m. We try to cover this in +the .mvn configuration, so if you find you have to do it to make a +build succeed, please raise a ticket to get the settings added to +source control. +
+
+
+

For hints on how to build the project look in .travis.yml if there +is one. There should be a "script" and maybe "install" command. Also +look at the "services" section to see if any services need to be +running locally (e.g. mongo or rabbit). Ignore the git-related bits +that you might find in "before_install" since they’re related to setting git +credentials and you already have those.

+
+
+

The projects that require middleware generally include a +docker-compose.yml, so consider using +Docker Compose to run the middeware servers +in Docker containers. See the README in the +scripts demo +repository for specific instructions about the common cases of mongo, +rabbit and redis.

+
+
+ + + + + +
+ + +If all else fails, build with the command from .travis.yml (usually +./mvnw install). +
+
+
+
+

Documentation

+
+

The spring-cloud-build module has a "docs" profile, and if you switch +that on it will try to build asciidoc sources from +src/main/asciidoc. As part of that process it will look for a +README.adoc and process it by loading all the includes, but not +parsing or rendering it, just copying it to ${main.basedir} +(defaults to ${basedir}, i.e. the root of the project). If there are +any changes in the README it will then show up after a Maven build as +a modified file in the correct place. Just commit it and push the change.

+
+
+
+

Working with the code

+
+

If you don’t have an IDE preference we would recommend that you use +Spring Tools Suite or +Eclipse when working with the code. We use the +m2eclipse eclipse plugin for maven support. Other IDEs and tools +should also work without issue as long as they use Maven 3.3.3 or better.

+
+
+

Importing into eclipse with m2eclipse

+
+

We recommend the m2eclipse eclipse plugin when working with +eclipse. If you don’t already have m2eclipse installed it is available from the "eclipse +marketplace".

+
+
+ + + + + +
+ + +Older versions of m2e do not support Maven 3.3, so once the +projects are imported into Eclipse you will also need to tell +m2eclipse to use the right profile for the projects. If you +see many different errors related to the POMs in the projects, check +that you have an up to date installation. If you can’t upgrade m2e, +add the "spring" profile to your settings.xml. Alternatively you can +copy the repository settings from the "spring" profile of the parent +pom into your settings.xml. +
+
+
+
+

Importing into eclipse without m2eclipse

+
+

If you prefer not to use m2eclipse you can generate eclipse project metadata using the +following command:

+
+
+
+
$ ./mvnw eclipse:eclipse
+
+
+
+

The generated eclipse projects can be imported by selecting import existing projects +from the file menu.

+
+
+
+
+
+
+

Contributing

+
+
+

Spring Cloud is released under the non-restrictive Apache 2.0 license, +and follows a very standard Github development process, using Github +tracker for issues and merging pull requests into master. If you want +to contribute even something trivial please do not hesitate, but +follow the guidelines below.

+
+
+

Sign the Contributor License Agreement

+
+

Before we accept a non-trivial patch or pull request we will need you to sign the +Contributor License Agreement. +Signing the contributor’s agreement does not grant anyone commit rights to the main +repository, but it does mean that we can accept your contributions, and you will get an +author credit if we do. Active contributors might be asked to join the core team, and +given the ability to merge pull requests.

+
+
+
+

Code of Conduct

+
+

This project adheres to the Contributor Covenant code of +conduct. By participating, you are expected to uphold this code. Please report +unacceptable behavior to spring-code-of-conduct@pivotal.io.

+
+
+
+

Code Conventions and Housekeeping

+
+

None of these is essential for a pull request, but they will all help. They can also be +added after the original pull request but before a merge.

+
+
+
    +
  • +

    Use the Spring Framework code format conventions. If you use Eclipse +you can import formatter settings using the +eclipse-code-formatter.xml file from the +Spring +Cloud Build project. If using IntelliJ, you can use the +Eclipse Code Formatter +Plugin to import the same file.

    +
  • +
  • +

    Make sure all new .java files to have a simple Javadoc class comment with at least an +@author tag identifying you, and preferably at least a paragraph on what the class is +for.

    +
  • +
  • +

    Add the ASF license header comment to all new .java files (copy from existing files +in the project)

    +
  • +
  • +

    Add yourself as an @author to the .java files that you modify substantially (more +than cosmetic changes).

    +
  • +
  • +

    Add some Javadocs and, if you change the namespace, some XSD doc elements.

    +
  • +
  • +

    A few unit tests would help a lot as well — someone has to do it.

    +
  • +
  • +

    If no-one else is using your branch, please rebase it against the current master (or +other target branch in the main project).

    +
  • +
  • +

    When writing a commit message please follow these conventions, +if you are fixing an existing issue please add Fixes gh-XXXX at the end of the commit +message (where XXXX is the issue number).

    +
  • +
+
+
+
+

Checkstyle

+
+

Spring Cloud Build comes with a set of checkstyle rules. You can find them in the spring-cloud-build-tools module. The most notable files under the module are:

+
+
+
spring-cloud-build-tools/
+
+
└── src
+    ├── checkstyle
+    │   └── checkstyle-suppressions.xml (3)
+    └── main
+        └── resources
+            ├── checkstyle-header.txt (2)
+            └── checkstyle.xml (1)
+
+
+
+ + + + + + + + + + + + + +
1Default Checkstyle rules
2File header setup
3Default suppression rules
+
+
+

Checkstyle configuration

+
+

Checkstyle rules are disabled by default. To add checkstyle to your project just define the following properties and plugins.

+
+
+
pom.xml
+
+
<properties>
+<maven-checkstyle-plugin.failsOnError>true</maven-checkstyle-plugin.failsOnError> (1)
+        <maven-checkstyle-plugin.failsOnViolation>true
+        </maven-checkstyle-plugin.failsOnViolation> (2)
+        <maven-checkstyle-plugin.includeTestSourceDirectory>true
+        </maven-checkstyle-plugin.includeTestSourceDirectory> (3)
+</properties>
+
+<build>
+        <plugins>
+            <plugin> (4)
+                <groupId>io.spring.javaformat</groupId>
+                <artifactId>spring-javaformat-maven-plugin</artifactId>
+            </plugin>
+            <plugin> (5)
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-checkstyle-plugin</artifactId>
+            </plugin>
+        </plugins>
+
+    <reporting>
+        <plugins>
+            <plugin> (5)
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-checkstyle-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </reporting>
+</build>
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
1Fails the build upon Checkstyle errors
2Fails the build upon Checkstyle violations
3Checkstyle analyzes also the test sources
4Add the Spring Java Format plugin that will reformat your code to pass most of the Checkstyle formatting rules
5Add checkstyle plugin to your build and reporting phases
+
+
+

If you need to suppress some rules (e.g. line length needs to be longer), then it’s enough for you to define a file under ${project.root}/src/checkstyle/checkstyle-suppressions.xml with your suppressions. Example:

+
+
+
projectRoot/src/checkstyle/checkstyle-suppresions.xml
+
+
<?xml version="1.0"?>
+<!DOCTYPE suppressions PUBLIC
+		"-//Puppy Crawl//DTD Suppressions 1.1//EN"
+		"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
+<suppressions>
+	<suppress files=".*ConfigServerApplication\.java" checks="HideUtilityClassConstructor"/>
+	<suppress files=".*ConfigClientWatch\.java" checks="LineLengthCheck"/>
+</suppressions>
+
+
+
+

It’s advisable to copy the ${spring-cloud-build.rootFolder}/.editorconfig and ${spring-cloud-build.rootFolder}/.springformat to your project. That way, some default formatting rules will be applied. You can do so by running this script:

+
+
+
+
$ curl https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/.editorconfig -o .editorconfig
+$ touch .springformat
+
+
+
+
+
+

IDE setup

+
+

Intellij IDEA

+
+

In order to setup Intellij you should import our coding conventions, inspection profiles and set up the checkstyle plugin.

+
+
+
spring-cloud-build-tools/
+
+
└── src
+    ├── checkstyle
+    │   └── checkstyle-suppressions.xml (3)
+    └── main
+        └── resources
+            ├── checkstyle-header.txt (2)
+            ├── checkstyle.xml (1)
+            └── intellij
+                ├── Intellij_Project_Defaults.xml (4)
+                └── Intellij_Spring_Boot_Java_Conventions.xml (5)
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
1Default Checkstyle rules
2File header setup
3Default suppression rules
4Project defaults for Intellij that apply most of Checkstyle rules
5Project style conventions for Intellij that apply most of Checkstyle rules
+
+
+
+Code style +
+
Figure 1. Code style
+
+
+

Go to FileSettingsEditorCode style. There click on the icon next to the Scheme section. There, click on the Import Scheme value and pick the Intellij IDEA code style XML option. Import the spring-cloud-build-tools/src/main/resources/intellij/Intellij_Spring_Boot_Java_Conventions.xml file.

+
+
+
+Code style +
+
Figure 2. Inspection profiles
+
+
+

Go to FileSettingsEditorInspections. There click on the icon next to the Profile section. There, click on the Import Profile and import the spring-cloud-build-tools/src/main/resources/intellij/Intellij_Project_Defaults.xml file.

+
+
+
Checkstyle
+

To have Intellij work with Checkstyle, you have to install the Checkstyle plugin. It’s advisable to also install the Assertions2Assertj to automatically convert the JUnit assertions

+
+
+
+Checkstyle +
+
+
+

Go to FileSettingsOther settingsCheckstyle. There click on the + icon in the Configuration file section. There, you’ll have to define where the checkstyle rules should be picked from. In the image above, we’ve picked the rules from the cloned Spring Cloud Build repository. However, you can point to the Spring Cloud Build’s GitHub repository (e.g. for the checkstyle.xml : https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/main/resources/checkstyle.xml). We need to provide the following variables:

+
+
+ +
+
+ + + + + +
+ + +Remember to set the Scan Scope to All sources since we apply checkstyle rules for production and test sources. +
+
+
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/aws-intro.html b/aws-intro.html new file mode 100644 index 000000000..b7939c96c --- /dev/null +++ b/aws-intro.html @@ -0,0 +1,165 @@ + + + + + + + +Notes on JAR Layout + + + + + + + + + +
+
+
+
+

The adapter has a couple of generic request handlers that you can use. The most generic is SpringBootStreamHandler, which uses a Jackson ObjectMapper provided by Spring Boot to serialize and deserialize the objects in the function. There is also a SpringBootRequestHandler which you can extend, and provide the input and output types as type parameters (enabling AWS to inspect the class and do the JSON conversions itself).

+
+
+

If your app has more than one @Bean of type Function etc. then you can choose the one to use by configuring function.name (e.g. as FUNCTION_NAME environment variable in AWS). The functions are extracted from the Spring Cloud FunctionCatalog (searching first for Function then Consumer and finally Supplier).

+
+
+
+
+

Notes on JAR Layout

+
+
+

You don’t need the Spring Cloud Function Web or Stream adapter at runtime in Lambda, so you might need to exclude those before you create the JAR you send to AWS. A Lambda application has to be shaded, but a Spring Boot standalone application does not, so you can run the same app using 2 separate jars (as per the sample). The sample app creates 2 jar files, one with an aws classifier for deploying in Lambda, and one executable (thin) jar that includes spring-cloud-function-web at runtime. Spring Cloud Function will try and locate a "main class" for you from the JAR file manifest, using the Start-Class attribute (which will be added for you by the Spring Boot tooling if you use the starter parent). If there is no Start-Class in your manifest you can use an environment variable MAIN_CLASS when you deploy the function to AWS.

+
+
+
+
+

Upload

+
+
+

Build the sample under spring-cloud-function-samples/function-sample-aws and upload the -aws jar file to Lambda. The handler can be example.Handler or org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler (FQN of the class, not a method reference, although Lambda does accept method references).

+
+
+
+
./mvnw -U clean package
+
+
+
+

Using the AWS command line tools it looks like this:

+
+
+
+
aws lambda create-function --function-name Uppercase --role arn:aws:iam::[USERID]:role/service-role/[ROLE] --zip-file fileb://function-sample-aws/target/function-sample-aws-2.0.0.BUILD-SNAPSHOT-aws.jar --handler org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler --description "Spring Cloud Function Adapter Example" --runtime java8 --region us-east-1 --timeout 30 --memory-size 1024 --publish
+
+
+
+

The input type for the function in the AWS sample is a Foo with a single property called "value". So you would need this to test it:

+
+
+
+
{
+  "value": "test"
+}
+
+
+
+ + + + + +
+ + +The AWS sample app is written in the "functional" style (as an ApplicationContextInitializer). This is much faster on startup in Lambda than the traditional @Bean style, so if you don’t need @Beans (or @EnableAutoConfiguration) it’s a good choice. Warm starts are not affected. +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/aws-readme.html b/aws-readme.html new file mode 100644 index 000000000..825327a5c --- /dev/null +++ b/aws-readme.html @@ -0,0 +1,168 @@ + + + + + + + +Notes on JAR Layout + + + + + + + + + +
+
+
+
+

This project provides an adapter layer for a Spring Cloud Function application onto AWS Lambda. You can write an app with a single @Bean of type Function, Consumer or Supplier and it will be deployable in AWS if you get the JAR file laid out right. The best way to make it work is to include spring-cloud-function-context as a dependency, but not the higher level adapters (e.g. spring-cloud-function-stream).

+
+
+

The adapter has a couple of generic request handlers that you can use. The most generic is SpringBootStreamHandler, which uses a Jackson ObjectMapper provided by Spring Boot to serialize and deserialize the objects in the function. There is also a SpringBootRequestHandler which you can extend, and provide the input and output types as type parameters (enabling AWS to inspect the class and do the JSON conversions itself).

+
+
+

If your app has more than one @Bean of type Function etc. then you can choose the one to use by configuring function.name (e.g. as FUNCTION_NAME environment variable in AWS). The functions are extracted from the Spring Cloud FunctionCatalog (searching first for Function then Consumer and finally Supplier).

+
+
+
+
+

Notes on JAR Layout

+
+
+

You don’t need the Spring Cloud Function Web or Stream adapter at runtime in Lambda, so you might need to exclude those before you create the JAR you send to AWS. A Lambda application has to be shaded, but a Spring Boot standalone application does not, so you can run the same app using 2 separate jars (as per the sample). The sample app creates 2 jar files, one with an aws classifier for deploying in Lambda, and one executable (thin) jar that includes spring-cloud-function-web at runtime. Spring Cloud Function will try and locate a "main class" for you from the JAR file manifest, using the Start-Class attribute (which will be added for you by the Spring Boot tooling if you use the starter parent). If there is no Start-Class in your manifest you can use an environment variable MAIN_CLASS when you deploy the function to AWS.

+
+
+
+
+

Upload

+
+
+

Build the sample under spring-cloud-function-samples/function-sample-aws and upload the -aws jar file to Lambda. The handler can be example.Handler or org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler (FQN of the class, not a method reference, although Lambda does accept method references).

+
+
+
+
./mvnw -U clean package
+
+
+
+

Using the AWS command line tools it looks like this:

+
+
+
+
aws lambda create-function --function-name Uppercase --role arn:aws:iam::[USERID]:role/service-role/[ROLE] --zip-file fileb://function-sample-aws/target/function-sample-aws-2.0.0.BUILD-SNAPSHOT-aws.jar --handler org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler --description "Spring Cloud Function Adapter Example" --runtime java8 --region us-east-1 --timeout 30 --memory-size 1024 --publish
+
+
+
+

The input type for the function in the AWS sample is a Foo with a single property called "value". So you would need this to test it:

+
+
+
+
{
+  "value": "test"
+}
+
+
+
+ + + + + +
+ + +The AWS sample app is written in the "functional" style (as an ApplicationContextInitializer). This is much faster on startup in Lambda than the traditional @Bean style, so if you don’t need @Beans (or @EnableAutoConfiguration) it’s a good choice. Warm starts are not affected. +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/aws.html b/aws.html new file mode 100644 index 000000000..0071af271 --- /dev/null +++ b/aws.html @@ -0,0 +1,282 @@ + + + + + + + +Introduction + + + + + + + + + +
+
+
+
+

2.1.0.BUILD-SNAPSHOT

+
+
+

The AWS adapter takes a Spring Cloud Function app and converts it to a form that can run in AWS Lambda.

+
+
+
+
+

Introduction

+
+
+

The adapter has a couple of generic request handlers that you can use. The most generic is SpringBootStreamHandler, which uses a Jackson ObjectMapper provided by Spring Boot to serialize and deserialize the objects in the function. There is also a SpringBootRequestHandler which you can extend, and provide the input and output types as type parameters (enabling AWS to inspect the class and do the JSON conversions itself).

+
+
+

If your app has more than one @Bean of type Function etc. then you can choose the one to use by configuring function.name (e.g. as FUNCTION_NAME environment variable in AWS). The functions are extracted from the Spring Cloud FunctionCatalog (searching first for Function then Consumer and finally Supplier).

+
+
+
+
+

Notes on JAR Layout

+
+
+

You don’t need the Spring Cloud Function Web or Stream adapter at runtime in Lambda, so you might need to exclude those before you create the JAR you send to AWS. A Lambda application has to be shaded, but a Spring Boot standalone application does not, so you can run the same app using 2 separate jars (as per the sample). The sample app creates 2 jar files, one with an aws classifier for deploying in Lambda, and one executable (thin) jar that includes spring-cloud-function-web at runtime. Spring Cloud Function will try and locate a "main class" for you from the JAR file manifest, using the Start-Class attribute (which will be added for you by the Spring Boot tooling if you use the starter parent). If there is no Start-Class in your manifest you can use an environment variable MAIN_CLASS when you deploy the function to AWS.

+
+
+
+
+

Upload

+
+
+

Build the sample under spring-cloud-function-samples/function-sample-aws and upload the -aws jar file to Lambda. The handler can be example.Handler or org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler (FQN of the class, not a method reference, although Lambda does accept method references).

+
+
+
+
./mvnw -U clean package
+
+
+
+

Using the AWS command line tools it looks like this:

+
+
+
+
aws lambda create-function --function-name Uppercase --role arn:aws:iam::[USERID]:role/service-role/[ROLE] --zip-file fileb://function-sample-aws/target/function-sample-aws-2.0.0.BUILD-SNAPSHOT-aws.jar --handler org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler --description "Spring Cloud Function Adapter Example" --runtime java8 --region us-east-1 --timeout 30 --memory-size 1024 --publish
+
+
+
+

The input type for the function in the AWS sample is a Foo with a single property called "value". So you would need this to test it:

+
+
+
+
{
+  "value": "test"
+}
+
+
+
+ + + + + +
+ + +The AWS sample app is written in the "functional" style (as an ApplicationContextInitializer). This is much faster on startup in Lambda than the traditional @Bean style, so if you don’t need @Beans (or @EnableAutoConfiguration) it’s a good choice. Warm starts are not affected. +
+
+
+
+
+

Functional Bean Definitions

+
+
+

Your functions will start much quicker if you can use functional bean definitions instead of @Bean. To do this make your main class +an ApplicationContextInitalizer<GenericApplicationContext> and use the registerBean() methods in GenericApplicationContext to +create all the beans you need. You function need sto be registered as a bean of type FunctionRegistration so that the input and +output types can be accessed by the framework. There is an example in github (the AWS sample is written in this style). It would +look something like this:

+
+
+
+
@SpringBootApplication
+public class FuncApplication implements ApplicationContextInitializer<GenericApplicationContext> {
+
+	public static void main(String[] args) throws Exception {
+		FunctionalSpringApplication.run(FuncApplication.class, args);
+	}
+
+	public Function<Foo, Bar> function() {
+		return value -> new Bar(value.uppercase()));
+	}
+
+	@Override
+	public void initialize(GenericApplicationContext context) {
+		context.registerBean("function", FunctionRegistration.class,
+			() -> new FunctionRegistration<Function<Foo, Bar>>(function())
+				.type(FunctionType.from(Foo.class).to(Bar.class).getType()));
+	}
+
+}
+
+
+
+
+
+

Platfom Specific Features

+
+
+

HTTP and API Gateway

+
+

AWS has some platform-specific data types, including batching of messages, which is much more efficient than processing each one individually. To make use of these types you can write a function that depends on those types. Or you can rely on Spring to extract the data from the AWS types and convert it to a Spring Message. To do this you tell AWS that the function is of a specific generic handler type (depending on the AWS service) and provide a bean of type Function<Message<S>,Message<T>>, where S and T are your business data types. If there is more than one bean of type Function you may also need to configure the Spring Boot property function.name to be the name of the target bean (e.g. use FUNCTION_NAME as an environment variable).

+
+
+

The supported AWS services and generic handler types are listed below:

+
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + +
ServiceAWS TypesGeneric Handler

API Gateway

APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent

org.springframework.cloud.function.adapter.aws.SpringBootApiGatewayRequestHandler

Kinesis

KinesisEvent

org.springframework.cloud.function.adapter.aws.SpringBootKinesisEventHandler

+
+

For example, to deploy behind an API Gateway, use --handler org.springframework.cloud.function.adapter.aws.SpringBootApiGatewayRequestHandler in your AWS command line (in via the UI) and define a @Bean of type Function<Message<Foo>,Message<Bar>> where Foo and Bar are POJO types (the data will be marshalled and unmarshalled by AWS using Jackson).

+
+
+
+
+
+

Custom Runtime

+
+
+

An AWS Lambda custom runtime can be created really easily using the HTTP export features in Spring Cloud Function Web. To make this work just add Spring Cloud Function AWS and Spring Cloud Function Web as dependencies in your project and set the following in your application.properties:

+
+
+
+
spring.cloud.function.web.export.enabled=true
+
+
+
+

Set the handler name in AWS to the name of your function. Then provide a bootstrap script in the root of your zip/jar that runs the Spring Boot application. The functional bean definition style works for custom runtimes too, and is faster than the @Bean style, so the example FuncApplication above would work. A custom runtime can start up much quicker even than a functional bean implementation of a Java lambda - it depends mostly on the number of classes you need to load at runtime. Spring doesn’t do very much here, so you can reduce the cold start time by only using primitive types in your function, for instance, and not doing any work in custom @PostConstruct initializers.

+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/azure-intro.html b/azure-intro.html new file mode 100644 index 000000000..d30536334 --- /dev/null +++ b/azure-intro.html @@ -0,0 +1,228 @@ + + + + + + + +Accessing Azure ExecutionContext + + + + + + + + + +
+
+
+
+

This project provides an adapter layer for a Spring Cloud Function application onto Azure. +You can write an app with a single @Bean of type Function and it will be deployable in Azure if you get the JAR file laid out right.

+
+
+

There is an AzureSpringBootRequestHandler which you must extend, and provide the input and output types as annotated method parameters (enabling Azure to inspect the class and create JSON bindings). The base class has two useful methods (handleRequest and handleOutput) to which you can delegate the actual function call, so mostly the function will only ever have one line.

+
+
+

Example:

+
+
+
+
public class FooHandler extends AzureSpringBootRequestHandler<Foo, Bar> {
+	@FunctionName("uppercase")
+	public Bar execute(
+			@HttpTrigger(name = "req", methods = { HttpMethod.GET,
+					HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS)
+                    Foo foo,
+			ExecutionContext context) {
+		return handleRequest(foo, context);
+	}
+}
+
+
+
+

This Azure handler will delegate to a Function<Foo,Bar> bean (or a Function<Publisher<Foo>,Publisher<Bar>>). Some Azure triggers (e.g. @CosmosDBTrigger) result in a input type of List and in that case you can bind to List in the Azure handler, or String (the raw JSON). The List input delegates to a Function with input type Map<String,Object>, or Publisher or List of the same type. The output of the Function can be a List (one-for-one) or a single value (aggregation), and the output binding in the Azure declaration should match.

+
+
+

If your app has more than one @Bean of type Function etc. then you can choose the one to use by configuring function.name. Or if you make the @FunctionName in the Azure handler method match the function name it should work that way (also for function apps with multiple functions). The functions are extracted from the Spring Cloud FunctionCatalog so the default function names are the same as the bean names.

+
+
+
+
+

Accessing Azure ExecutionContext

+
+

Some time there is a need to access the target execution context provided by Azure runtime in the form of com.microsoft.azure.functions.ExecutionContext. +For example one of such needs is logging, so it can appear in the Azure console.

+
+
+

For that purpose Spring Cloud Function will register ExecutionContext as bean in the Application context, so it could be injected into your function. +For example

+
+
+
+
@Bean
+public Function<Foo, Bar> uppercase(ExecutionContext targetContext) {
+	return foo -> {
+		targetContext.getLogger().info("Invoking 'uppercase' on " + foo.getValue());
+		return new Bar(foo.getValue().toUpperCase());
+	};
+}
+
+
+
+

Normally type-based injection should suffice, however if need to you can also utilise the bean name under which it is registered which is targetExecutionContext.

+
+
+
+

Notes on JAR Layout

+
+

You don’t need the Spring Cloud Function Web at runtime in Azure, so you can exclude this before you create the JAR you deploy to Azure, but it won’t be used if you include it so it doesn’t hurt to leave it in. A function application on Azure is an archive generated by the Maven plugin. The function lives in the JAR file generated by this project. The sample creates it as an executable jar, using the thin layout, so that Azure can find the handler classes. If you prefer you can just use a regular flat JAR file. The dependencies should not be included.

+
+
+
+

Build

+
+
+
+
./mvnw -U clean package
+
+
+
+
+
+

Running the sample

+
+
+

You can run the sample locally, just like the other Spring Cloud Function samples:

+
+
+
+
+

and curl -H "Content-Type: text/plain" localhost:8080/function -d '{"value": "hello foobar"}'.

+
+
+

You will need the az CLI app (see https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-first-java-maven for more detail). To deploy the function on Azure runtime:

+
+
+
+
$ az login
+$ mvn azure-functions:deploy
+
+
+
+

On another terminal try this: curl https://<azure-function-url-from-the-log>/api/uppercase -d '{"value": "hello foobar!"}'. Please ensure that you use the right URL for the function above. Alternatively you can test the function in the Azure Dashboard UI (click on the function name, go to the right hand side and click "Test" and to the bottom right, "Run").

+
+
+

The input type for the function in the Azure sample is a Foo with a single property called "value". So you need this to test it with something like below:

+
+
+
+
{
+  "value": "foobar"
+}
+
+
+
+ + + + + +
+ + +The Azure sample app is written in the "non-functional" style (using @Bean). The functional style (with just Function or ApplicationContextInitializer) is much faster on startup in Azure than the traditional @Bean style, so if you don’t need @Beans (or @EnableAutoConfiguration) it’s a good choice. Warm starts are not affected. +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/azure-readme.html b/azure-readme.html new file mode 100644 index 000000000..74a5bc51b --- /dev/null +++ b/azure-readme.html @@ -0,0 +1,241 @@ + + + + + + + +Accessing Azure ExecutionContext + + + + + + + + + +
+
+
+
+

This project provides an adapter layer for a Spring Cloud Function application onto Azure. +You can write an app with a single @Bean of type Function and it will be deployable in Azure if you get the JAR file laid out right.

+
+
+

This project provides an adapter layer for a Spring Cloud Function application onto Azure. +You can write an app with a single @Bean of type Function and it will be deployable in Azure if you get the JAR file laid out right.

+
+
+

There is an AzureSpringBootRequestHandler which you must extend, and provide the input and output types as annotated method parameters (enabling Azure to inspect the class and create JSON bindings). The base class has two useful methods (handleRequest and handleOutput) to which you can delegate the actual function call, so mostly the function will only ever have one line.

+
+
+

Example:

+
+
+
+
public class FooHandler extends AzureSpringBootRequestHandler<Foo, Bar> {
+	@FunctionName("uppercase")
+	public Bar execute(
+			@HttpTrigger(name = "req", methods = { HttpMethod.GET,
+					HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS)
+                    Foo foo,
+			ExecutionContext context) {
+		return handleRequest(foo, context);
+	}
+}
+
+
+
+

This Azure handler will delegate to a Function<Foo,Bar> bean (or a Function<Publisher<Foo>,Publisher<Bar>>). Some Azure triggers (e.g. @CosmosDBTrigger) result in a input type of List and in that case you can bind to List in the Azure handler, or String (the raw JSON). The List input delegates to a Function with input type Map<String,Object>, or Publisher or List of the same type. The output of the Function can be a List (one-for-one) or a single value (aggregation), and the output binding in the Azure declaration should match.

+
+
+

If your app has more than one @Bean of type Function etc. then you can choose the one to use by configuring function.name. Or if you make the @FunctionName in the Azure handler method match the function name it should work that way (also for function apps with multiple functions). The functions are extracted from the Spring Cloud FunctionCatalog so the default function names are the same as the bean names.

+
+
+
+
+

Accessing Azure ExecutionContext

+
+

Some time there is a need to access the target execution context provided by Azure runtime in the form of com.microsoft.azure.functions.ExecutionContext. +For example one of such needs is logging, so it can appear in the Azure console.

+
+
+

For that purpose Spring Cloud Function will register ExecutionContext as bean in the Application context, so it could be injected into your function. +For example

+
+
+
+
@Bean
+public Function<Foo, Bar> uppercase(ExecutionContext targetContext) {
+	return foo -> {
+		targetContext.getLogger().info("Invoking 'uppercase' on " + foo.getValue());
+		return new Bar(foo.getValue().toUpperCase());
+	};
+}
+
+
+
+

Normally type-based injection should suffice, however if need to you can also utilise the bean name under which it is registered which is targetExecutionContext.

+
+
+
+

Notes on JAR Layout

+
+

You don’t need the Spring Cloud Function Web at runtime in Azure, so you can exclude this before you create the JAR you deploy to Azure, but it won’t be used if you include it so it doesn’t hurt to leave it in. A function application on Azure is an archive generated by the Maven plugin. The function lives in the JAR file generated by this project. The sample creates it as an executable jar, using the thin layout, so that Azure can find the handler classes. If you prefer you can just use a regular flat JAR file. The dependencies should not be included.

+
+
+
+

Build

+
+
+
+
./mvnw -U clean package
+
+
+
+
+
+

Running the sample

+
+
+

You can run the sample locally, just like the other Spring Cloud Function samples:

+
+
+
+
+

and curl -H "Content-Type: text/plain" localhost:8080/function -d '{"value": "hello foobar"}'.

+
+
+

You will need the az CLI app (see https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-first-java-maven for more detail). To deploy the function on Azure runtime:

+
+
+
+
$ az login
+$ mvn azure-functions:deploy
+
+
+
+

On another terminal try this: curl https://<azure-function-url-from-the-log>/api/uppercase -d '{"value": "hello foobar!"}'. Please ensure that you use the right URL for the function above. Alternatively you can test the function in the Azure Dashboard UI (click on the function name, go to the right hand side and click "Test" and to the bottom right, "Run").

+
+
+

The input type for the function in the Azure sample is a Foo with a single property called "value". So you need this to test it with something like below:

+
+
+
+
{
+  "value": "foobar"
+}
+
+
+
+ + + + + +
+ + +The Azure sample app is written in the "non-functional" style (using @Bean). The functional style (with just Function or ApplicationContextInitializer) is much faster on startup in Azure than the traditional @Bean style, so if you don’t need @Beans (or @EnableAutoConfiguration) it’s a good choice. Warm starts are not affected. +
+
+
+
+
+

Sample Function

+
+
+

Go to the function-sample-azure to learn about how the sample works, and how to run and test it.

+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/azure.html b/azure.html new file mode 100644 index 000000000..99e0bc83f --- /dev/null +++ b/azure.html @@ -0,0 +1,234 @@ + + + + + + + +Accessing Azure ExecutionContext + + + + + + + + + +
+
+
+
+

2.1.0.BUILD-SNAPSHOT

+
+
+

The Azure adapter bootstraps a Spring Cloud Function context and channels function calls from the Azure framework into the user functions, using Spring Boot configuration where necessary. Azure Functions has quite a unique, but invasive programming model, involving annotations in user code that are specific to the platform. The easiest way to use it with Spring Cloud is to extend a base class and write a method in it with the @FunctionName annotation which delegates to a base class method.

+
+
+

This project provides an adapter layer for a Spring Cloud Function application onto Azure. +You can write an app with a single @Bean of type Function and it will be deployable in Azure if you get the JAR file laid out right.

+
+
+

There is an AzureSpringBootRequestHandler which you must extend, and provide the input and output types as annotated method parameters (enabling Azure to inspect the class and create JSON bindings). The base class has two useful methods (handleRequest and handleOutput) to which you can delegate the actual function call, so mostly the function will only ever have one line.

+
+
+

Example:

+
+
+
+
public class FooHandler extends AzureSpringBootRequestHandler<Foo, Bar> {
+	@FunctionName("uppercase")
+	public Bar execute(
+			@HttpTrigger(name = "req", methods = { HttpMethod.GET,
+					HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS)
+                    Foo foo,
+			ExecutionContext context) {
+		return handleRequest(foo, context);
+	}
+}
+
+
+
+

This Azure handler will delegate to a Function<Foo,Bar> bean (or a Function<Publisher<Foo>,Publisher<Bar>>). Some Azure triggers (e.g. @CosmosDBTrigger) result in a input type of List and in that case you can bind to List in the Azure handler, or String (the raw JSON). The List input delegates to a Function with input type Map<String,Object>, or Publisher or List of the same type. The output of the Function can be a List (one-for-one) or a single value (aggregation), and the output binding in the Azure declaration should match.

+
+
+

If your app has more than one @Bean of type Function etc. then you can choose the one to use by configuring function.name. Or if you make the @FunctionName in the Azure handler method match the function name it should work that way (also for function apps with multiple functions). The functions are extracted from the Spring Cloud FunctionCatalog so the default function names are the same as the bean names.

+
+
+
+
+

Accessing Azure ExecutionContext

+
+

Some time there is a need to access the target execution context provided by Azure runtime in the form of com.microsoft.azure.functions.ExecutionContext. +For example one of such needs is logging, so it can appear in the Azure console.

+
+
+

For that purpose Spring Cloud Function will register ExecutionContext as bean in the Application context, so it could be injected into your function. +For example

+
+
+
+
@Bean
+public Function<Foo, Bar> uppercase(ExecutionContext targetContext) {
+	return foo -> {
+		targetContext.getLogger().info("Invoking 'uppercase' on " + foo.getValue());
+		return new Bar(foo.getValue().toUpperCase());
+	};
+}
+
+
+
+

Normally type-based injection should suffice, however if need to you can also utilise the bean name under which it is registered which is targetExecutionContext.

+
+
+
+

Notes on JAR Layout

+
+

You don’t need the Spring Cloud Function Web at runtime in Azure, so you can exclude this before you create the JAR you deploy to Azure, but it won’t be used if you include it so it doesn’t hurt to leave it in. A function application on Azure is an archive generated by the Maven plugin. The function lives in the JAR file generated by this project. The sample creates it as an executable jar, using the thin layout, so that Azure can find the handler classes. If you prefer you can just use a regular flat JAR file. The dependencies should not be included.

+
+
+
+

Build

+
+
+
+
./mvnw -U clean package
+
+
+
+
+
+

Running the sample

+
+
+

You can run the sample locally, just like the other Spring Cloud Function samples:

+
+
+
+
+

and curl -H "Content-Type: text/plain" localhost:8080/function -d '{"value": "hello foobar"}'.

+
+
+

You will need the az CLI app (see https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-first-java-maven for more detail). To deploy the function on Azure runtime:

+
+
+
+
$ az login
+$ mvn azure-functions:deploy
+
+
+
+

On another terminal try this: curl https://<azure-function-url-from-the-log>/api/uppercase -d '{"value": "hello foobar!"}'. Please ensure that you use the right URL for the function above. Alternatively you can test the function in the Azure Dashboard UI (click on the function name, go to the right hand side and click "Test" and to the bottom right, "Run").

+
+
+

The input type for the function in the Azure sample is a Foo with a single property called "value". So you need this to test it with something like below:

+
+
+
+
{
+  "value": "foobar"
+}
+
+
+
+ + + + + +
+ + +The Azure sample app is written in the "non-functional" style (using @Bean). The functional style (with just Function or ApplicationContextInitializer) is much faster on startup in Azure than the traditional @Bean style, so if you don’t need @Beans (or @EnableAutoConfiguration) it’s a good choice. Warm starts are not affected. +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/functional.html b/functional.html new file mode 100644 index 000000000..092d39a6e --- /dev/null +++ b/functional.html @@ -0,0 +1,305 @@ + + + + + + + +Comparing Functional with Traditional Bean Definitions + + + + + + + + + +
+
+
+
+

Spring Cloud Function supports a "functional" style of bean declarations for small apps where you need fast startup. The functional style of bean declaration was a feature of Spring Framework 5.0 with significant enhancements in 5.1.

+
+
+
+
+

Comparing Functional with Traditional Bean Definitions

+
+
+

Here’s a vanilla Spring Cloud Function application from with the +familiar @Configuration and @Bean declaration style:

+
+
+
+
@SpringBootApplication
+public class DemoApplication {
+
+  @Bean
+  public Function<String, String> uppercase() {
+    return value -> value.toUpperCase();
+  }
+
+  public static void main(String[] args) {
+    SpringApplication.run(DemoApplication.class, args);
+  }
+
+}
+
+
+
+

You can run the above in a serverless platform, like AWS Lambda or Azure Functions, or you can run it in its own HTTP server just by including spring-cloud-function-starter-web on the classpath. Running the main method would expose an endpoint that you can use to ping that uppercase function:

+
+
+
+
$ curl localhost:8080 -d foo
+FOO
+
+
+
+

The web adapter in spring-cloud-function-starter-web uses Spring MVC, so you needed a Servlet container. You can also use Webflux where the default server is netty (even though you can still use Servlet containers if you want to) - just include the spring-cloud-starter-function-webflux dependency instead. The functionality is the same, and the user application code can be used in both.

+
+
+

Now for the functional beans: the user application code can be recast into "functional" +form, like this:

+
+
+
+
@SpringBootConfiguration
+public class DemoApplication implements ApplicationContextInitializer<GenericApplicationContext> {
+
+  public static void main(String[] args) {
+    FunctionalSpringApplication.run(DemoApplication.class, args);
+  }
+
+  public Function<String, String> uppercase() {
+    return value -> value.toUpperCase();
+  }
+
+  @Override
+  public void initialize(GenericApplicationContext context) {
+    context.registerBean("demo", FunctionRegistration.class,
+        () -> new FunctionRegistration<>(uppercase())
+            .type(FunctionType.from(String.class).to(String.class)));
+  }
+
+}
+
+
+
+

The main differences are:

+
+
+
    +
  • +

    The main class is an ApplicationContextInitializer.

    +
  • +
  • +

    The @Bean methods have been converted to calls to context.registerBean()

    +
  • +
  • +

    The @SpringBootApplication has been replaced with +@SpringBootConfiguration to signify that we are not enabling Spring +Boot autoconfiguration, and yet still marking the class as an "entry +point".

    +
  • +
  • +

    The SpringApplication from Spring Boot has been replaced with a +FunctionalSpringApplication from Spring Cloud Function (it’s a +subclass).

    +
  • +
+
+
+

The business logic beans that you register in a Spring Cloud Function app are of type FunctionRegistration. This is a wrapper that contains both the function and information about the input and output types. In the @Bean form of the application that information can be derived reflectively, but in a functional bean registration some of it is lost unless we use a FunctionRegistration.

+
+
+

An alternative to using an ApplicationContextInitializer and FunctionRegistration is to make the application itself implement Function (or Consumer or Supplier). Example (equivalent to the above):

+
+
+
+
@SpringBootConfiguration
+public class DemoApplication implements Function<String, String> {
+
+  public static void main(String[] args) {
+    FunctionalSpringApplication.run(DemoApplication.class, args);
+  }
+
+  @Override
+  public String uppercase(String value) {
+    return value.toUpperCase();
+  }
+
+}
+
+
+
+

It would also work if you add a separate, standalone class of type Function and register it with the SpringApplication using an alternative form of the run() method. The main thing is that the generic type information is available at runtime through the class declaration.

+
+
+

The app runs in its own HTTP server if you add spring-cloud-starter-function-webflux (it won’t work with the MVC starter at the moment because the functional form of the embedded Servlet container hasn’t been implemented). The app also runs just fine in AWS Lambda or Azure Functions, and the improvements in startup time are dramatic.

+
+
+ + + + + +
+ + +The "lite" web server has some limitations for the range of Function signatures - in particular it doesn’t (yet) support Message input and output, but POJOs and any kind of Publisher should be fine. +
+
+
+
+
+

Testing Functional Applications

+
+
+

Spring Cloud Function also has some utilities for integration testing that will be very familiar to Spring Boot users. For example, here is an integration test for the HTTP server wrapping the app above:

+
+
+
+
@RunWith(SpringRunner.class)
+@FunctionalSpringBootTest
+@AutoConfigureWebTestClient
+public class FunctionalTests {
+
+	@Autowired
+	private WebTestClient client;
+
+	@Test
+	public void words() throws Exception {
+		client.post().uri("/").body(Mono.just("foo"), String.class).exchange()
+				.expectStatus().isOk().expectBody(String.class).isEqualTo("FOO");
+	}
+
+}
+
+
+
+

This test is almost identical to the one you would write for the @Bean version of the same app - the only difference is the @FunctionalSpringBootTest annotation, instead of the regular @SpringBootTest. All the other pieces, like the @Autowired WebTestClient, are standard Spring Boot features.

+
+
+

Or you could write a test for a non-HTTP app using just the FunctionCatalog. For example:

+
+
+
+
@RunWith(SpringRunner.class)
+@FunctionalSpringBootTest
+public class FunctionalTests {
+
+	@Autowired
+	private FunctionCatalog catalog;
+
+	@Test
+	public void words() throws Exception {
+		Function<Flux<String>, Flux<String>> function = catalog.lookup(Function.class,
+				"function");
+		assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO");
+	}
+
+}
+
+
+
+

(The FunctionCatalog always returns functions from Flux to Flux, even if the user declares them with a simpler signature.)

+
+
+
+
+

Limitations of Functional Bean Declaration

+
+
+

Most Spring Cloud Function apps have a relatively small scope compared to the whole of Spring Boot, so we are able to adapt it to these functional bean definitions easily. If you step outside that limited scope, you can extend your Spring Cloud Function app by switching back to @Bean style configuration, or by using a hybrid approach. If you want to take advantage of Spring Boot autoconfiguration for integrations with external datastores, for example, you will need to use @EnableAutoConfiguration. Your functions can still be defined using the functional declarations if you want (i.e. the "hybrid" style), but in that case you will need to explicitly switch off the "full functional mode" using spring.functional.enabled=false so that Spring Boot can take back control.

+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/getting-started.html b/getting-started.html new file mode 100644 index 000000000..18fd15ae3 --- /dev/null +++ b/getting-started.html @@ -0,0 +1,183 @@ + + + + + + + +Building and Running a Function + + + + + + + + + +
+
+
+
+

Build from the command line (and "install" the samples):

+
+
+
+
$ ./mvnw clean install
+
+
+
+

(If you like to YOLO add -DskipTests.)

+
+
+

Run one of the samples, e.g.

+
+
+
+
$ java -jar spring-cloud-function-samples/function-sample/target/*.jar
+
+
+
+

This runs the app and exposes its functions over HTTP, so you can +convert a string to uppercase, like this:

+
+
+
+
$ curl -H "Content-Type: text/plain" localhost:8080/uppercase -d Hello
+HELLO
+
+
+
+

You can convert multiple strings (a Flux<String>) by separating them +with new lines

+
+
+
+
$ curl -H "Content-Type: text/plain" localhost:8080/uppercase -d 'Hello
+> World'
+HELLOWORLD
+
+
+
+

(You can use QJ in a terminal to insert a new line in a literal +string like that.)

+
+
+
+
+

Building and Running a Function

+
+
+

The sample @SpringBootApplication above has a function that can be +decorated at runtime by Spring Cloud Function to be an HTTP endpoint, +or a Stream processor, for instance with RabbitMQ, Apache Kafka or +JMS.

+
+
+

The @Beans can be Function, Consumer or Supplier (all from +java.util), and their parametric types can be String or POJO.

+
+
+

Functions can also be of Flux<String> or Flux<Pojo> and Spring +Cloud Function takes care of converting the data to and from the +desired types, as long as it comes in as plain text or (in the case of +the POJO) JSON. There is also support for Message<Pojo> where the +message headers are copied from the incoming event, depending on the +adapter. The web adapter also supports conversion from form-encoded +data to a Map, and if you are using the function with Spring Cloud +Stream then all the conversion and coercion features for message +payloads will be applicable as well.

+
+
+

Functions can be grouped together in a single application, or deployed +one-per-jar. It’s up to the developer to choose. An app with multiple +functions can be deployed multiple times in different "personalities", +exposing different functions over different physical transports.

+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/index.html b/index.html index eaaf07939..ce4e29861 100644 --- a/index.html +++ b/index.html @@ -1,93 +1,171 @@ ---- -# The name of your project -title: Spring Cloud Function - -badges: - - # Customize your project's badges. Delete any entries that do not apply. - custom: - - name: Source (GitHub) - url: https://github.com/spring-cloud/spring-cloud-function - icon: github - - - name: StackOverflow - url: http://stackoverflow.com/questions/tagged/spring-cloud - icon: stackoverflow - ---- - - - - -{% capture parent_link %} -[Spring Cloud]({{ site.projects_site_url }}/spring-cloud) -{% endcapture %} - - -{% capture billboard_description %} - -Spring Cloud Function is a project with the following high-level goals: - -* Promote the implementation of business logic via functions. -* Decouple the development lifecycle of business logic from any specific runtime target so that the same code can run as a web endpoint, a stream processor, or a task. -* Support a uniform programming model across serverless providers, as well as the ability to run standalone (locally or in a PaaS). -* Enable Spring Boot features (auto-configuration, dependency injection, metrics) on serverless providers. - -It abstracts away all of the transport details and infrastructure, allowing the developer to keep all the familiar tools and processes, and focus firmly on business logic. - -{% endcapture %} - -{% capture main_content %} - -## Features - -Spring Cloud Function features: - -1. Wrappers for `@Beans` of type `Function`, `Consumer` and -`Supplier`, exposing them to the outside world as either HTTP -endpoints and/or message stream listeners/publishers with RabbitMQ, Kafka etc. - -2. Compiling strings which are Java function bodies into bytecode, and -then turning them into `@Beans` that can be wrapped as above. - -3. Deploying a JAR file containing such an application context with an -isolated classloader, so that you can pack them together in a single -JVM. - -4. Adapters for [AWS Lambda](https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-aws), [Microsoft Azure](https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-azure), [Apache OpenWhisk](https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk) and possibly other "serverless" service providers. - - -Here's a complete, executable, testable Spring Boot application (implementing a simple string manipulation): - -```java -@SpringBootApplication -public class Application { - - @Bean - public Function, Flux> uppercase() { - return flux -> flux.map(value -> value.toUpperCase()); - } - - public static void main(String[] args) { - SpringApplication.run(Application.class, args); - } + + + + + + + + +Spring Cloud Function Reference Documentation + + + + + + + + + +
+
+
+
+

2.1.0.BUILD-SNAPSHOT

+
+
+

The reference documentation consists of the following sections:

+
+
+ + + + + + + + + + + + + + + + + +
+Reference Guide + +

Spring Cloud Function Reference

+
+AWS Adapter + +

AWS Adapter Reference

+
+Azure Adapter + +

Azure Adapter Reference

+
+Apache OpenWhisk Adapter + +

Apache OpenWhisk Adapter Reference

+
+
+
+

Relevant Links:

+
+
+ + + + + + + + + +
+Reactor + +

Project Reactor

+
+riff + +

Project riff

+
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/intro.html b/intro.html new file mode 100644 index 000000000..656464f72 --- /dev/null +++ b/intro.html @@ -0,0 +1,177 @@ + + + + + + + +Untitled + + + + + + + + + +
+
+
+
+

Spring Cloud Function is a project with the following high-level goals:

+
+
+
    +
  • +

    Promote the implementation of business logic via functions.

    +
  • +
  • +

    Decouple the development lifecycle of business logic from any specific runtime target so that the same code can run as a web endpoint, a stream processor, or a task.

    +
  • +
  • +

    Support a uniform programming model across serverless providers, as well as the ability to run standalone (locally or in a PaaS).

    +
  • +
  • +

    Enable Spring Boot features (auto-configuration, dependency injection, metrics) on serverless providers.

    +
  • +
+
+
+

It abstracts away all of the transport details and +infrastructure, allowing the developer to keep all the familiar tools +and processes, and focus firmly on business logic.

+
+
+

Here’s a complete, executable, testable Spring Boot application +(implementing a simple string manipulation):

+
+
+
+
@SpringBootApplication
+public class Application {
+
+  @Bean
+  public Function<Flux<String>, Flux<String>> uppercase() {
+    return flux -> flux.map(value -> value.toUpperCase());
+  }
+
+  public static void main(String[] args) {
+    SpringApplication.run(Application.class, args);
+  }
+}
+
+
+
+

It’s just a Spring Boot application, so it can be built, run and +tested, locally and in a CI build, the same way as any other Spring +Boot application. The Function is from java.util and Flux is a +Reactive Streams Publisher from +Project Reactor. The function can be +accessed over HTTP or messaging.

+
+
+

Spring Cloud Function has 4 main features:

+
+
+
    +
  1. +

    Wrappers for @Beans of type Function, Consumer and +Supplier, exposing them to the outside world as either HTTP +endpoints and/or message stream listeners/publishers with RabbitMQ, Kafka etc.

    +
  2. +
  3. +

    Compiling strings which are Java function bodies into bytecode, and +then turning them into @Beans that can be wrapped as above.

    +
  4. +
  5. +

    Deploying a JAR file containing such an application context with an +isolated classloader, so that you can pack them together in a single +JVM.

    +
  6. +
  7. +

    Adapters for AWS Lambda, Azure, Apache OpenWhisk and possibly other "serverless" service providers.

    +
  8. +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/openwhisk-quick-start.html b/openwhisk-quick-start.html new file mode 100644 index 000000000..6726417dd --- /dev/null +++ b/openwhisk-quick-start.html @@ -0,0 +1,216 @@ + + + + + + + +Untitled + + + + + + + + + +
+
+
+
+

Implement a POF (be sure to use the functions package):

+
+
+
+
package functions;
+
+import java.util.function.Function;
+
+public class Uppercase implements Function<String, String> {
+
+	public String apply(String input) {
+		return input.toUpperCase();
+	}
+}
+
+
+
+

Install it into your local Maven repository:

+
+
+
+
./mvnw clean install
+
+
+
+

Create a function.properties file that provides its Maven coordinates. For example:

+
+
+
+
dependencies.function: com.example:pof:0.0.1-SNAPSHOT
+
+
+
+

Copy the openwhisk runner JAR to the working directory (same directory as the properties file):

+
+
+
+
cp spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/target/spring-cloud-function-adapter-openwhisk-2.0.0.BUILD-SNAPSHOT.jar runner.jar
+
+
+
+

Generate a m2 repo from the --thin.dryrun of the runner JAR with the above properties file:

+
+
+
+
java -jar -Dthin.root=m2 runner.jar --thin.name=function --thin.dryrun
+
+
+
+

Use the following Dockerfile:

+
+
+
+
FROM openjdk:8-jdk-alpine
+VOLUME /tmp
+COPY m2 /m2
+ADD runner.jar .
+ADD function.properties .
+ENV JAVA_OPTS=""
+ENTRYPOINT [ "java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "runner.jar", "--thin.root=/m2", "--thin.name=function", "--function.name=uppercase"]
+EXPOSE 8080
+
+
+
+
+
+ + + + + +
+ + +you could use a Spring Cloud Function app, instead of just a jar with a POF in it, in which case you would have to change the way the app runs in the container so that it picks up the main class as a source file. For example, you could change the ENTRYPOINT above and add --spring.main.sources=com.example.SampleApplication. +
+
+
+
+
+

Build the Docker image:

+
+
+
+
docker build -t [username/appname] .
+
+
+
+

Push the Docker image:

+
+
+
+
docker push [username/appname]
+
+
+
+

Use the OpenWhisk CLI (e.g. after vagrant ssh) to create the action:

+
+
+
+
wsk action create example --docker [username/appname]
+
+
+
+

Invoke the action:

+
+
+
+
wsk action invoke example --result --param payload foo
+{
+    "result": "FOO"
+}
+
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/openwhisk-readme.html b/openwhisk-readme.html new file mode 100644 index 000000000..abe3c7a94 --- /dev/null +++ b/openwhisk-readme.html @@ -0,0 +1,245 @@ + + + + + + + +Quick Start + + + + + + + + + +
+
+

Quick Start

+
+
+

Implement a POF (be sure to use the functions package):

+
+
+
+
package functions;
+
+import java.util.function.Function;
+
+public class Uppercase implements Function<String, String> {
+
+	public String apply(String input) {
+		return input.toUpperCase();
+	}
+}
+
+
+
+

Install it into your local Maven repository:

+
+
+
+
./mvnw clean install
+
+
+
+

Create a function.properties file that provides its Maven coordinates. For example:

+
+
+
+
dependencies.function: com.example:pof:0.0.1-SNAPSHOT
+
+
+
+

Copy the openwhisk runner JAR to the working directory (same directory as the properties file):

+
+
+
+
cp spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/target/spring-cloud-function-adapter-openwhisk-2.0.0.BUILD-SNAPSHOT.jar runner.jar
+
+
+
+

Generate a m2 repo from the --thin.dryrun of the runner JAR with the above properties file:

+
+
+
+
java -jar -Dthin.root=m2 runner.jar --thin.name=function --thin.dryrun
+
+
+
+

Use the following Dockerfile:

+
+
+
+
FROM openjdk:8-jdk-alpine
+VOLUME /tmp
+COPY m2 /m2
+ADD runner.jar .
+ADD function.properties .
+ENV JAVA_OPTS=""
+ENTRYPOINT [ "java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "runner.jar", "--thin.root=/m2", "--thin.name=function", "--function.name=uppercase"]
+EXPOSE 8080
+
+
+
+
+
+ + + + + +
+ + +you could use a Spring Cloud Function app, instead of just a jar with a POF in it, in which case you would have to change the way the app runs in the container so that it picks up the main class as a source file. For example, you could change the ENTRYPOINT above and add --spring.main.sources=com.example.SampleApplication. +
+
+
+
+
+

Build the Docker image:

+
+
+
+
docker build -t [username/appname] .
+
+
+
+

Push the Docker image:

+
+
+
+
docker push [username/appname]
+
+
+
+

Use the OpenWhisk CLI (e.g. after vagrant ssh) to create the action:

+
+
+
+
wsk action create example --docker [username/appname]
+
+
+
+

Invoke the action:

+
+
+
+
wsk action invoke example --result --param payload foo
+{
+    "result": "FOO"
+}
+
+
+
+
+
+

Examples

+
+
+

The following examples are built based on the details and explanations above, on how to deploy Spring Cloud Functions on to OpenWhisk

+
+
+ +
+
+

The base docker images used for above examples is available here.

+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/openwhisk.html b/openwhisk.html new file mode 100644 index 000000000..998bdde6f --- /dev/null +++ b/openwhisk.html @@ -0,0 +1,233 @@ + + + + + + + +Quick Start + + + + + + + + + +
+
+
+
+

2.1.0.BUILD-SNAPSHOT

+
+
+

The OpenWhisk adapter is in the form of an executable jar that can be used in a a docker image to be deployed to Openwhisk. The platform works in request-response mode, listening on port 8080 on a specific endpoint, so the adapter is a simple Spring MVC application.

+
+
+
+
+

Quick Start

+
+
+

Implement a POF (be sure to use the functions package):

+
+
+
+
package functions;
+
+import java.util.function.Function;
+
+public class Uppercase implements Function<String, String> {
+
+	public String apply(String input) {
+		return input.toUpperCase();
+	}
+}
+
+
+
+

Install it into your local Maven repository:

+
+
+
+
./mvnw clean install
+
+
+
+

Create a function.properties file that provides its Maven coordinates. For example:

+
+
+
+
dependencies.function: com.example:pof:0.0.1-SNAPSHOT
+
+
+
+

Copy the openwhisk runner JAR to the working directory (same directory as the properties file):

+
+
+
+
cp spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/target/spring-cloud-function-adapter-openwhisk-2.0.0.BUILD-SNAPSHOT.jar runner.jar
+
+
+
+

Generate a m2 repo from the --thin.dryrun of the runner JAR with the above properties file:

+
+
+
+
java -jar -Dthin.root=m2 runner.jar --thin.name=function --thin.dryrun
+
+
+
+

Use the following Dockerfile:

+
+
+
+
FROM openjdk:8-jdk-alpine
+VOLUME /tmp
+COPY m2 /m2
+ADD runner.jar .
+ADD function.properties .
+ENV JAVA_OPTS=""
+ENTRYPOINT [ "java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "runner.jar", "--thin.root=/m2", "--thin.name=function", "--function.name=uppercase"]
+EXPOSE 8080
+
+
+
+
+
+ + + + + +
+ + +you could use a Spring Cloud Function app, instead of just a jar with a POF in it, in which case you would have to change the way the app runs in the container so that it picks up the main class as a source file. For example, you could change the ENTRYPOINT above and add --spring.main.sources=com.example.SampleApplication. +
+
+
+
+
+

Build the Docker image:

+
+
+
+
docker build -t [username/appname] .
+
+
+
+

Push the Docker image:

+
+
+
+
docker push [username/appname]
+
+
+
+

Use the OpenWhisk CLI (e.g. after vagrant ssh) to create the action:

+
+
+
+
wsk action create example --docker [username/appname]
+
+
+
+

Invoke the action:

+
+
+
+
wsk action invoke example --result --param payload foo
+{
+    "result": "FOO"
+}
+
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/spring-cloud-function.html b/spring-cloud-function.html index 49a5c6e0d..04b696149 100644 --- a/spring-cloud-function.html +++ b/spring-cloud-function.html @@ -5,8 +5,9 @@ -spring-cloud-function - +Spring Cloud Function + +