Skip to content

Latest commit

 

History

History
561 lines (397 loc) · 23.9 KB

functions-reference-java.md

File metadata and controls

561 lines (397 loc) · 23.9 KB
title description ms.topic ms.date ms.custom
Java developer reference for Azure Functions
Understand how to develop functions with Java.
conceptual
09/14/2018
devx-track-java, devx-track-azurecli

Azure Functions Java developer guide

This guide contains detailed information to help you succeed developing Azure Functions using Java.

As a Java developer, if you're new to Azure Functions, please consider first reading one of the following articles:

Getting started Concepts

Java function basics

A Java function is a public method, decorated with the annotation @FunctionName. This method defines the entry for a Java function, and must be unique in a particular package. The package can have multiple classes with multiple public methods annotated with @FunctionName. A single package is deployed to a function app in Azure. When running in Azure, the function app provides the deployment, execution, and management context for your individual Java functions.

Programming model

The concepts of triggers and bindings are fundamental to Azure Functions. Triggers start the execution of your code. Bindings give you a way to pass data to and return data from a function, without having to write custom data access code.

Create Java functions

To make it easier to create Java functions, there are Maven-based tooling and archetypes that use predefined Java templates to help you create projects with a specific function trigger.

Maven-based tooling

The following developer environments have Azure Functions tooling that lets you create Java function projects:

The article links above show you how to create your first functions using your IDE of choice.

Project Scaffolding

If you prefer command line development from the Terminal, the simplest way to scaffold Java-based function projects is to use Apache Maven archetypes. The Java Maven archetype for Azure Functions is published under the following groupId:artifactId: com.microsoft.azure:azure-functions-archetype.

The following command generates a new Java function project using this archetype:

mvn archetype:generate \
    -DarchetypeGroupId=com.microsoft.azure \
    -DarchetypeArtifactId=azure-functions-archetype
mvn archetype:generate ^
    -DarchetypeGroupId=com.microsoft.azure ^
    -DarchetypeArtifactId=azure-functions-archetype

To get started using this archetype, see the Java quickstart.

Folder structure

Here is the folder structure of an Azure Functions Java project:

FunctionsProject
 | - src
 | | - main
 | | | - java
 | | | | - FunctionApp
 | | | | | - MyFirstFunction.java
 | | | | | - MySecondFunction.java
 | - target
 | | - azure-functions
 | | | - FunctionApp
 | | | | - FunctionApp.jar
 | | | | - host.json
 | | | | - MyFirstFunction
 | | | | | - function.json
 | | | | - MySecondFunction
 | | | | | - function.json
 | | | | - bin
 | | | | - lib
 | - pom.xml

You can use a shared host.json file to configure the function app. Each function has its own code file (.java) and binding configuration file (function.json).

You can put more than one function in a project. Avoid putting your functions into separate jars. The FunctionApp in the target directory is what gets deployed to your function app in Azure.

Triggers and annotations

Functions are invoked by a trigger, such as an HTTP request, a timer, or an update to data. Your function needs to process that trigger, and any other inputs, to produce one or more outputs.

Use the Java annotations included in the com.microsoft.azure.functions.annotation.* package to bind input and outputs to your methods. For more information, see the Java reference docs.

Important

You must configure an Azure Storage account in your local.settings.json to run Azure Blob storage, Azure Queue storage, or Azure Table storage triggers locally.

Example:

public class Function {
    public String echo(@HttpTrigger(name = "req", 
      methods = {HttpMethod.POST},  authLevel = AuthorizationLevel.ANONYMOUS) 
        String req, ExecutionContext context) {
        return String.format(req);
    }
}

Here is the generated corresponding function.json by the azure-functions-maven-plugin:

{
  "scriptFile": "azure-functions-example.jar",
  "entryPoint": "com.example.Function.echo",
  "bindings": [
    {
      "type": "httpTrigger",
      "name": "req",
      "direction": "in",
      "authLevel": "anonymous",
      "methods": [ "GET","POST" ]
    },
    {
      "type": "http",
      "name": "$return",
      "direction": "out"
    }
  ]
}

Java versions

