[[microsoft-azure-functions]]
= Microsoft Azure Functions
:page-aliases: adapters/azure.adoc

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::{github-raw}/docs/src/main/asciidoc/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 xref:adapters/azure-intro.adoc#azure.web.adapter[Azure Web Adapter] section below.

[[azure-adapter]]
== Azure Adapter

Provides `Spring` & `Spring Cloud Function` integration for Azure Functions.

[[dependencies]]
=== Dependencies

In order to enable the Azure Function integration add the azure adapter dependency to your `pom.xml` or `build.gradle`
files:

[tabs]
======
Maven::
+
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
----
<dependencies>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-function-adapter-azure</artifactId>
	</dependency>
</dependencies>
----

Gradle::
+
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
----
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 xref:spring-cloud-function/programming-model.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",
				methods = { HttpMethod.POST },
				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",
			methods = { HttpMethod.POST },
			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 xref:spring-cloud-function/programming-model.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 xref:adapters/azure-intro.adoc#star-class-configuration[main class configuration].
<2> Functions auto-wired and used in the Azure function handlers.

[[function-catalog]]
=== 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 xref:spring-cloud-function/programming-model.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]]
=== 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",
		methods = { HttpMethod.POST },
		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 xref:adapters/azure-intro.adoc#disable.spring.boot.plugin[Disable Spring Boot Plugin] section below explains how to handle this.

[[azure-maven-gradle-plugins]]
=== 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:

[tabs]
======
Maven::
+

[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
----
<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>
----

Gradle::
+

[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
----
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 xref:adapters/azure-intro.adoc#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`:

[tabs]
======
Maven::
+

[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
----
<properties>
	<start-class>YOUR APP MAIN CLASS</start-class>
	...
</properties>
----

Gradle::
+

[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
----
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]]
=== 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]]
== 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:

[tabs]
======
Maven::
+
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
----
<dependencies>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-function-adapter-azure-web</artifactId>
	</dependency>
</dependencies>
----

Gradle::
+
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
----
dependencies {
    implementation 'org.springframework.cloud:spring-cloud-function-adapter-azure-web'
}
----
======

The same xref:adapters/azure-intro.adoc#azure.configuration[Configuration] and xref:adapters/azure-intro.adoc#azure.usage[Usage] instructions apply to the `Azure Web Adapter` as well.


== Azure 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]]
== Build

[tabs]
======
Maven::
+
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
----
./mvnw -U clean package
----

Gradle::
+
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
----
./gradlew azureFunctionsPackage
----
======

[[running-locally]]
== 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:

[tabs]
======
Maven::
+
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
----
./mvnw azure-functions:run
----

Gradle::
+
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
----
./gradlew azureFunctionsRun
----
======

[[running-on-azure]]
== Running on Azure

Make sure you are logged in your Azure account.

----
az login
----

and deploy

[tabs]
======
Maven::
+
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
----
./mvnw azure-functions:deploy
----

Gradle::
+

[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
----
./gradlew azureFunctionsDeploy
----
======

[[debug-locally]]
== Debug locally

Run the function in debug mode.

[tabs]
======
Maven::
+

[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
----
./mvnw azure-functions:run -DenableDebug
----

Gradle::
+

[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
----
// 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]]
== 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]]
== 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!:
