azure-functions-java-worker/samples/spring-cloud-example
..
src
.gitignore
README.md
pom.xml

README.md

Spring Cloud Function in Azure

This article guides you through using Spring Cloud Functions to develop a Java function and publish it to Azure Functions. When you're done, your function code runs on the Consumption Plan in Azure and can be triggered using an HTTP request.

[!INCLUDE quickstarts-free-trial-note]

Prerequisites

To develop functions using Java, you must have the following installed:

[!IMPORTANT]

  1. You must set the JAVA_HOME environment variable to the install location of the JDK to complete this quickstart.
  2. Make sure your core tools version is at least 4.0.5030

What we're going to build

We're going to build a classical "Hello, World" function, that runs on Azure Functions, and which is configured with Spring Cloud Function.

It will receive a simple User JSON object, which contains a user name, and send back a Greeting object, which contains the welcome message to that user.

The project we'll build is available in the samples of azure-function-java-worker repository on GitHub. You can use that sample repository directly if you want to see the final work described in this quickstart.

Create a new Maven project

We're going to create an empty Maven project, and configure it with Spring Cloud Function and Azure Functions.

In an empty folder, create a new pom.xml file and copy/paste the content from the sample project's pom.xml file.

[!NOTE] This file uses Maven dependencies from both Spring Boot and Spring Cloud Function, and it configures the Spring Boot and Azure Functions Maven plugins.

You need to customize a few properties for your application:

  • <functionAppName> is the name of your Azure Function
  • <functionAppRegion> is the name of the Azure region where your Function is deployed
  • <functionResourceGroup> is the name of the Azure resource group you're using

Change those properties directly near the top of the pom.xml file, as shown in the following example:

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>

        <azure.functions.maven.plugin.version>1.22.0</azure.functions.maven.plugin.version>
        <azure.functions.java.library.version>3.0.0</azure.functions.java.library.version>

        <!-- customize those two properties. The functionAppName should be unique across Azure -->
        <functionResourceGroup>my-spring-function-resource-group</functionResourceGroup>
        <functionAppServicePlanName>my-spring-function-service-plan</functionAppServicePlanName>
        <functionAppName>my-spring-function</functionAppName>

        <functionAppRegion>westeurope</functionAppRegion>
        <start-class>example.Application</start-class>
    </properties>

Create Azure configuration files

Create a src/main/azure folder and add the following Azure Functions configuration files to it.

host.json:

{
  "version": "2.0",
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[4.*, 5.0.0)"
  },
  "functionTimeout": "00:10:00"
}

local.settings.json:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "",
    "FUNCTIONS_WORKER_RUNTIME": "java",
    "FUNCTIONS_EXTENSION_VERSION": "~4",
    "AzureWebJobsDashboard": ""
  }
}

Create domain objects

Azure Functions can receive and send objects in JSON format. We're now going to create our User and Greeting objects, which represent our domain model. You can create more complex objects, with more properties, if you want to customize this quickstart and make it more interesting for you.

Create a src/main/java/com/example/model folder and add the following two files:

User.java:

package com.example.model;

public class User {

    private String name;

    public User() {
    }

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Greeting.java:

package com.example.model;

public class Greeting {

    private String message;

    public Greeting() {
    }

    public Greeting(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

Create the Spring Boot application

This application will manage all business logic, and will have access to the full Spring Boot ecosystem. This capability gives you two main benefits over a standard Azure Function:

  • It doesn't rely on the Azure Functions APIs, so you can easily port it to other systems. For example, you can reuse it in a normal Spring Boot application.
  • You can use all the @Enable annotations from Spring Boot to add new features.

In the src/main/java/example folder, create the following file, which is a normal Spring Boot application:

DemoApplication.java:

package example;

import example.uppercase.Config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(Config.class, args);
    }
}

Now create the following file src/main/java/example/hello folder, which contains a Spring Boot component that represents the Function we want to run:

Hello.java:

package example.hello;

import example.hello.model.*;
import org.springframework.stereotype.Component;
import java.util.function.Function;

@Component
public class Hello implements Function<User, Greeting> {

    @Override
    public Greeting apply(User user) {
        return new Greeting("Hello, " + user.getName() + "!\n");
    }
}

[!NOTE] The Hello function is quite specific:

  • It is a java.util.function.Function. It contains the business logic, and it uses a standard Java API to transform one object into another.
  • Because it has the @Component annotation, it's a Spring Bean, and by default its name is the same as the class, but starting with a lowercase character: hello. Following this naming convention is important if you want to create other functions in your application. The name must match the Azure Functions name we'll create in the next section.

Create the Azure Function

To benefit from the full Azure Functions API, we're now going to code an Azure Function that will delegate its execution to the Spring Cloud Function we've created in the previous step.

In the src/main/java/com/example/hello folder, create the following Azure Function class file:

HelloHandler.java:

package example.hello;

import com.microsoft.azure.functions.*;
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.HttpTrigger;
import example.hello.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Optional;

@Component
public class HelloHandler {