The version of Java used when creating the function app on which functions runs in Azure is specified in the pom.xml file. The Maven archetype currently generates a pom.xml for Java 8, which you can change before publishing. The Java version in pom.xml should match the version on which you have locally developed and tested your app.

Supported versions

The following table shows current supported Java versions for each major version of the Functions runtime, by operating system:

Functions version Java versions (Windows) Java versions (Linux)
3.x 11
8
11
8
2.x 8 n/a

Unless you specify a Java version for your deployment, the Maven archetype defaults to Java 8 during deployment to Azure.

Specify the deployment version

You can control the version of Java targeted by the Maven archetype by using the -DjavaVersion parameter. The value of this parameter can be either 8 or 11.

The Maven archetype generates a pom.xml that targets the specified Java version. The following elements in pom.xml indicate the Java version to use:

Element Java 8 value Java 11 value Description
Java.version 1.8 11 Version of Java used by the maven-compiler-plugin.
JavaVersion 8 11 Java version hosted by the function app in Azure.

The following examples show the settings for Java 8 in the relevant sections of the pom.xml file:

Java.version

:::code language="xml" source="~/functions-quickstart-java/functions-add-output-binding-storage-queue/pom.xml" range="12-19" highlight="14":::

JavaVersion

:::code language="xml" source="~/functions-quickstart-java/functions-add-output-binding-storage-queue/pom.xml" range="77-85" highlight="80":::

Important

You must have the JAVA_HOME environment variable set correctly to the JDK directory that is used during code compiling using Maven. Make sure that the version of the JDK is at least as high as the Java.version setting.

Specify the deployment OS

Maven also lets you specify the operating system on which your function app runs in Azure. Use the os element to choose the operating system.

Element Windows Linux Docker
os windows linux docker

The following example shows the operating system setting in the runtime section of the pom.xml file:

:::code language="xml" source="~/functions-quickstart-java/functions-add-output-binding-storage-queue/pom.xml" range="77-85" highlight="79":::

JDK runtime availability and support

For local development of Java function apps, download and use the appropriate Azul Zulu Enterprise for Azure Java JDKs from Azul Systems. Azure Functions uses an Azul Java JDK runtime when you deploy your function app to the cloud.

Azure support for issues with the JDKs and function apps is available with a qualified support plan.

Customize JVM

Functions lets you customize the Java virtual machine (JVM) used to run your Java functions. The following JVM options are used by default:

  • -XX:+TieredCompilation
  • -XX:TieredStopAtLevel=1
  • -noverify
  • -Djava.net.preferIPv4Stack=true
  • -jar

You can provide additional arguments in an app setting named JAVA_OPTS. You can add app settings to your function app deployed to Azure in the Azure portal or the Azure CLI.

Important

In the Consumption plan, you must also add the WEBSITE_USE_PLACEHOLDER setting with a value of 0 for the customization to work. This setting does increase the cold start times for Java functions.

Azure portal

In the Azure portal, use the Application Settings tab to add the JAVA_OPTS setting.

Azure CLI

You can use the az functionapp config appsettings set command to set JAVA_OPTS, as in the following example:

az functionapp config appsettings set \
    --settings "JAVA_OPTS=-Djava.awt.headless=true" \
    "WEBSITE_USE_PLACEHOLDER=0" \
    --name <APP_NAME> --resource-group <RESOURCE_GROUP>
az functionapp config appsettings set ^
    --settings "JAVA_OPTS=-Djava.awt.headless=true" ^
    "WEBSITE_USE_PLACEHOLDER=0" ^
    --name <APP_NAME> --resource-group <RESOURCE_GROUP>
az functionapp config appsettings set \
    --settings "JAVA_OPTS=-Djava.awt.headless=true" \
    --name <APP_NAME> --resource-group <RESOURCE_GROUP>
az functionapp config appsettings set ^
    --settings "JAVA_OPTS=-Djava.awt.headless=true" ^
    --name <APP_NAME> --resource-group <RESOURCE_GROUP>

This example enables headless mode. Replace <APP_NAME> with the name of your function app, and <RESOURCE_GROUP> with the resource group.

Third-party libraries

