From debc9f8b30161116d34e6858082e63a895d03e75 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Sun, 23 Sep 2018 08:47:52 +0100 Subject: [PATCH] Update user guide azure content --- .../main/asciidoc/adapters/azure-intro.adoc | 49 +++++++------------ docs/src/main/asciidoc/adapters/azure.adoc | 2 +- .../java/com/example/SampleApplication.java | 21 ++++---- 3 files changed, 30 insertions(+), 42 deletions(-) diff --git a/docs/src/main/asciidoc/adapters/azure-intro.adoc b/docs/src/main/asciidoc/adapters/azure-intro.adoc index e73701d50..66abe5ebf 100644 --- a/docs/src/main/asciidoc/adapters/azure-intro.adoc +++ b/docs/src/main/asciidoc/adapters/azure-intro.adoc @@ -1,41 +1,30 @@ 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. -The adapter has a generic HTTP request handler that you can use optionally. -There is a `AzureSpringBootRequestHandler` which you must extend, and provide the input and output types as type parameters (enabling Azure to inspect the class and do the JSON conversions itself). +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. -If your app has more than one `@Bean` of type `Function` etc. then you can choose the one to use by configuring `function.name`. -The functions are extracted from the Spring Cloud `FunctionCatalog`. +Example: -=== Notes on JAR Layout - -You don't need the Spring Cloud Function Web at runtime in Azure, so you need to exclude this before you create the JAR you deploy to Azure. -A function application on Azure 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 here). -The sample app creates the shaded jar file, with an `azure` classifier for deploying in Azure. - -=== JSON Configuration - -The Azure tooling needs to find some JSON configuration files to tell it how to deploy and integrate the function (e.g. which Java class to use as the entry point, and which triggers to use). Those files can be created with the Maven plugin for a non-Spring function, but the tooling doesn't work yet with the adapter in its current form. There is an example `function.json` in the sample which hooks the function up as an HTTP endpoint: - -``` -{ - "scriptFile" : "../function-sample-azure-2.0.0.BUILD-SNAPSHOT-azure.jar", - "entryPoint" : "example.FooHandler.execute", - "bindings" : [ { - "type" : "httpTrigger", - "name" : "foo", - "direction" : "in", - "authLevel" : "anonymous", - "methods" : [ "get", "post" ] - }, { - "type" : "http", - "name" : "$return", - "direction" : "out" - } ], - "disabled" : false +```java +public class FooHandler extends AzureSpringBootRequestHandler { + @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` bean (or a `Function,Publisher>`). 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`, 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. + +=== 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 diff --git a/docs/src/main/asciidoc/adapters/azure.adoc b/docs/src/main/asciidoc/adapters/azure.adoc index c465b92cf..d228403c7 100644 --- a/docs/src/main/asciidoc/adapters/azure.adoc +++ b/docs/src/main/asciidoc/adapters/azure.adoc @@ -1,3 +1,3 @@ -The https://azure.microsoft.com[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 Spring Cloud Function Azure adapter trades the convenience of these annotations for portability of the function implementations. Instead of using the annotations you have to write some JSON by hand (at least for now) to guide the platform to call the right methods in the adapter. +The https://azure.microsoft.com[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. include::azure-intro.adoc[] \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-pojo/src/main/java/com/example/SampleApplication.java b/spring-cloud-function-samples/function-sample-pojo/src/main/java/com/example/SampleApplication.java index 2cf66d468..bd836f9a0 100644 --- a/spring-cloud-function-samples/function-sample-pojo/src/main/java/com/example/SampleApplication.java +++ b/spring-cloud-function-samples/function-sample-pojo/src/main/java/com/example/SampleApplication.java @@ -15,17 +15,18 @@ */ package com.example; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.util.MultiValueMap; -import reactor.core.publisher.Flux; - import java.util.HashMap; import java.util.Map; import java.util.function.Function; import java.util.function.Supplier; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.util.MultiValueMap; + +import reactor.core.publisher.Flux; + @SpringBootApplication public class SampleApplication { @@ -41,18 +42,16 @@ public class SampleApplication { @Bean public Function, Map> sum() { return multiValueMap -> { - Map result = new HashMap<>(); - - multiValueMap.forEach((s, strings) -> result.put(s, strings.stream().mapToInt(Integer::parseInt).sum())); - + multiValueMap.forEach((s, strings) -> result.put(s, + strings.stream().mapToInt(Integer::parseInt).sum())); return result; }; } @Bean public Supplier> words() { - return () -> Flux.fromArray(new Foo[]{new Foo("foo"), new Foo("bar")}).log(); + return () -> Flux.fromArray(new Foo[] { new Foo("foo"), new Foo("bar") }).log(); } }