    @Autowired
    private Hello hello;

    @FunctionName("hello")
    public HttpResponseMessage execute(
            @HttpTrigger(name = "request", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<User>> request,
            ExecutionContext context) {
        User user = request.getBody()
                .filter(u -> u.getName() != null)
                .orElseGet(() -> new User(request.getQueryParameters().getOrDefault("name", "world")));
        context.getLogger().info("Greeting user name: " + user.getName());
        return request
                .createResponseBuilder(HttpStatus.OK)
                .body(hello.apply(user))
                .header("Content-Type", "application/json")
                .build();
    }
}

This Java class is an Azure Function, with the following interesting features:

  • It has @Component annotation, it's a Spring Bean.
  • The name of the function, as defined by the @FunctionName("hello") annotation, is hello.
  • It's a real Azure Function, so you can use the full Azure Functions API here.

Add unit tests

This step is optional but recommended to validate that the application works correctly.

Create a src/test/java/com/example folder and add the following JUnit tests:

HelloTest.java:

package example.hello;

import example.hello.model.Greeting;
import example.hello.model.User;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class HelloTest {

    @Test
    public void test() {
        Greeting result = new Hello().apply(new User("foo"));
        assertThat(result.getMessage()).isEqualTo("Hello, foo!\n");
    }
}

You can now test your Azure Function using Maven:

mvn clean test

Run the Function locally

Before you deploy your application to Azure Function, let's first test it locally.

First you need to package your application into a Jar file:

mvn package

Now that the application is packaged, you can run it using the azure-functions Maven plugin:

mvn azure-functions:run

The Azure Function should now be available on your localhost, using port 7071. You can test the function by sending it a POST request, with a User object in JSON format. For example, using cURL:

curl -X POST http://localhost:7071/api/hello -d "{\"name\":\"Azure\"}"

The Function should answer you with a Greeting object, still in JSON format:

{
  "message": "Hello, Azure!\n"
}

Here's a screenshot of the cURL request on the top of the screen, and the local Azure Function at the bottom:

Azure Function running locally

Debug the Function locally

The following sections describe how to debug the function.

Debug using Intellij IDEA

Open the project in Intellij IDEA, then create a Remote JVM Debug run configuration to attach. For more information, see Tutorial: Remote debug.

Create a Remote JVM Debug run configuration

Run the application with the following command:

mvn azure-functions:run -DenableDebug

When the application starts, you'll see the following output:

Worker process started and initialized.
Listening for transport dt_socket at address: 5005

Start project debugging in Intellij IDEA. You'll see the following output:

Connected to the target VM, address: 'localhost:5005', transport: 'socket'

Mark the breakpoints you want to debug. After sending a request, the Intellij IDEA will enter debugging mode.

Debug using Visual Studio Code

Open the project in Visual Studio Code, then configure the following launch.json file content:

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "java",
            "name": "Attach to Remote Program",
            "request": "attach",
            "hostName": "127.0.0.1",
            "port": 5005
        }
    ]
}

Run the application with the following command:

mvn azure-functions:run -DenableDebug

When the application starts, you'll see the following output:

Worker process started and initialized.
Listening for transport dt_socket at address: 5005

Start project debugging in Visual Studio Code, then mark the breakpoints you want to debug. After sending a request, Visual Studio Code will enter debugging mode. For more information, see Running and debugging Java.

Deploy the Function to Azure Functions

Now you're going to publish the Azure Function to production. Remember that the <functionAppName>, <functionAppRegion>, and <functionResourceGroup> properties you've defined in your pom.xml file will be used to configure your function.

[!NOTE] The Maven plugin needs to authenticate with Azure. If you have Azure CLI installed, use az login before continuing. For more authentication options, see Authentication in the azure-maven-plugins repository.

Run Maven to deploy your function automatically:

mvn azure-functions:deploy

Now go to the Azure portal to find the Function App that has been created.

Select the function:

  • In the function overview, note the function's URL.
  • Select the Platform features tab to find the Log streaming service, then select this service to check your running function.

Now, as you did in the previous section, use cURL to access the running function, as shown in the following example. Be sure to replace your-function-name by your real function name.

curl https://your-function-name.azurewebsites.net/api/hello -d "{\"name\":\"Azure\"}"

Like in the previous section, the Function should answer you with a Greeting object, still in JSON format:

{
  "message": "Hello, Azure!\n"
}

Congratulations, you have a Spring Cloud Function running on Azure Functions! For more docs and samples of spring cloud functions please checkout below links

Next steps

To learn more about Spring and Azure, continue to the Spring on Azure documentation center.

[!div class="nextstepaction"] Spring on Azure