Azure Functions supports the use of third-party libraries. By default, all dependencies specified in your project pom.xml file are automatically bundled during the mvn package goal. For libraries not specified as dependencies in the pom.xml file, place them in a lib directory in the function's root directory. Dependencies placed in the lib directory are added to the system class loader at runtime.

The com.microsoft.azure.functions:azure-functions-java-library dependency is provided on the classpath by default, and doesn't need to be included in the lib directory. Also, azure-functions-java-worker adds dependencies listed here to the classpath.

Data type support

You can use Plain old Java objects (POJOs), types defined in azure-functions-java-library, or primitive data types such as String and Integer to bind to input or output bindings.

POJOs

For converting input data to POJO, azure-functions-java-worker uses the gson library. POJO types used as inputs to functions should be public.

Binary data

Bind binary inputs or outputs to byte[], by setting the dataType field in your function.json to binary:

   @FunctionName("BlobTrigger")
    @StorageAccount("AzureWebJobsStorage")
     public void blobTrigger(
        @BlobTrigger(name = "content", path = "myblob/{fileName}", dataType = "binary") byte[] content,
        @BindingName("fileName") String fileName,
        final ExecutionContext context
    ) {
        context.getLogger().info("Java Blob trigger function processed a blob.\n Name: " + fileName + "\n Size: " + content.length + " Bytes");
    }

If you expect null values, use Optional<T>.

Bindings

Input and output bindings provide a declarative way to connect to data from within your code. A function can have multiple input and output bindings.

Input binding example

package com.example;

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

public class Function {
    @FunctionName("echo")
    public static String echo(
        @HttpTrigger(name = "req", methods = { HttpMethod.PUT }, authLevel = AuthorizationLevel.ANONYMOUS, route = "items/{id}") String inputReq,
        @TableInput(name = "item", tableName = "items", partitionKey = "Example", rowKey = "{id}", connection = "AzureWebJobsStorage") TestInputData inputData,
        @TableOutput(name = "myOutputTable", tableName = "Person", connection = "AzureWebJobsStorage") OutputBinding<Person> testOutputData
    ) {
        testOutputData.setValue(new Person(httpbody + "Partition", httpbody + "Row", httpbody + "Name"));
        return "Hello, " + inputReq + " and " + inputData.getKey() + ".";
    }

    public static class TestInputData {
        public String getKey() { return this.RowKey; }
        private String RowKey;
    }
    public static class Person {
        public String PartitionKey;
        public String RowKey;
        public String Name;

        public Person(String p, String r, String n) {
            this.PartitionKey = p;
            this.RowKey = r;
            this.Name = n;
        }
    }
}

You invoke this function with an HTTP request.

  • HTTP request payload is passed as a String for the argument inputReq.
  • One entry is retrieved from Table storage, and is passed as TestInputData to the argument inputData.

To receive a batch of inputs, you can bind to String[], POJO[], List<String>, or List<POJO>.

@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;
}

This function gets triggered whenever there is new data in the configured event hub. Because the cardinality is set to MANY, the function receives a batch of messages from the event hub. EventData from event hub gets converted to TestEventData for the function execution.

Output binding example

You can bind an output binding to the return value by using $return.

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 static String copy(@BlobTrigger(name = "blob", path = "samples-input-java/{name}") String content) {
        return content;
    }
}

If there are multiple output bindings, use the return value for only one of them.

To send multiple output values, use OutputBinding<T> defined in the azure-functions-java-library package.

