Skip to content

Repository files navigation

Azure Functions Logo

Branch Status
dev Build Status
v2.x Build Status

Library for Azure Java Functions

This repo contains library for building Azure Java Functions. Visit the complete documentation of Azure Functions - Java Developer Guide for more details.

The dev branch will be used to make any changes necessary to support v4 extension bundle.

The v2.x branch will be used to make any changes necessary to support v3 extension bundle.

azure-functions-maven plugin

How to use azure-functions-maven plugin to create, update, deploy and test azure java functions

Prerequisites

Package feed

All Maven packages and plugins are restored from the upstream-public Azure Artifacts feed (https://pkgs.dev.azure.com/azfunc/public/_packaging/upstream-public/maven/v1), which is configured as the central repository in every pom.xml in this repository.

The repository root also has a settings.xml that mirrors every remote repository (external:*) to the same feed. It exists because a pom.xml cannot cover everything:

  • Maven resolves build extensions and plugin prefixes before a pom's <repositories> are honored, so those requests would otherwise go straight to Maven Central.
  • MavenAuthenticate@0 and the credential provider key credentials off the Azure Artifacts feed name (upstream-public), while the pom repository id must be central in order to override the id Maven inherits from the Super POM. The mirror id bridges the two.
  • build.ps1 clones and builds third-party Maven projects whose poms declare repositories this repository does not control, so only the mirror can keep those restores on the feed.

CI installs this file to ~/.m2/settings.xml. Locally you only need it when pulling a package or version the feed has not cached yet, in which case pass it explicitly with mvn -s settings.xml.

Anonymous restore (default)

The feed allows anonymous reads, so no credentials are required to build once a package version has been saved to the feed. External contributors and fresh clones need no setup. mvn just works. Never commit credentials or a <server> entry to settings.xml in this repository because doing so would force authentication on everyone.

Authenticating (Microsoft developers only)

Authentication is only needed to ingest a package version that the feed has not cached yet. The first restore of any new or upgraded dependency will fail anonymously with:

No local versions of package '...'; please provide authentication to access versions from upstream that have not yet been saved to your feed.

When that happens, a Microsoft developer with access to the azfunc/public project must run the restore once with credentials, which pulls the version from upstream and saves it to the feed. Every subsequent anonymous restore then succeeds.

The recommended way to authenticate is the artifacts-maven-credprovider, which acquires a token via Entra ID so you do not have to manage a PAT.

Run the helper script for your shell from the root of your clone. It installs the credential provider into your local Maven repository if it is missing, then writes .mvn/extensions.xml. Both scripts are idempotent, so re-running them is safe:

./eng/scripts/Install-MavenCredentialProvider.ps1
./eng/scripts/install-maven-credprovider.sh

Pass -Version / --version to install a different release, and -Force / --force to reinstall or to overwrite an .mvn/extensions.xml the script does not manage.

If you would rather do it by hand, the equivalent steps are:

  1. Bootstrap the credential provider once per machine. Run this from a directory outside any Maven project, such as your home directory. It downloads the extension from the public AzureArtifacts tools feed, which needs no authentication:

    mvn dependency:get "-Dartifact=com.microsoft.azure:artifacts-maven-credprovider:3.2.1" "-DremoteRepositories=central::::https://pkgs.dev.azure.com/artifacts-public/PublicTools/_packaging/AzureArtifacts/maven/v1"

    Using the repository id central matters. Maven records the extension as having come from central, which is the same id this repository's pom.xml files declare, so the cached copy validates during later builds.

  2. Create .mvn/extensions.xml at the root of your clone:

    <extensions xmlns="http://maven.apache.org/EXTENSIONS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/EXTENSIONS/1.1.0 https://maven.apache.org/xsd/core-extensions-1.0.0.xsd">
        <extension>
            <groupId>com.microsoft.azure</groupId>
            <artifactId>artifacts-maven-credprovider</artifactId>
            <version>3.2.1</version>
        </extension>
    </extensions>

.mvn/ is deliberately listed in .gitignore. Do not commit it. The extension exits when it detects a build context, and committing it would break anonymous restores for everyone else.

If you would rather not use the credential provider, you can instead add a <server> entry to your user-level ~/.m2/settings.xml (never to a file inside this repository), using an Azure DevOps personal access token with Packaging read and write scope:

<settings>
    <servers>
        <server>
            <!-- Must match the <id> of the repository declared in the pom.xml files. -->
            <id>central</id>
            <username>azfunc</username>
            <password>[PERSONAL_ACCESS_TOKEN]</password>
        </server>
    </servers>
</settings>

CI covers this automatically. The MavenAuthenticate@0 task in the build templates authenticates the central repository, so merged changes to dependency versions are ingested by the pipeline. The credential provider is not used in pipelines.

Parent POM

Please see for details on Parent POM https://github.com/Microsoft/maven-java-parent

Summary

Azure Functions is a solution for easily running small pieces of code, or "functions," in the cloud. You can write just the code you need for the problem at hand, without worrying about a whole application or the infrastructure to run it. Functions can make development even more productive.Pay only for the time your code runs and trust Azure to scale as needed. Azure Functions lets you develop serverless applications on Microsoft Azure.

Azure Functions supports triggers, which are ways to start execution of your code, and bindings, which are ways to simplify coding for input and output data. A function should be a stateless method to process input and produce output. Although you are allowed to write instance methods, your function must not depend on any instance fields of the class. You need to make sure all the function methods are public accessible and method with annotation @FunctionName is unique as that defines the entry for the the function.

A deployable unit is an uber JAR containing one or more functions (see below), and a JSON file with the list of functions and triggers definitions, deployed to Azure Functions. The JAR can be created in many ways, although we recommend Azure Functions Maven Plugin, as it provides templates to get you started with key scenarios.

All the input and output bindings can be defined in function.json (not recommended), or in the Java method by using annotations (recommended). All the types and annotations used in this document are included in the azure-functions-java-library package.

Sample

Here is an example of a HttpTrigger Azure function in Java:

package com.example;

import com.microsoft.azure.functions.annotation.*;

public class Function {
    @FunctionName("echo")
    public static String echo(@HttpTrigger(name = "req", methods = { HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) String in) {
        return "Hello, " + in + ".";
    }
}

Adding 3rd Party Libraries

Azure Functions supports the use of 3rd party libraries. If using the Maven plugin for Azure Functions, all of your dependencies specified in your pom.xml file will be automatically bundled during the mvn package step.

Data Types

You are free to use all the data types in Java for the input and output data, including native types; customized POJO types and specialized Azure types defined in this API. Azure Functions runtime will try its best to convert the actual input value to the type you need (for example, a String input will be treated as a JSON string and be parsed to a POJO type defined in your code).

JSON Support

The POJO types (Java classes) you may define have to be publicly accessible (public modifier). POJO properties/fields may be private. For example a JSON string { "x": 3 } is able to be converted to the following POJO type:

public class PojoData {
    private int x;
}

Other supported types

Binary data is represented as byte[] or Byte[] in your Azure functions code. And make sure you specify dataType = "binary" in the corresponding triggers/bindings.

Empty input values could be null as your functions argument, but a recommended way to deal with potential empty values is to use Optional<T> type.

Inputs

Inputs are divided into two categories in Azure Functions: one is the trigger input and the other is the additional input. Trigger input is the input who triggers your function. And besides that, you may also want to get inputs from other sources (like a blob), that is the additional input.

Let's take the following code snippet as an example:

package com.example;

import com.microsoft.azure.functions.annotation.*;

public class Function {
    @FunctionName("echo")
    public String echo(
        @HttpTrigger(name = "req", methods = { HttpMethod.PUT }, authLevel = AuthorizationLevel.ANONYMOUS, route = "items/{id}") String in,
        @TableInput(name = "item", tableName = "items", partitionKey = "example", rowKey = "{id}", connection = "AzureWebJobsStorage") TestInputData inputData
    ) {
        return "Hello, " + in + " and " + inputData.getRowKey() + ".";
    }

}

public class TestInputData {
    public String getRowKey() { return this.rowKey; }
    private String rowKey;
}

When this function is invoked, the HTTP request payload will be passed as the String for argument in; and one entry will be retrieved from the Azure Table Storage and be passed to argument inputData as TestInputData type.

To receive events in a batch when using EventHubTrigger, set cardinality to many and change input type to an array or List<>

@FunctionName("ProcessIotMessages")
    public void processIotMessages(
        @EventHubTrigger(name = "message", eventHubName = "%AzureWebJobsEventHubPath%", connection = "AzureWebJobsEventHubSender", cardinality = Cardinality.MANY) List<TestEventData> messages,
        final ExecutionContext context)
    {
        context.getLogger().info("Java Event Hub trigger received messages. Batch size: " + messages.size());
    }
    
    public class TestEventData {
    public String id;
}

Note: You can also bind to String[], TestEventData[] or List

Outputs

Outputs can be expressed in return value or output parameters. If there is only one output, you are recommended to use the return value. For multiple outputs, you have to use output parameters.

Return value is the simplest form of output, you just return the value of any type, and Azure Functions runtime will try to marshal it back to the actual type (such as an HTTP response). You could apply any output annotations to the function method (the name property of the annotation has to be $return) to define the return value output.

For example, a blob content copying function could be defined as the following code. @StorageAccount annotation is used here to prevent the duplicating of the connection property for both @BlobTrigger and @BlobOutput.

package com.example;

import com.microsoft.azure.functions.annotation.*;

public class Function {
    @FunctionName("copy")
    @StorageAccount("AzureWebJobsStorage")
    @BlobOutput(name = "$return", path = "samples-output-java/{name}")
    public String copy(@BlobTrigger(name = "blob", path = "samples-input-java/{name}") String content) {
        return content;
    }
}

To produce multiple output values, use OutputBinding<T> type defined in the azure-functions-java-library package. If you need to make an HTTP response and push a message to a queue, you can write something like:

package com.example;

import com.microsoft.azure.functions.*;
import com.microsoft.azure.functions.annotation.*;

public class Function {
    @FunctionName("push")
    public String push(
        @HttpTrigger(name = "req", methods = { HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) String body,
        @QueueOutput(name = "message", queueName = "myqueue", connection = "AzureWebJobsStorage") OutputBinding<String> queue
    ) {
        queue.setValue("This is the queue message to be pushed");
        return "This is the HTTP response content";
    }
}

Use OutputBinding<byte[]> type to make a binary output value (for parameters); for return values, just use byte[].

Execution Context

You interact with Azure Functions execution environment via the ExecutionContext object defined in the azure-functions-java-library package. You are able to get the invocation ID, the function name and a built-in logger (which is integrated prefectly with Azure Function Portal experience as well as AppInsights) from the context object.

What you need to do is just add one more ExecutionContext typed parameter to your function method. Let's take a timer triggered function as an example:

package com.example;

import com.microsoft.azure.functions.*;
import com.microsoft.azure.functions.annotation.*;

public class Function {
    @FunctionName("heartbeat")
    public static void heartbeat(
        @TimerTrigger(name = "schedule", schedule = "*/30 * * * * *") String timerInfo,
        ExecutionContext context
    ) {
        context.getLogger().info("Heartbeat triggered by " + context.getFunctionName());
    }
}

Specialized Data Types

HTTP Request and Response

Sometimes a function need to take a more detailed control of the input and output, and that's why we also provide some specialized types in the azure-functions-java-library package for you to manipulate:

Specialized Type Target Typical Usage
HttpRequestMessage<T> HTTP Trigger Get method, headers or queries
HttpResponseMessage HTTP Output Binding Return status other than 200

Metadata

Metadata comes from different sources, like HTTP headers, HTTP queries, and trigger metadata. You can use @BindingName annotation together with the metadata name to get the value.

For example, the queryValue in the following code snippet will be "test" if the requested URL is http://{example.host}/api/metadata?name=test.

package com.example;

import java.util.Optional;
import com.microsoft.azure.functions.annotation.*;

public class Function {
    @FunctionName("metadata")
    public static String metadata(
        @HttpTrigger(name = "req", methods = { HttpMethod.GET, HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) Optional<String> body,
        @BindingName("name") String queryValue
    ) {
        return body.orElse(queryValue);
    }
}

License

This project is under the benevolent umbrella of the .NET Foundation and is licensed under the MIT License.

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.

About

Contains annotations for writing Azure Functions in Java

Resources

Code of conduct

Security policy

Stars

46 stars

Watchers

34 watching

Forks

Releases

Packages

Used by

Contributors

Languages