Azure Functions doesn't only support C# but also supports many other languages, including Java, JavaScript, Python, PowerShell and so forth. Specifically, I'm going to discuss a few things to know when you're developing a Java-based Azure Functions app, especially on Mac.
Local Development Environments
It's not that a big issue when using either Visual Studio or Visual Studio Code for .NET function app development. In addition to that, it's not that a big issue when using Visual Studio Code for JavaScript-based function app development, either.
Basic reference docs for Java devs to develop Azure Functions app, please refer to this Azure Functions Java Developers' Guide page.
It's common that the majority of Java devs use IntelliJ IDEA (IntelliJ). I believe that Java Function app development also uses IntelliJ. But if you're on Mac, you should know the following Mac-specific issues so that you don't get hiccups.
Things to Know When Using IntelliJ
-
IntelliJ Edition:
There's no dependency on the Spring framework for Azure Functions app development. Therefore, you don't need the Ultimate Edition for Function app development. Of course, if you want to utilise many other features that UE only supports, you should use UE. But basically, CE would be sufficient for Function app development.
It's also better to install the Azure Tools for IntelliJ plug-in for the Function app development because it's bootstrapping and templating all the Function app related stuff.
The doc page, Building the First Java Function App with IntelliJ, provides essential information to build a Java function app and deploy it to Azure.
-
Maven Build:
When you write an Azure Functions app with IntelliJ, it has the dependency on the Maven build. So, if you want to integrate with the Gradle build, follow this page, Building an Azure Functions app with Gradle, and import it to IntelliJ.
Things to Know When Using IntelliJ on Mac OS
Let's focus on the "Azure Functions app development". How IntelliJ works on Mac is different from doing on Windows in that context. The following two issues are only happening on Mac.
-
Azure Functions Core Tools Loading:
If you install Azure Functions Core Tools on Windows, open IntelliJ and run the Azure Functions app locally, it works with no issue.
However, if you install Azure Functions Core Tools on Mac, open IntelliJ from Finder or Dock, and run the Azure Functions app locally, it throws the error like below – it doesn't recognise Azure Functions Core Tools.
By the way, you can open IntelliJ from Terminal by typing the
idea .
command.With this IntelliJ instance, run the Azure Functions app locally. Then, it recognises Azure Functions Core Tools and runs the app with no issue.
Why does this discrepancy occur? It's because the loaded environment variables are different between "Finder-launched application" and "Shell-launched application". Due to this difference, JVM is loaded differently. This issue has already been addressed.
When you see the
$PATH
variable from the "Finder-launched IntelliJ", it looks like this:Let's check the same
$PATH
variable from the "Shell-launched IntelliJ". Can you see the difference?It's not fixed yet. It's not even sure whether the issue should go to the plugin or IntelliJ. Therefore, the only workaround, for now, is that you should open the IntelliJ instance from the command line.
-
Azure Functions Port Lockout:
You run the Azure Functions App within IntelliJ, stop running the app, and re-run the app. Then, you will see the following error – the port 7071 is still being used.
Azure Functions uses two processes – main process and sub-process. The Azure Functions runtime runs on the main process, and the language worker runs on the sub-process. In the shell environment or Visual Studio Code, all the child processes are also terminated when the main process is terminated. On the other hand, IntelliJ only kills the main process, not the child process, which occupies the existing port.
In this case, you have to manually find the process holding the port number and kill it. Here are the sample bash commands.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersprocess_id=$(sudo lsof -nP -i4TCP:7071 | grep LISTEN | awk '{print $2}') sudo kill -9 $process_id Alternatively, you can download the following shell script and run it when necessary.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters#!/bin/bash # Even after IntelliJ IDEA stops running the function app, # the process is still alive. It only happens on IntelliJ IDEA on Mac. # This script finds the Azure Functions process and kill it. # # You don't need this script if you use terminal or Visual Studio Code. # You don't need this script if you run IntelliJ IDEA on Windows. # # Usage: ./kill-process.sh -p 7071 # set -e # Usage function function usage() { cat <<USAGE Usage: $0 [-p|--port-number <port number>] [-h|--help] Options: -p|--port-number: The port number to kill. Default: 7071 -h|--help: Show this message. USAGE exit 1 } # Set up arguments port_number=7071 if [[ $# -eq 0 ]]; then port_number=7071 fi while [[ "$1" != "" ]]; do case $1 in -p | --port-number) shift port_number=$1 ;; -h | --help) usage exit 1 ;; *) usage exit 1 ;; esac shift done if [[ $port_number == "" ]]; then echo "Port number not set" usage exit 1 fi echo "[$(date +"%Y-%m-%d %H:%M:%S")] Killing the process currently holding the port ..." process_id=$(sudo lsof -nP -i4TCP:$port_number | grep LISTEN | awk '{print $2}') if [[ $process_id != "" ]]; then echo "[$(date +"%Y-%m-%d %H:%M:%S")] Process ID: $process_id being killed ..." sudo kill -9 $process_id echo "[$(date +"%Y-%m-%d %H:%M:%S")] Process ID: $process_id has been killed. You can now run the app." else echo "[$(date +"%Y-%m-%d %H:%M:%S")] No process found to kill. You can now run the app." fi As I've also piled an issue on the plugin repository, I'll update it here how it's going.
Azure Deployment Environment
According to the doc, Azure Functions supports both Java 8 and 11. Therefore, you need to configure the pom.xml
file with the value of 1.8 or 11 for the java.version
property and the value of 8 or 11 for the javaVersion
property (line #6-7, 18-19, 32).
<?xml version="1.0" encoding="UTF-8"?> | |
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
... | |
<properties> | |
... | |
<java.version>1.8</java.version> | |
<javaVersion>8</javaVersion> | |
... | |
</properties> | |
... | |
<build> | |
<plugins> | |
<plugin> | |
<groupId>org.apache.maven.plugins</groupId> | |
<artifactId>maven-compiler-plugin</artifactId> | |
<version>3.8.1</version> | |
<configuration> | |
<source>${java.version}</source> | |
<target>${java.version}</target> | |
<encoding>${project.build.sourceEncoding}</encoding> | |
</configuration> | |
</plugin> | |
<plugin> | |
<groupId>com.microsoft.azure</groupId> | |
<artifactId>azure-functions-maven-plugin</artifactId> | |
<version>${azure.functions.maven.plugin.version}</version> | |
<configuration> | |
... | |
<runtime> | |
<!-- runtime os, could be windows, linux or docker--> | |
<os>windows</os> | |
<javaVersion>${javaVersion}</javaVersion> | |
... | |
</runtime> | |
... | |
</plugin> | |
... | |
</plugins> | |
</build> | |
</project> |
Here's a catch. If you provision the Azure Functions app instance through Azure Portal, you can choose the Java version.
However, you MUST explicitly declare the Java version when you provision the instance through bicep or ARM template; otherwise it is set to Java 8 by default. Therefore, if you want to deploy the function app with Java 11, this has to be declared in the bicep file to avoid confusion.
Here's the bicep sample. For the Linux plan, you should declare the linuxFxVersion
to Java|11
(line #8). For the Windows plan you should declare the javaVersion
to 11
(line #11).
resource <symbolic-name> 'Microsoft.Web/sites@2021-02-01' = { | |
... | |
properties: { | |
... | |
siteConfig: { | |
... | |
// Linux plan only | |
linuxFxVersion = 'Java|11' | |
// Windows Plan only | |
javaVersion = '11' | |
} | |
} | |
} |
With the higher Java version (ie. Java 11) declared, you can deploy the app compiled in a lower version (ie. Java 8) and run it with no issue. But with the lower version (ie. Java 8) declared, your app compiled in the higher version (ie. Java 11) is deployed but will never work.
So far, we've discussed how to write an Azure Functions app in Java, what possible issues you should know if you're developing it on Mac, and what you should know when you deploy the app to Azure. While developing the app, I guess you will face other issues, but these issues and workarounds identified in this post will be the bare minimum points you should know.