4.0.6-SNAPSHOT
Microsoft Azure Functions
Azure function adapter for deploying Spring Cloud Function
applications as native Azure Java Functions.
The Azure Functions
programming model relays, extensively, on Java 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 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.
For Web-based function applications, you can replace the generic adapter-azure with the specialized 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 Spring Web programming model instead.
For further information follow the Azure Web Adapter section below.
|
1. Azure Adapter
Provides Spring
& Spring Cloud Function
integration for Azure Functions.
1.1. Dependencies
In order to enable the Azure Function integration add the azure adapter dependency to your pom.xml
or build.gradle
files:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-azure</artifactId>
</dependency>
</dependencies>
dependencies {
implementation 'org.springframework.cloud:spring-cloud-function-adapter-azure'
}
version 4.0.0+ is required. Having the adapter on the classpath activates the Azure Java Worker integration.
|
1.2. 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 Function Catalog for Spring Cloud Function composition) and use those inside the Azure function handlers.
@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 @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 Function Catalog composition API. |
Use the Java annotations included in the 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:
@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 main class configuration. |
2 | Functions auto-wired and used in the Azure function handlers. |
1.2.1. 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 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.
1.2.2. 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.
@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:
@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. |
1.3. 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 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.
The Azure packaging format is not compatible with the default Spring Boot packaging (e.g. uber jar ).
The Disable Spring Boot Plugin section below explains how to handle this.
|
1.3.1. Azure Maven/Gradle Plugins
Azure provides Maven and 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:
<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>
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: Java Versions, Deployment OS.
1.3.2. 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 Spring Boot Thin Launcher as shown in this Maven snippet:
<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>
1.3.3. Main-Class Configuration
Specify the Main-Class
/Start-Class
to point to your Spring application entry point, such as the 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
:
<properties>
<start-class>YOUR APP MAIN CLASS</start-class>
...
</properties>
jar {
manifest {
attributes(
"Main-Class": "YOUR-APP-MAIN-CLASS"
)
}
}
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 App Settings.
|
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.
|
1.3.4. Metadata Configuration
You can use a shared host.json file to configure the function app.
{
"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.
If the file is not in the project top folder you need to configure your plugins accordingly (like hostJson maven attribute).
|
1.4. Samples
Here is a list of various Spring Cloud Function Azure Adapter samples you can explore:
2. Azure Web Adapter
For, pure, Web-based function applications, you can replace the generic adapter-azure
with the specialized 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 Spring Web programming model instead.
To enable the Azure Web Adapter, add the adapter dependency to your pom.xml
or build.gradle
files:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-azure-web</artifactId>
</dependency>
</dependencies>
dependencies {
implementation 'org.springframework.cloud:spring-cloud-function-adapter-azure-web'
}
The same Configuration and Usage instructions apply to the Azure Web Adapter
as well.
2.1. Samples
For further information, explore the following, Azure Web Adapter, sample:
3. Usage
Common instructions for building and deploying both, Azure Adapter
and Azure Web Adapter
type of applications.
3.1. Build
./mvnw -U clean package
./gradlew azureFunctionsPackage
3.2. 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 here).
For some configuration you would need the Azurite emulator as well.
Then run the sample:
./mvnw azure-functions:run
./gradlew azureFunctionsRun
3.3. Running on Azure
Make sure you are logged in your Azure account.
az login
and deploy
./mvnw azure-functions:deploy
./gradlew azureFunctionsDeploy
3.4. Debug locally
Run the function in debug mode.
./mvnw azure-functions:run -DenableDebug
// 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:
{
"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:
{
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "Attach to Remote Program",
"request": "attach",
"hostName": "localhost",
"port": "5005"
},
]
}
4. FunctionInvoker (deprecated)
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 azure-sample README and code.