Migrate Structure

This commit is contained in:
Marcin Grzejszczak
2023-09-08 15:57:04 +02:00
parent 19d1f320de
commit 2108c95ff5
17 changed files with 273 additions and 0 deletions

View File

@@ -1,19 +0,0 @@
:branch: master
image::https://travis-ci.org/spring-cloud/spring-cloud-function.svg?branch={branch}[Build Status, link=https://travis-ci.org/spring-cloud/spring-cloud-function]
== Introduction
include::_intro.adoc[]
== Getting Started
include::getting-started.adoc[]
== Building
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/building.adoc[]
== Contributing
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/contributing.adoc[]

View File

@@ -1,47 +0,0 @@
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):
[source,java]
----
@SpringBootApplication
public class Application {
@Bean
public Function<String, String> uppercase() {
return value -> value.toUpperCase();
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
----
It's just a Spring Boot application, so it can be built, run and
tested, locally and in a CI build, the same way as any other Spring
Boot application. The `Function` is from `java.util` and `Flux` is a
https://www.reactive-streams.org/[Reactive Streams] `Publisher` from
https://projectreactor.io/[Project Reactor]. The function can be
accessed over HTTP or messaging.
Spring Cloud Function has the following features:
* _Choice of programming styles - reactive, imperative or hybrid._
* _Function composition and adaptation (e.g., composing imperative functions with reactive)._
* _Support for reactive function with multiple inputs and outputs allowing merging, joining and other complex streaming operation to be handled by functions._
* _Transparent type conversion of inputs and outputs._
* _Packaging functions for deployments, specific to the target platform (e.g., Project Riff, AWS Lambda and more)_
* _Adapters to expose function to the outside world as HTTP endpoints etc._
* _Deploying a JAR file containing such an application context with an isolated classloader, so that you can pack them together in a single JVM._
* _Adapters for https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-aws[AWS Lambda], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-azure[Azure], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp[Google Cloud Functions], and possibly other "serverless" service providers._

View File

@@ -1,318 +0,0 @@
:branch: master
=== AWS Lambda
The https://aws.amazon.com/[AWS] adapter takes a Spring Cloud Function app and converts it to a form that can run in AWS Lambda.
The details of how to get stared with AWS Lambda is out of scope of this document, so the expectation is that user has some familiarity with
AWS and AWS Lambda and wants to learn what additional value spring provides.
==== Getting Started
One of the goals of Spring Cloud Function framework is to provide necessary infrastructure elements to enable a _simple function application_
to interact in a certain way in a particular environment.
A simple function application (in context or Spring) is an application that contains beans of type Supplier, Function or Consumer.
So, with AWS it means that a simple function bean should somehow be recognised and executed in AWS Lambda environment.
Lets look at the example:
[source, java]
----
@SpringBootApplication
public class FunctionConfiguration {
public static void main(String[] args) {
SpringApplication.run(FunctionConfiguration.class, args);
}
@Bean
public Function<String, String> uppercase() {
return value -> value.toUpperCase();
}
}
----
It shows a complete Spring Boot application with a function bean defined in it. Whats interesting is that on the surface this is just
another boot app, but in the context of AWS Adapter it is also a perfectly valid AWS Lambda application. No other code or configuration
is required. All you need to do is package it and deploy it, so lets look how we can do that.
To make things simpler weve provided a sample project ready to be built and deployed and you can access it
https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-aws[here].
You simply execute `./mvnw clean package` to generate JAR file. All the necessary maven plugins have already been setup to generate
appropriate AWS deployable JAR file. (You can read more details about JAR layout in <<Notes on JAR Layout>>).
Then you have to upload the JAR file (via AWS dashboard or AWS CLI) to AWS.
When ask about _handler_ you specify `org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest` which is a generic request handler.
image::{github-raw}/docs/src/main/asciidoc/images/AWS-deploy.png[width=800,scaledwidth="75%",align="center"]
That is all. Save and execute the function with some sample data which for this function is expected to be a
String which function will uppercase and return back.
While `org.springframework.cloud.function.adapter.aws.FunctionInvoker` is a general purpose AWS's `RequestHandler` implementation aimed at completely
isolating you from the specifics of AWS Lambda API, for some cases you may want to specify which specific AWS's `RequestHandler` you want
to use. The next section will explain you how you can accomplish just that.
==== AWS Request Handlers
The adapter has a couple of generic request handlers that you can use. The most generic is (and the one we used in the Getting Started section)
is `org.springframework.cloud.function.adapter.aws.FunctionInvoker` which is the implementation of AWS's `RequestStreamHandler`.
User doesn't need to do anything other then specify it as 'handler' on AWS dashboard when deploying function.
It will handle most of the case including Kinesis, streaming etc. .
If your app has more than one `@Bean` of type `Function` etc. then you can choose the one to use by configuring `spring.cloud.function.definition`
property or environment variable. The functions are extracted from the Spring Cloud `FunctionCatalog`. In the event you don't specify `spring.cloud.function.definition`
the framework will attempt to find a default following the search order where it searches first for `Function` then `Consumer` and finally `Supplier`).
==== AWS Function Routing
One of the core features of Spring Cloud Function is https://docs.spring.io/spring-cloud-function/docs/{project-version}/reference/html/spring-cloud-function.html#_function_routing_and_filtering[routing]
- an ability to have one special function to delegate to other functions based on the user provided routing instructions.
In AWS Lambda environment this feature provides one additional benefit, as it allows you to bind a single function (Routing Function)
as AWS Lambda and thus a single HTTP endpoint for API Gateway. So in the end you only manage one function and one endpoint, while benefiting
from many function that can be part of your application.
More details are available in the provided https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-aws-routing[sample],
yet few general things worth mentioning.
Routing capabilities will be enabled by default whenever there is more then one function in your application as `org.springframework.cloud.function.adapter.aws.FunctionInvoker`
can not determine which function to bind as AWS Lambda, so it defaults to `RoutingFunction`.
This means that all you need to do is provide routing instructions which you can do https://docs.spring.io/spring-cloud-function/docs/{project-version}/reference/html/spring-cloud-function.html#_function_routing_and_filtering[using several mechanisms]
(see https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-aws-routing[sample] for more details).
Also, note that since AWS does not allow dots `.` and/or hyphens`-` in the name of the environment variable, you can benefit from boot support and simply substitute
dots with underscores and hyphens with camel case. So for example `spring.cloud.function.definition` becomes `spring_cloud_function_definition`
and `spring.cloud.function.routing-expression` becomes `spring_cloud_function_routingExpression`.
===== AWS Function Routing with Custom Runtime
When using <<Custom Runtime>> Function Routing works the same way. All you need is to specify `functionRouter` as AWS Handler the same way you would use the name of the function as handler.
==== 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 [[thin-jar,thin jar]] 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 or system property `MAIN_CLASS` when you deploy the function to AWS.
If you are not using the functional bean definitions but relying on Spring Boot's auto-configuration,
and are not depending on `spring-boot-starter-parent`,
then additional transformers must be configured as part of the maven-shade-plugin execution.
[[shade-plugin-setup]]
[source, xml]
----
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.4</version>
</dependency>
</dependencies>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>aws</shadedClassifierName>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.handlers</resource>
</transformer>
<transformer implementation="org.springframework.boot.maven.PropertiesMergingResourceTransformer">
<resource>META-INF/spring.factories</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring/org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.schemas</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.components</resource>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
----
==== Build file setup
In order to run Spring Cloud Function applications on AWS Lambda, you can leverage Maven or Gradle
plugins offered by the cloud platform provider.
===== Maven
In order to use the adapter plugin for Maven, add the plugin dependency to your `pom.xml`
file:
[source,xml]
----
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-aws</artifactId>
</dependency>
</dependencies>
----
As pointed out in the <<Notes on JAR Layout>>, you will need a shaded jar in order to upload it
to AWS Lambda. You can use the https://maven.apache.org/plugins/maven-shade-plugin/[Maven Shade Plugin] for that.
The example of the <<shade-plugin-setup,setup>> can be found above.
You can use the Spring Boot Maven Plugin to generate the <<thin-jar>>.
[source,xml]
----
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-thin-layout</artifactId>
<version>${wrapper.version}</version>
</dependency>
</dependencies>
</plugin>
----
You can find the entire sample `pom.xml` file for deploying Spring Cloud Function
applications to AWS Lambda with Maven https://github.com/spring-cloud/spring-cloud-function/blob/{branch}/spring-cloud-function-samples/function-sample-aws/pom.xml[here].
===== Gradle
In order to use the adapter plugin for Gradle, add the dependency to your `build.gradle` file:
[source,groovy]
----
dependencies {
compile("org.springframework.cloud:spring-cloud-function-adapter-aws:${version}")
}
----
As pointed out in <<Notes on JAR Layout>>, you will need a shaded jar in order to upload it
to AWS Lambda. You can use the https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow/[Gradle Shadow Plugin] for that:
[source,groovy]
----
buildscript {
dependencies {
classpath "com.github.jengelman.gradle.plugins:shadow:${shadowPluginVersion}"
}
}
apply plugin: 'com.github.johnrengelman.shadow'
assemble.dependsOn = [shadowJar]
import com.github.jengelman.gradle.plugins.shadow.transformers.*
shadowJar {
classifier = 'aws'
dependencies {
exclude(
dependency("org.springframework.cloud:spring-cloud-function-web:${springCloudFunctionVersion}"))
}
// Required for Spring
mergeServiceFiles()
append 'META-INF/spring.handlers'
append 'META-INF/spring.schemas'
append 'META-INF/spring.tooling'
append 'META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports'
append 'META-INF/spring/org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports'
transform(PropertiesFileTransformer) {
paths = ['META-INF/spring.factories']
mergeStrategy = "append"
}
}
----
You can use the Spring Boot Gradle Plugin and Spring Boot Thin Gradle Plugin to generate
the <<thin-jar>>.
[source,groovy]
----
buildscript {
dependencies {
classpath("org.springframework.boot.experimental:spring-boot-thin-gradle-plugin:${wrapperVersion}")
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'org.springframework.boot'
apply plugin: 'org.springframework.boot.experimental.thin-launcher'
assemble.dependsOn = [thinJar]
----
You can find the entire sample `build.gradle` file for deploying Spring Cloud Function
applications to AWS Lambda with Gradle https://github.com/spring-cloud/spring-cloud-function/blob/{branch}/spring-cloud-function-samples/function-sample-aws/build.gradle[here].
==== 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"
}
----
NOTE: 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.
==== Type Conversion
Spring Cloud Function will attempt to transparently handle type conversion between the raw
input stream and types declared by your function.
For example, if your function signature is as such `Function<Foo, Bar>` we will attempt to convert
incoming stream event to an instance of `Foo`.
In the event type is not known or can not be determined (e.g., `Function<?, ?>`) we will attempt to
convert an incoming stream event to a generic `Map`.
====== Raw Input
There are times when you may want to have access to a raw input. In this case all you need is to declare your
function signature to accept `InputStream`. For example, `Function<InputStream, ?>`. In this case
we will not attempt any conversion and will pass the raw input directly to a function.

View File

@@ -1,95 +0,0 @@
*{project-version}*
The https://aws.amazon.com/[AWS] adapter takes a Spring Cloud Function app and converts it to a form that can run in AWS Lambda.
== Introduction
include::adapters/aws-intro.adoc[]
== 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 `ApplicationContextInitializer<GenericApplicationContext>` and use the `registerBean()` methods in `GenericApplicationContext` to
create all the beans you need. You function need to 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:
```java
@SpringBootConfiguration
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(FunctionTypeUtils.functionType(Foo.class, Bar.class)));
}
}
```
== AWS Context
In a typical implementation of AWS Handler user has access to AWS _context_ object. With function approach you can have the same experience if you need it.
Upon each invocation the framework will add `aws-context` message header containing the AWS _context_ instance for that particular invocation. So if you need to access it
you can simply have `Message<YourPojo>` as an input parameter to your function and then access `aws-context` from message headers.
For convenience we provide AWSLambdaUtils.AWS_CONTEXT constant.
== Platform 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:
|===
| Service | AWS Types | Generic 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
You can also benefit from https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html[AWS Lambda custom runtime] feature of AWS Lambda
and Spring Cloud Function provides all the necessary components to make it easy.
From the code perspective the application should look no different then any other Spring Cloud Function application.
The only thing you need to do is to provide a `bootstrap` script in the root of your zip/jar that runs the Spring Boot application.
and select "Custom Runtime" when creating a function in AWS.
Here is an example 'bootstrap' file:
```text
#!/bin/sh
cd ${LAMBDA_TASK_ROOT:-.}
java -Dspring.main.web-application-type=none -Dspring.jmx.enabled=false \
-noverify -XX:TieredStopAtLevel=1 -Xss256K -XX:MaxMetaspaceSize=128M \
-Djava.security.egd=file:/dev/./urandom \
-cp .:`echo lib/*.jar | tr ' ' :` com.example.LambdaApplication
```
The `com.example.LambdaApplication` represents your application which contains function beans.
Set the handler name in AWS to the name of your function. You can use function composition here as well (e.g., `uppecrase|reverse`).
That is pretty much all. Once you upload your zip/jar to AWS your function will run in custom runtime.
We provide a https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-aws-custom-new[sample project]
where you can also see how to configure yoru POM to properly generate the zip file.
The functional bean definition style works for custom runtimes as well, and is
faster than the `@Bean` style. 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.

View File

@@ -1,533 +0,0 @@
:branch: master
== Microsoft Azure Functions
:sectnums:
https://azure.microsoft.com[Azure] function adapter for deploying `Spring Cloud Function` applications as native Azure Java Functions.
The `Azure Functions` https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java[programming model] relays, extensively, on Java https://learn.microsoft.com/en-us/java/api/com.microsoft.azure.functions.annotation?view=azure-java-stable[annotations] for defining the function's handler methods and their input and output types.
At compile time the annotated classes are processed by the provided Azure Maven/Gradle plugins to generate the necessary Azure Function binding files, configurations and package artifacts.
The Azure annotations are just a type-safe way to configure your java function to be recognized as Azure function.
The https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-adapters/spring-cloud-function-adapter-azure[spring-cloud-function-adapter-azure] extends the basic programming model to provide Spring and Spring Cloud Function support.
With the adapter you can build your Spring Cloud Function application using dependency injections and then auto-wire the necessary services into your Azure handler methods.
image::../images/scf-azure-adapter.svg[width=800,scaledwidth="75%",align="center"]
TIP: For Web-based function applications, you can replace the generic `adapter-azure` with the specialized https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web[spring-cloud-function-adapter-azure-web].
With the Azure Web Adapter you can deploy any Spring Web application as an Azure, HttpTrigger, function.
This adapter hides the Azure annotations complexity and uses the familiar https://docs.spring.io/spring-boot/docs/current/reference/html/web.html[Spring Web] programming model instead.
For further information follow the <<azure.web.adapter,Azure Web Adapter>> section below.
== Azure Adapter
Provides `Spring` & `Spring Cloud Function` integration for Azure Functions.
=== Dependencies
In order to enable the Azure Function integration add the azure adapter dependency to your `pom.xml` or `build.gradle`
files:
====
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
.Maven
----
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-azure</artifactId>
</dependency>
</dependencies>
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Gradle
----
dependencies {
implementation 'org.springframework.cloud:spring-cloud-function-adapter-azure'
}
----
====
NOTE: version `4.0.0+` is required. Having the adapter on the classpath activates the Azure Java Worker integration.
[[azure.development.guidelines]]
=== Development Guidelines
Use the `@Component` (or `@Service`) annotation to turn any exiting Azure Function class (e.g. with `@FunctionName` handlers) into a Spring component.
Then you can auto-wire the required dependencies (or the <<spring-cloud-function.adoc#function.catalog,Function Catalog>> for Spring Cloud Function composition) and use those inside the Azure function handlers.
[source,java]
----
@Component // <1>
public class MyAzureFunction {
// Plain Spring bean - not a Spring Cloud Functions!
@Autowired private Function<String, String> uppercase; // <2>
// The FunctionCatalog leverages the Spring Cloud Function framework.
@Autowired private FunctionCatalog functionCatalog; // <2>
@FunctionName("spring") // <3>
public String plainBean( // <4>
@HttpTrigger(name = "req", authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
ExecutionContext context) {
return this.uppercase.apply(request.getBody().get());
}
@FunctionName("scf") // <3>
public String springCloudFunction( // <5>
@HttpTrigger(name = "req", authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
ExecutionContext context) {
// Use SCF composition. Composed functions are not just spring beans but SCF such.
Function composed = this.functionCatalog.lookup("reverse|uppercase"); // <6>
return (String) composed.apply(request.getBody().get());
}
}
----
<1> Indicates that the `MyAzureFunction` class is a "component" to be considered by the Spring Framework as a candidate for auto-detection and classpath scanning.
<2> Auto-wire the `uppercase` and `functionCatalog` beans defined in the `HttpTriggerDemoApplication` (below).
<3> The https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java?tabs=bash%2Cconsumption#java-function-basics[@FunctionName] annotation identifies the designated Azure function handlers.
When invoked by a trigger (such as `@HttpTrigger`), functions process that trigger, and any other inputs, to produce one or more outputs.
<4> The `plainBean` method handler is mapped to an Azure function that uses of the auto-wired `uppercase` spring bean to compute the result.
It demonstrates how to use "plain" Spring components in your Azure handlers.
<5> The `springCloudFunction` method handler is mapped to another Azure function, that uses the auto-wired `FunctionCatalog` instance to compute the result.
<6> Shows how to leverage the Spring Cloud Function <<spring-cloud-function.adoc#function.catalog,Function Catalog>> composition API.
TIP: Use the Java annotations included in the https://learn.microsoft.com/en-us/java/api/com.microsoft.azure.functions.annotation?view=azure-java-stable[com.microsoft.azure.functions.annotation.*] package to bind input and outputs to your methods.
The implementation of the business logic used inside the Azure handlers looks like a common Spring application:
[[HttpTriggerDemoApplication]]
[source,java]
----
@SpringBootApplication // <1>
public class HttpTriggerDemoApplication {
public static void main(String[] args) {
SpringApplication.run(HttpTriggerDemoApplication.class, args);
}
@Bean
public Function<String, String> uppercase() { // <2>
return payload -> payload.toUpperCase();
}
@Bean
public Function<String, String> reverse() { // <2>
return payload -> new StringBuilder(payload).reverse().toString();
}
}
----
<1> The `@SpringBootApplication` annotated class is used as a `Main-Class` as explained in <<star-class-configuration, main class configuration>>.
<2> Functions auto-wired and used in the Azure function handlers.
==== Function Catalog
The Spring Cloud Function supports a range of type signatures for user-defined functions, while providing a consistent execution model.
For this it uses the <<spring-cloud-function.adoc#function.catalog,Function Catalog>> to transform all user defined functions into a canonical representation.
The Azure adapter can auto-wire any Spring component, such as the `uppercase` above.
But those are treated as plain Java class instances, not as a canonical Spring Cloud Functions!
To leverage Spring Cloud Function and have access to the canonical function representations, you need to auto-wire the `FunctionCatalog` and use it in your handler, like the `functionCatalog` instance the `springCloudFunction()` handler above.
==== Accessing Azure ExecutionContext
Some time there is a need to access the target execution context provided by the 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 the `AzureFunctionUtil.enhanceInputIfNecessary` allow you to add an instance of the `ExecutionContext` as a Message header so you can retrieve it via `executionContext` key.
[source,java]
----
@FunctionName("myfunction")
public String execute(
@HttpTrigger(name = "req", authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
ExecutionContext context) {
Message message =
(Message) AzureFunctionUtil.enhanceInputIfNecessary(request.getBody().get(), context); // <1>
return this.uppercase.apply(message);
}
----
<1> Leverages the `AzureFunctionUtil` utility to inline the `context` as message header using the `AzureFunctionUtil.EXECUTION_CONTEXT` header key.
Now you can retrieve the ExecutionContext from message headers:
[source,java]
----
@Bean
public Function<Message<String>, String> uppercase(JsonMapper mapper) {
return message -> {
String value = message.getPayload();
ExecutionContext context =
(ExecutionContext) message.getHeaders().get(AzureFunctionUtil.EXECUTION_CONTEXT); // <1>
. . .
}
}
----
<1> Retrieve the ExecutionContext instance from the header.
[[azure.configuration]]
=== Configuration
To run your function applications on Microsoft Azure, you have to provide the necessary configurations, such as `function.json` and `host.json`, and adhere to the compulsory https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java?tabs=bash%2Cconsumption#folder-structure[packaging format].
Usually the Azure Maven (or Gradle) plugins are used to generate the necessary configurations from the annotated classes and to produce the required package format.
IMPORTANT: The Azure https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java?tabs=bash%2Cconsumption#folder-structure[packaging format] is not compatible with the default Spring Boot packaging (e.g. `uber jar`).
The <<disable.spring.boot.plugin,Disable Spring Boot Plugin>> section below explains how to handle this.
==== Azure Maven/Gradle Plugins
Azure provides https://github.com/microsoft/azure-maven-plugins/tree/develop/azure-functions-maven-plugin[Maven] and https://github.com/microsoft/azure-gradle-plugins/tree/master/azure-functions-gradle-plugin[Gradle] plugins to process the annotated classes, generate the necessary configurations and produce the expected package layout.
Plugins are used to set the platform, runtime and app-settings properties like this:
====
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
.Maven
----
<plugin>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-functions-maven-plugin</artifactId>
<version>1.22.0 or higher</version>
<configuration>
<appName>YOUR-AZURE-FUNCTION-APP-NAME</appName>
<resourceGroup>YOUR-AZURE-FUNCTION-RESOURCE-GROUP</resourceGroup>
<region>YOUR-AZURE-FUNCTION-APP-REGION</region>
<appServicePlanName>YOUR-AZURE-FUNCTION-APP-SERVICE-PLANE-NAME</appServicePlanName>
<pricingTier>YOUR-AZURE-FUNCTION-PRICING-TIER</pricingTier>
<hostJson>${project.basedir}/src/main/resources/host.json</hostJson>
<runtime>
<os>linux</os>
<javaVersion>11</javaVersion>
</runtime>
<appSettings>
<property>
<name>FUNCTIONS_EXTENSION_VERSION</name>
<value>~4</value>
</property>
</appSettings>
</configuration>
<executions>
<execution>
<id>package-functions</id>
<goals>
<goal>package</goal>
</goals>
</execution>
</executions>
</plugin>
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Gradle
----
plugins {
id "com.microsoft.azure.azurefunctions" version "1.11.0"
// ...
}
apply plugin: "com.microsoft.azure.azurefunctions"
azurefunctions {
appName = 'YOUR-AZURE-FUNCTION-APP-NAME'
resourceGroup = 'YOUR-AZURE-FUNCTION-RESOURCE-GROUP'
region = 'YOUR-AZURE-FUNCTION-APP-REGION'
appServicePlanName = 'YOUR-AZURE-FUNCTION-APP-SERVICE-PLANE-NAME'
pricingTier = 'YOUR-AZURE-FUNCTION-APP-SERVICE-PLANE-NAME'
runtime {
os = 'linux'
javaVersion = '11'
}
auth {
type = 'azure_cli'
}
appSettings {
FUNCTIONS_EXTENSION_VERSION = '~4'
}
// Uncomment to enable local debug
// localDebug = "transport=dt_socket,server=y,suspend=n,address=5005"
}
----
====
More information about the runtime configurations: https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java?tabs=bash%2Cconsumption#java-versions[Java Versions], https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java?tabs=bash%2Cconsumption#specify-the-deployment-os[Deployment OS].
[[disable.spring.boot.plugin]]
==== Disable Spring Boot Plugin
Expectedly, the Azure Functions run inside the Azure execution runtime, not inside the SpringBoot runtime!
Furthermore, Azure expects a specific packaging format, generated by the Azure Maven/Gradle plugins, that is not compatible with the default Spring Boot packaging.
You have to either disable the SpringBoot Maven/Gradle plugin or use the https://github.com/dsyer/spring-boot-thin-launcher[Spring Boot Thin Launcher] as shown in this Maven snippet:
[source,xml]
----
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-thin-layout</artifactId>
</dependency>
</dependencies>
</plugin>
----
[[star-class-configuration]]
==== Main-Class Configuration
Specify the `Main-Class`/`Start-Class` to point to your Spring application entry point, such as the <<HttpTriggerDemoApplication,HttpTriggerDemoApplication>> class in the example above.
You can use the Maven `start-class` property or set the `Main-Class` attribute of your `MANIFEST/META-INFO`:
====
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
.Maven
----
<properties>
<start-class>YOUR APP MAIN CLASS</start-class>
...
</properties>
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Gradle
----
jar {
manifest {
attributes(
"Main-Class": "YOUR-APP-MAIN-CLASS"
)
}
}
----
====
TIP: Alternatively you can use the `MAIN_CLASS` environment variable to set the class name explicitly.
For local runs, add the `MAIN_CLASS` variable to your `local.settings.json` file and for Azure portal deployment set the variable in the https://learn.microsoft.com/en-us/azure/azure-functions/functions-how-to-use-azure-function-app-settings?tabs=portal#get-started-in-the-azure-portal[App Settings].
IMPORTANT: If the `MAIN_CLASS` variable is not set, the Azure adapter lookups the `MANIFEST/META-INFO` attributes from the jars found on the classpath and selects the first `Main-Class:` annotated with either a `@SpringBootApplication` or `@SpringBootConfiguration` annotation.
==== Metadata Configuration
You can use a shared https://learn.microsoft.com/en-us/azure/azure-functions/functions-host-json[host.json] file to configure the function app.
[source,json]
----
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.*, 5.0.0)"
}
}
----
The host.json metadata file contains configuration options that affect all functions in a function app instance.
TIP: If the file is not in the project top folder you need to configure your plugins accordingly (like `hostJson` maven attribute).
=== Samples
Here is a list of various Spring Cloud Function Azure Adapter samples you can explore:
- https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-http-trigger[Http Trigger (Maven)]
- https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle[Http Trigger (Gradle)]
- https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-blob-trigger[Blob Trigger (Maven)]
- https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-timer-trigger[Timer Trigger (Maven)]
- https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-kafka-trigger[ Kafka Trigger & Output Binding (Maven)].
[[azure.web.adapter]]
== Azure Web Adapter
For, pure, Web-based function applications, you can replace the generic `adapter-azure` with the specialized https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web[spring-cloud-function-adapter-azure-web].
The Azure Web Adapter can deploy any Spring Web application as a native Azure function, using the HttpTrigger internally.
It hides the Azure annotations complexity and relies on the familiar https://docs.spring.io/spring-boot/docs/current/reference/html/web.html[Spring Web] programming model instead.
To enable the Azure Web Adapter, add the adapter dependency to your `pom.xml` or `build.gradle` files:
====
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
.Maven
----
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-azure-web</artifactId>
</dependency>
</dependencies>
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Gradle
----
dependencies {
implementation 'org.springframework.cloud:spring-cloud-function-adapter-azure-web'
}
----
====
The same <<azure.configuration, Configuration>> and <<azure.usage,Usage>> instructions apply to the `Azure Web Adapter` as well.
=== Samples
For further information, explore the following, Azure Web Adapter, sample:
- https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-web[ Azure Web Adapter (Maven)].
[[azure.usage]]
== Usage
Common instructions for building and deploying both, `Azure Adapter` and `Azure Web Adapter` type of applications.
=== Build
====
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
.Maven
----
./mvnw -U clean package
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Gradle
----
./gradlew azureFunctionsPackage
----
====
=== Running locally
To run locally on top of `Azure Functions`, and to deploy to your live Azure environment, you will need `Azure Functions Core Tools` installed along with the Azure CLI (see https://docs.microsoft.com/en-us/azure/azure-functions/create-first-function-cli-java?tabs=bash%2Cazure-cli%2Cbrowser#configure-your-local-environment[here]).
For some configuration you would need the https://learn.microsoft.com/en-us/azure/storage/common/storage-use-emulator[Azurite emulator] as well.
Then run the sample:
====
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
.Maven
----
./mvnw azure-functions:run
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Gradle
----
./gradlew azureFunctionsRun
----
====
=== Running on Azure
Make sure you are logged in your Azure account.
----
az login
----
and deploy
====
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
.Maven
----
./mvnw azure-functions:deploy
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Gradle
----
./gradlew azureFunctionsDeploy
----
====
=== Debug locally
Run the function in debug mode.
====
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
.Maven
----
./mvnw azure-functions:run -DenableDebug
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Gradle
----
// If you want to debug your functions, please add the following line
// to the azurefunctions section of your build.gradle.
azurefunctions {
...
localDebug = "transport=dt_socket,server=y,suspend=n,address=5005"
}
----
====
Alternatively and the `JAVA_OPTS` value to your `local.settings.json` like this:
[source,json]
----
{
"IsEncrypted": false,
"Values": {
...
"FUNCTIONS_WORKER_RUNTIME": "java",
"JAVA_OPTS": "-Djava.net.preferIPv4Stack=true -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=127.0.0.1:5005"
}
}
----
Here is snippet for a `VSCode` remote debugging configuration:
[source,json]
----
{
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "Attach to Remote Program",
"request": "attach",
"hostName": "localhost",
"port": "5005"
},
]
}
----
== FunctionInvoker (deprecated)
WARNING: The legacy `FunctionInvoker` programming model is deprecated and will not be supported going forward.
For additional documentation and samples about the Function Integration approach follow the https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure/[azure-sample] README and code.
== Relevant Links
- https://learn.microsoft.com/en-us/azure/developer/java/spring-framework/getting-started-with-spring-cloud-function-in-azure[Spring Cloud Function in Azure]
- https://spring.io/blog/2023/02/24/spring-cloud-function-for-azure-function[Spring Cloud Function for Azure Function (blog)]
- <<spring-cloud-function.adoc#,Spring Cloud Function - Reference Guide>>
- https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java?tabs=bash%2Cconsumption[Azure Functions Java developer guide]
- https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference?tabs=blob[Azure Functions developer guide]
:sectnums!:

View File

@@ -1,3 +0,0 @@
*{project-version}*
include::adapters/azure-intro.adoc[]

View File

@@ -1,295 +0,0 @@
:branch: master
=== Google Cloud Functions
The Google Cloud Functions adapter enables Spring Cloud Function apps to run on the https://cloud.google.com/functions[Google Cloud Functions] serverless platform.
You can either run the function locally using the open source https://github.com/GoogleCloudPlatform/functions-framework-java[Google Functions Framework for Java] or on GCP.
==== Project Dependencies
Start by adding the `spring-cloud-function-adapter-gcp` dependency to your project.
[source, xml]
----
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-gcp</artifactId>
</dependency>
...
</dependencies>
----
In addition, add the `spring-boot-maven-plugin` which will build the JAR of the function to deploy.
NOTE: Notice that we also reference `spring-cloud-function-adapter-gcp` as a dependency of the `spring-boot-maven-plugin`. This is necessary because it modifies the plugin to package your function in the correct JAR format for deployment on Google Cloud Functions.
[source, xml]
----
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<outputDirectory>target/deploy</outputDirectory>
</configuration>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-gcp</artifactId>
</dependency>
</dependencies>
</plugin>
----
Finally, add the Maven plugin provided as part of the Google Functions Framework for Java.
This allows you to test your functions locally via `mvn function:run`.
NOTE: The function target should always be set to `org.springframework.cloud.function.adapter.gcp.GcfJarLauncher`; this is an adapter class which acts as the entry point to your Spring Cloud Function from the Google Cloud Functions platform.
[source,xml]
----
<plugin>
<groupId>com.google.cloud.functions</groupId>
<artifactId>function-maven-plugin</artifactId>
<version>0.9.1</version>
<configuration>
<functionTarget>org.springframework.cloud.function.adapter.gcp.GcfJarLauncher</functionTarget>
<port>8080</port>
</configuration>
</plugin>
----
A full example of a working `pom.xml` can be found in the https://github.com/spring-cloud/spring-cloud-function/blob/master/spring-cloud-function-samples/function-sample-gcp-http/pom.xml[Spring Cloud Functions GCP sample].
==== HTTP Functions
Google Cloud Functions supports deploying https://cloud.google.com/functions/docs/writing/http[HTTP Functions], which are functions that are invoked by HTTP request. The sections below describe instructions for deploying a Spring Cloud Function as an HTTP Function.
===== Getting Started
Lets start with a simple Spring Cloud Function example:
[source, java]
----
@SpringBootApplication
public class CloudFunctionMain {
public static void main(String[] args) {
SpringApplication.run(CloudFunctionMain.class, args);
}
@Bean
public Function<String, String> uppercase() {
return value -> value.toUpperCase();
}
}
----
Specify your configuration main class in `resources/META-INF/MANIFEST.MF`.
[source]
----
Main-Class: com.example.CloudFunctionMain
----
Then run the function locally.
This is provided by the Google Cloud Functions `function-maven-plugin` described in the project dependencies section.
----
mvn function:run
----
Invoke the HTTP function:
----
curl http://localhost:8080/ -d "hello"
----
===== Deploy to GCP
Start by packaging your application.
----
mvn package
----
If you added the custom `spring-boot-maven-plugin` plugin defined above, you should see the resulting JAR in `target/deploy` directory.
This JAR is correctly formatted for deployment to Google Cloud Functions.
Next, make sure that you have the https://cloud.google.com/sdk/install[Cloud SDK CLI] installed.
From the project base directory run the following command to deploy.
----
gcloud functions deploy function-sample-gcp-http \
--entry-point org.springframework.cloud.function.adapter.gcp.GcfJarLauncher \
--runtime java11 \
--trigger-http \
--source target/deploy \
--memory 512MB
----
Invoke the HTTP function:
----
curl https://REGION-PROJECT_ID.cloudfunctions.net/function-sample-gcp-http -d "hello"
----
Setting custom HTTP statusCode:
----
Functions can specify a custom HTTP response code by setting the `FunctionInvoker.HTTP_STATUS_CODE` header.
----
[source, java]
----
@Bean
public Function<String, Message<String>> function() {
String payload = "hello";
Message<String> message = MessageBuilder.withPayload(payload).setHeader(FunctionInvoker.HTTP_STATUS_CODE, 404).build();
return input -> message;
};
----
==== Background Functions
Google Cloud Functions also supports deploying https://cloud.google.com/functions/docs/writing/background[Background Functions] which are invoked indirectly in response to an event, such as a message on a https://cloud.google.com/pubsub[Cloud Pub/Sub] topic, a change in a https://cloud.google.com/storage[Cloud Storage] bucket, or a https://firebase.google.com/[Firebase] event.
The `spring-cloud-function-adapter-gcp` allows for functions to be deployed as background functions as well.
The sections below describe the process for writing a Cloud Pub/Sub topic background function.
However, there are a number of different event types that can trigger a background function to execute which are not discussed here; these are described in the https://cloud.google.com/functions/docs/calling[Background Function triggers documentation].
===== Getting Started
Lets start with a simple Spring Cloud Function which will run as a GCF background function:
[source, java]
----
@SpringBootApplication
public class BackgroundFunctionMain {
public static void main(String[] args) {
SpringApplication.run(BackgroundFunctionMain.class, args);
}
@Bean
public Consumer<PubSubMessage> pubSubFunction() {
return message -> System.out.println("The Pub/Sub message data: " + message.getData());
}
}
----
In addition, create `PubSubMessage` class in the project with the below definition.
This class represents the https://cloud.google.com/functions/docs/calling/pubsub#event_structure[Pub/Sub event structure] which gets passed to your function on a Pub/Sub topic event.
[source, java]
----
public class PubSubMessage {
private String data;
private Map<String, String> attributes;
private String messageId;
private String publishTime;
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public Map<String, String> getAttributes() {
return attributes;
}
public void setAttributes(Map<String, String> attributes) {
this.attributes = attributes;
}
public String getMessageId() {
return messageId;
}
public void setMessageId(String messageId) {
this.messageId = messageId;
}
public String getPublishTime() {
return publishTime;
}
public void setPublishTime(String publishTime) {
this.publishTime = publishTime;
}
}
----
Specify your configuration main class in `resources/META-INF/MANIFEST.MF`.
[source]
----
Main-Class: com.example.BackgroundFunctionMain
----
Then run the function locally.
This is provided by the Google Cloud Functions `function-maven-plugin` described in the project dependencies section.
----
mvn function:run
----
Invoke the HTTP function:
----
curl localhost:8080 -H "Content-Type: application/json" -d '{"data":"hello"}'
----
Verify that the function was invoked by viewing the logs.
===== Deploy to GCP
In order to deploy your background function to GCP, first package your application.
----
mvn package
----
If you added the custom `spring-boot-maven-plugin` plugin defined above, you should see the resulting JAR in `target/deploy` directory.
This JAR is correctly formatted for deployment to Google Cloud Functions.
Next, make sure that you have the https://cloud.google.com/sdk/install[Cloud SDK CLI] installed.
From the project base directory run the following command to deploy.
----
gcloud functions deploy function-sample-gcp-background \
--entry-point org.springframework.cloud.function.adapter.gcp.GcfJarLauncher \
--runtime java11 \
--trigger-topic my-functions-topic \
--source target/deploy \
--memory 512MB
----
Google Cloud Function will now invoke the function every time a message is published to the topic specified by `--trigger-topic`.
For a walkthrough on testing and verifying your background function, see the instructions for running the https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-gcp-background/[GCF Background Function sample].
==== Sample Functions
The project provides the following sample functions as reference:
* The https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-gcp-http/[function-sample-gcp-http] is an HTTP Function which you can test locally and try deploying.
* The https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-gcp-background/[function-sample-gcp-background] shows an example of a background function that is triggered by a message being published to a specified Pub/Sub topic.

View File

@@ -1,3 +0,0 @@
*{project-version}*
include::adapters/gcp-intro.adoc[]

View File

@@ -1,316 +0,0 @@
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:
```java
@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);
}
}
```
Now for the functional beans: the user application code can be recast into "functional"
form, like this:
```java
@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(FunctionTypeUtils.functionType(String.class, 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):
```java
@SpringBootConfiguration
public class DemoApplication implements Function<String, String> {
public static void main(String[] args) {
FunctionalSpringApplication.run(DemoApplication.class, args);
}
@Override
public String apply(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.
Suppose you have
[source, java]
----
@Component
public class CustomFunction implements Function<Flux<Foo>, Flux<Bar>> {
@Override
public Flux<Bar> apply(Flux<Foo> flux) {
return flux.map(foo -> new Bar("This is a Bar object from Foo value: " + foo.getValue()));
}
}
----
You register it as such:
[source, java]
----
@Override
public void initialize(GenericApplicationContext context) {
context.registerBean("function", FunctionRegistration.class,
() -> new FunctionRegistration<>(new CustomFunction()).type(CustomFunction.class));
}
----
== 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.
[[function_visualization]]
= Function visualization and control
Spring Cloud Function supports visualization of functions available in `FunctionCatalog` through Actuator endpoints as well as programmatic way.
==== Programmatic way
To see function available within your application context programmatically all you need is access to `FunctionCatalog`. There you can
finds methods to get the size of the catalog, lookup functions as well as list the names of all the available functions.
For example,
[source,java]
----
FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class);
int size = functionCatalog.size(); // will tell you how many functions available in catalog
Set<String> names = functionCatalog.getNames(null); will list the names of all the Function, Suppliers and Consumers available in catalog
. . .
----
==== Actuator
Since actuator and web are optional, you must first add one of the web dependencies as well as add the actuator dependency manually.
The following example shows how to add the dependency for the Web framework:
[source,xml]
----
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
----
The following example shows how to add the dependency for the WebFlux framework:
[source,xml]
----
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
----
You can add the Actuator dependency as follows:
[source,xml]
----
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
----
You must also enable the `functions` actuator endpoints by setting the following property: `--management.endpoints.web.exposure.include=functions`.
Access the following URL to see the functions in FunctionCatalog:
`http://<host>:<port>/actuator/functions`
For example,
[source,text]
----
curl http://localhost:8080/actuator/functions
----
Your output should look something like this:
[source,text]
----
{"charCounter":
{"type":"FUNCTION","input-type":"string","output-type":"integer"},
"logger":
{"type":"CONSUMER","input-type":"string"},
"functionRouter":
{"type":"FUNCTION","input-type":"object","output-type":"object"},
"words":
{"type":"SUPPLIER","output-type":"string"}. . .
----
= Testing Functional Applications
Spring Cloud Function also has some utilities for integration testing that will be very familiar to Spring Boot users.
Suppose this is your application:
[source, java]
----
@SpringBootApplication
public class SampleFunctionApplication {
public static void main(String[] args) {
SpringApplication.run(SampleFunctionApplication.class, args);
}
@Bean
public Function<String, String> uppercase() {
return v -> v.toUpperCase();
}
}
----
Here is an integration test for the HTTP server wrapping this application:
[source, java]
----
@SpringBootTest(classes = SampleFunctionApplication.class,
webEnvironment = WebEnvironment.RANDOM_PORT)
public class WebFunctionTests {
@Autowired
private TestRestTemplate rest;
@Test
public void test() throws Exception {
ResponseEntity<String> result = this.rest.exchange(
RequestEntity.post(new URI("/uppercase")).body("hello"), String.class);
System.out.println(result.getBody());
}
}
----
or when function bean definition style is used:
[source, java]
----
@FunctionalSpringBootTest
public class WebFunctionTests {
@Autowired
private TestRestTemplate rest;
@Test
public void test() throws Exception {
ResponseEntity<String> result = this.rest.exchange(
RequestEntity.post(new URI("/uppercase")).body("hello"), String.class);
System.out.println(result.getBody());
}
}
----
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` `TestRestTemplate`, are standard Spring Boot features.
And to help with correct dependencies here is the excerpt from POM
[source, xml, subs=attributes+]
----
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>{spring-boot-version}</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
. . . .
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-web</artifactId>
<version>{project-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
----
Or you could write a test for a non-HTTP app using just the `FunctionCatalog`. For example:
[source, java]
----
@FunctionalSpringBootTest
public class FunctionalTests {
@Autowired
private FunctionCatalog catalog;
@Test
public void words() {
Function<String, String> function = catalog.lookup(Function.class,
"uppercase");
assertThat(function.apply("hello")).isEqualTo("HELLO");
}
}
----

View File

@@ -1,33 +0,0 @@
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 `^Q^J` in a terminal to insert a new line in a literal
string like that.)

View File

@@ -1,23 +0,0 @@
= Spring Cloud Function Reference Documentation
Mark Fisher, Dave Syer, Oleg Zhurakousky, Anshul Mehra, Dan Dobrin, Chris Bono, Artem Bilan
*{project-version}*
:docinfo: shared
The reference documentation consists of the following sections:
[horizontal]
<<spring-cloud-function.adoc#,Reference Guide>> :: Spring Cloud Function Reference
https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-cloudevent[Cloud Events] :: Cloud Events
https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-rsocket[RSocket] :: RSocket
<<./spring-integration.adoc#spring-integration,Spring Integration>> :: Spring Integration Framework Interaction
<<aws.adoc#,AWS Adapter>> :: AWS Adapter Reference
<<azure.adoc#, Azure Adapter>> :: Azure Adapter Reference
<<gcp.adoc#, GCP Adapter>> :: GCP Adapter Reference
Relevant Links:
[horizontal]
https://projectreactor.io/[Reactor] :: Project Reactor

View File

@@ -1,46 +0,0 @@
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.
## Features
Spring Cloud Function features:
* _Choice of programming styles - reactive, imperative or hybrid._
* _Function composition and adaptation (e.g., composing imperative functions with reactive)._
* _Support for reactive function with multiple inputs and outputs allowing merging, joining and other complex streaming operation to be handled by functions._
* _Transparent type conversion of inputs and outputs._
* _Packaging functions for deployments, specific to the target platform (e.g., Project Riff, AWS Lambda and more)_
* _Adapters to expose function to the outside world as HTTP endpoints etc._
* _Deploying a JAR file containing such an application context with an isolated classloader, so that you can pack them together in a single JVM._
* _Adapters for https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-aws[AWS Lambda], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-azure[Microsoft Azure], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp[Google Cloud Functions], 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 {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public Function<Flux<String>, Flux<String>> uppercase() {
return flux -> flux.map(value -> value.toUpperCase());
}
}
```
### Sample Projects
* https://github.com/spring-cloud/spring-cloud-function/blob/master/spring-cloud-function-samples/function-sample[Vanilla]
* https://github.com/spring-cloud/spring-cloud-function/blob/master/spring-cloud-function-samples/function-sample-pof[Plain Old Function]
* https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-aws[AWS Lambda]
* https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-azure[Microsoft Azure]
* https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-gcp-http[Google Cloud Functions]

File diff suppressed because it is too large Load Diff

View File

@@ -1,101 +0,0 @@
[[spring-integration]]
== Spring Integration Interaction
https://spring.io/projects/spring-integration[Spring Integration Framework] extends the Spring programming model to support the well-known Enterprise Integration Patterns.
It enables lightweight messaging within Spring-based applications and supports integration with external systems via declarative adapters.
It also provides a high-level DSL to compose various operations (endpoints) into a logical integration flow.
With a lambda style of this DSL configuration, Spring Integration already has a good level of `java.util.function` interfaces adoption.
The `@MessagingGateway` proxy interface can also be as a `Function` or `Consumer`, which according to the Spring Cloud Function environment can be registered into a function catalog.
See more information in Spring Integration https://docs.spring.io/spring-integration/docs/current/reference/html/messaging-endpoints.html#functions-support[ReferenceManual] about its support for functions.
On the other hand, starting with version `4.0.3`, Spring Cloud Function introduces a `spring-cloud-function-integration` module which provides deeper, more cloud-specific and auto-configuration based API for interaction with a `FunctionCatalog` from Spring Integration DSL perspective.
The `FunctionFlowBuilder` is auto-configured and autowired with a `FunctionCatalog` and represents an entry point for function-specific DSL for target `IntegrationFlow` instance.
In addition to standard `IntegrationFlow.from()` factories (for convenience), the `FunctionFlowBuilder` exposes a `fromSupplier(String supplierDefinition)` factory to lookup the target `Supplier` in the provided `FunctionCatalog`.
Then this `FunctionFlowBuilder` leads to the `FunctionFlowDefinition`.
This `FunctionFlowDefinition` is an implementation of the `IntegrationFlowExtension` and exposes `apply(String functionDefinition)` and `accept(String consumerDefinition)` operators to lookup `Function` or `Consumer` from the `FunctionCatalog`, respectively.
See their Javadocs for more information.
The following example demonstrates the `FunctionFlowBuilder` in action alongside with the power of the rest of `IntegrationFlow` API:
[source,java]
----
@Configuration
public class IntegrationConfiguration {
@Bean
Supplier<byte[]> simpleByteArraySupplier() {
return "simple test data"::getBytes;
}
@Bean
Function<String, String> upperCaseFunction() {
return String::toUpperCase;
}
@Bean
BlockingQueue<String> results() {
return new LinkedBlockingQueue<>();
}
@Bean
Consumer<String> simpleStringConsumer(BlockingQueue<String> results) {
return results::add;
}
@Bean
QueueChannel wireTapChannel() {
return new QueueChannel();
}
@Bean
IntegrationFlow someFunctionFlow(FunctionFlowBuilder functionFlowBuilder) {
return functionFlowBuilder
.fromSupplier("simpleByteArraySupplier")
.wireTap("wireTapChannel")
.apply("upperCaseFunction")
.log(LoggingHandler.Level.WARN)
.accept("simpleStringConsumer");
}
}
----
Since the `FunctionCatalog.lookup()` functionality is not limited just to simple function names, a function composition feature can also be used in the mentioned `apply()` and `accept()` operators:
[source,java]
----
@Bean
IntegrationFlow functionCompositionFlow(FunctionFlowBuilder functionFlowBuilder) {
return functionFlowBuilder
.from("functionCompositionInput")
.accept("upperCaseFunction|simpleStringConsumer");
}
----
This API becomes more relevant, when we add into our Spring Cloud applications auto-configuration dependencies for predefined functions.
For example https://spring.io/projects/spring-cloud-stream-applications[Stream Applications] project, in addition to application images, provides artifacts with functions for various integration use-case, e.g. `debezium-supplier`, `elasticsearch-consumer`, `aggregator-function` etc.
The following configuration is based on the `http-supplier`, `spel-function` and `file-consumer`, respectively:
[source,java]
----
@Bean
IntegrationFlow someFunctionFlow(FunctionFlowBuilder functionFlowBuilder) {
return functionFlowBuilder
.fromSupplier("httpSupplier", e -> e.poller(Pollers.trigger(new OnlyOnceTrigger())))
.<Flux<?>>handle((fluxPayload, headers) -> fluxPayload, e -> e.async(true))
.channel(c -> c.flux())
.apply("spelFunction")
.<String, String>transform(String::toUpperCase)
.accept("fileConsumer");
}
----
What we would need else is just to add their configuration into an `application.properties` (if necessary):
[source,properties]
----
http.path-pattern=/testPath
spel.function.expression=new String(payload)
file.consumer.name=test-data.txt
----