@FunctionName("QueueOutputPOJOList")
    public HttpResponseMessage QueueOutputPOJOList(@HttpTrigger(name = "req", methods = { HttpMethod.GET,
            HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
            @QueueOutput(name = "itemsOut", queueName = "test-output-java-pojo", connection = "AzureWebJobsStorage") OutputBinding<List<TestData>> itemsOut, 
            final ExecutionContext context) {
        context.getLogger().info("Java HTTP trigger processed a request.");
       
        String query = request.getQueryParameters().get("queueMessageId");
        String queueMessageId = request.getBody().orElse(query);
        itemsOut.setValue(new ArrayList<TestData>());
        if (queueMessageId != null) {
            TestData testData1 = new TestData();
            testData1.id = "msg1"+queueMessageId;
            TestData testData2 = new TestData();
            testData2.id = "msg2"+queueMessageId;

            itemsOut.getValue().add(testData1);
            itemsOut.getValue().add(testData2);

            return request.createResponseBuilder(HttpStatus.OK).body("Hello, " + queueMessageId).build();
        } else {
            return request.createResponseBuilder(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("Did not find expected items in CosmosDB input list").build();
        }
    }

     public static class TestData {
        public String id;
    }

You invoke this function on an HttpRequest. It writes multiple values to Queue storage.

HttpRequestMessage and HttpResponseMessage

These are defined in azure-functions-java-library. They are helper types to work with HttpTrigger functions.

Specialized type Target Typical usage
HttpRequestMessage<T> HTTP Trigger Gets method, headers, or queries
HttpResponseMessage HTTP Output Binding Returns status other than 200

Metadata

Few triggers send trigger metadata along with input data. You can use annotation @BindingName to bind to trigger metadata.

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);
    }
}

In the preceding example, the queryValue is bound to the query string parameter name in the HTTP request URL, http://{example.host}/api/metadata?name=test. Here's another example, showing how to bind to Id from queue trigger metadata.

 @FunctionName("QueueTriggerMetadata")
    public void QueueTriggerMetadata(
        @QueueTrigger(name = "message", queueName = "test-input-java-metadata", connection = "AzureWebJobsStorage") String message,@BindingName("Id") String metadataId,
        @QueueOutput(name = "output", queueName = "test-output-java-metadata", connection = "AzureWebJobsStorage") OutputBinding<TestData> output,
        final ExecutionContext context
    ) {
        context.getLogger().info("Java Queue trigger function processed a message: " + message + " with metadaId:" + metadataId );
        TestData testData = new TestData();
        testData.id = metadataId;
        output.setValue(testData);
    }

Note

The name provided in the annotation needs to match the metadata property.

Execution context

ExecutionContext, defined in the azure-functions-java-library, contains helper methods to communicate with the functions runtime. For more information, see the ExecutionContext reference article.

Logger

Use getLogger, defined in ExecutionContext, to write logs from function code.

Example:

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

public class Function {
    public String echo(@HttpTrigger(name = "req", methods = {HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) String req, ExecutionContext context) {
        if (req.isEmpty()) {
            context.getLogger().warning("Empty request body received by function " + context.getFunctionName() + " with invocation " + context.getInvocationId());
        }
        return String.format(req);
    }
}

View logs and trace

You can use the Azure CLI to stream Java stdout and stderr logging, as well as other application logging.

Here's how to configure your function app to write application logging by using the Azure CLI:

az webapp log config --name functionname --resource-group myResourceGroup --application-logging true
az webapp log config --name functionname --resource-group myResourceGroup --application-logging true

To stream logging output for your function app by using the Azure CLI, open a new command prompt, Bash, or Terminal session, and enter the following command:

az webapp log tail --name webappname --resource-group myResourceGroup
az webapp log tail --name webappname --resource-group myResourceGroup

The az webapp log tail command has options to filter output by using the --provider option.

To download the log files as a single ZIP file by using the Azure CLI, open a new command prompt, Bash, or Terminal session, and enter the following command:

az webapp log download --resource-group resourcegroupname --name functionappname

You must have enabled file system logging in the Azure portal or the Azure CLI before running this command.

Environment variables

In Functions, app settings, such as service connection strings, are exposed as environment variables during execution. You can access these settings by using, System.getenv("AzureWebJobsStorage").

The following example gets the application setting, with the key named myAppSetting:

public class Function {
    public String echo(@HttpTrigger(name = "req", methods = {HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) String req, ExecutionContext context) {
        context.getLogger().info("My app setting value: "+ System.getenv("myAppSetting"));
        return String.format(req);
    }
}

Note

The value of AppSetting FUNCTIONS_EXTENSION_VERSION should be ~2 or ~3 for an optimized cold start experience.

Next steps

For more information about Azure Functions Java development, see the following resources: