【Azure Funcsions】ローカルにAzure Functions[Java8]開発環境を構築する(win10)

事前準備

chocolatey インストール

chocolatey は Windows用の Package Manager (非公式)です。公式のPackageManagementのProviderにもなっています。

インストール

chocolatey をインストールするには Power Shell を開いて次のように実行します。

Set-ExecutionPolicy Bypass -Scope Process -Force; iex
  ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

JDK(zulu8) セットアップ

JDKは OLACLE のJDKではなく AZUL社のJDKを使います。OLACLE JDKはサポートしていないようで、ビルドするときにエラーになります。

Zulu JDK8 をセットアップするには コマンドプロンプトを管理者で開き次のように実行します。

セットアップ

cinst zulu8 -y

環境変数

Zulu JDKのディレクトリを環境変数へ設定します。「システムの環境変数」を開き次のように設定します。

※Pathについては既存の設定に追記します。

Path=C:\Program Files\Zulu\zulu-8\bin
JAVA_HOME=C:\Program Files\Zulu\zulu-8

確認

> java -version
openjdk version "1.8.0_292"
OpenJDK Runtime Environment (Zulu 8.54.0.21-CA-win64) (build 1.8.0_292-b10)
OpenJDK 64-Bit Server VM (Zulu 8.54.0.21-CA-win64) (build 25.292-b10, mixed mode)

Maven セットアップ

Maven とはJavaプロジェクトの作成、ビルド、実行を行うツールです。

Maven をセットアップするには コマンドプロンプトを管理者で開き次のように実行します。

セットアップ

cinst maven -y

確認

> mvn -v
Apache Maven 3.6.2 (40f52333136460af0dc0d7; 2019-08-28T00:06:16+09:00)
Maven home: C:\ProgramData\chocolatey\lib\maven\apache-maven-3.6.2\bin\..
Java version: 1.8.0_292, vendor: Azul Systems, Inc., runtime: C:\Program Files\Zulu\zulu-8\jre
Default locale: ja_JP, platform encoding: MS932
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

Visual Studio Code

統合開発エディターとしてVisual Studio Code(以下VSCode)をインストールします。

VSCode をインストールするには コマンドプロンプトを管理者で開き次のように実行します。

インストール

cinst vscode -y

もしくは以下ページからダウンロードしてGUIでインストールすることも可能です。

https://azure.microsoft.com/ja-jp/products/visual-studio-code/

確認

Java Extension Pack

VSCode > Extensions からJava Extensions Packを検索し、以下をインストールします。

ここまでで最低限の開発環境は構築できました。次はMavenによりAzure Functionsプロジェクトを作成します。

プロジェクト作成

Azure Functionsプロジェクトを作成するには、コマンドプロンプトで以下のように実行し、途中ウィザードによって必要情報を入力して作成します。

> mvn archetype:generate -DarchetypeGroupId=com.microsoft.azure -DarchetypeArtifactId=azure-functions-archetype -DjavaVersion=8

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< org.apache.maven:standalone-pom >-------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]

~~~ 省略 ~~~

[INFO] Project created from Archetype in dir: c:\TEST\api
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  01:58 min
[INFO] Finished at: 2021-04-26T13:17:06+09:00
[INFO] ------------------------------------------------------------------------

入力項目

groupId test-api
artifactId api
version 1
package com.test

サンプルプログラム

プロジェクトの作成が完了すると、以下のようなサンプルプログラムが一つ生成されます。

package com.test;
import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.HttpMethod;
import com.microsoft.azure.functions.HttpRequestMessage;
import com.microsoft.azure.functions.HttpResponseMessage;
import com.microsoft.azure.functions.HttpStatus;
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.HttpTrigger;
import java.util.Optional;

/**
 * Azure Functions with HTTP Trigger.
 */
public class Function {
    /**
     * This function listens at endpoint "/api/HttpExample". Two ways to invoke it using "curl" command in bash:
     * 1. curl -d "HTTP Body" {your host}/api/HttpExample
     * 2. curl "{your host}/api/HttpExample?name=HTTP%20Query"
     */
    @FunctionName("HttpExample")
    public HttpResponseMessage run(
            @HttpTrigger(
                name = "req",
                methods = {HttpMethod.GET, HttpMethod.POST},
                authLevel = AuthorizationLevel.ANONYMOUS)
                HttpRequestMessage<Optional<String>> request,
            final ExecutionContext context) {
        context.getLogger().info("Java HTTP trigger processed a request.");
        // Parse query parameter
        final String query = request.getQueryParameters().get("name");
        final String name = request.getBody().orElse(query);
        if (name == null) {
            return request.createResponseBuilder(HttpStatus.BAD_REQUEST).body("Please pass a name on the query string or in the request body").build();
        } else {
            return request.createResponseBuilder(HttpStatus.OK).body("Hello, " + name).build();
        }
    }
}

ビルドと実行

API起動

Azure Functionをローカルで起動するには任意のターミナルで次のように実行します。

mvn clean package azure-functions:run

初回実行時はライブラリのダウンロード等で時間がかかります。最終的に以下のようにAPIのエンドポイントが表示されれば起動完了です。

※このコンソールは戻ってきません。API起動中はAPIのログがコンソールに出力されます。

API動作確認

curl コマンド等で以下のように確認できます。

$ curl -i -X GET \
>  'http://localhost:7071/api/HttpExample?name=TEST'
HTTP/1.1 200 OK
Date: Mon, 26 Apr 2021 06:40:40 GMT
Content-Type: text/plain; charset=utf-8
Server: Kestrel
Transfer-Encoding: chunked

Hello, TEST

JSONを返すAPI

テキストを返すだけでは使い勝手が良くないのでJSONを返すAPIを作ってみます。

JSONを返すにはrunメソッドを次のようにして、APIを起動します。

context.getLogger().info("Java HTTP trigger processed a request.");

// Parse query parameter
final String query = request.getQueryParameters().get("name");
final String name = request.getBody().orElse(query);

int status;
String message;
if (name == null) {
    status = 400;
    message = "what's your name?";            
} else {
    status = 200;
    message = "Hello, " + name;            
}

Map<String, String> response = new HashMap<>();
response.put("message", message);

HttpResponseMessage.Builder builder = request
    .createResponseBuilder(HttpStatus.valueOf(status))
    .header("Content-Type", "application/json");

builder.body(response);
return builder.build();

実行結果

OKケース

$ curl -i -X GET \
<  'http://localhost:7071/api/HttpExample?name=TEST'
HTTP/1.1 200 OK
Date: Mon, 26 Apr 2021 07:47:04 GMT
Content-Type: application/json; charset=utf-8
Server: Kestrel
Transfer-Encoding: chunked
{
  "message": "Hello, TEST"
}

NGケース

$ curl -i -X GET  'http://localhost:7071/api/HttpExample'
HTTP/1.1 400 Bad Request
Date: Mon, 26 Apr 2021 07:47:38 GMT
Content-Type: application/json; charset=utf-8
Server: Kestrel
Transfer-Encoding: chunked
{
  "message": "what's your name?"
}

備考

Unitテストでエラーになる場合

ひとまずAPIの実行だけ確認したい場合は、maven.text.skipオプションによってテストコードをスキップできます。

mvn clean package azure-functions:run -Dmaven.test.skip=true

ビルド時にClassCastException(AppClassLoader → URLClassLoader)が発生する

(前後省略)
Executed 'Functions.attributes' (Failed, Id=a6aa8f59-5fc0-4bba-9ce8-4633ddb1c803)
System.Private.CoreLib: Exception while executing function: Functions.attributes. System.Private.CoreLib: Result: Failure
Exception: ClassCastException: class jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to class java.net.URLClassLoader

これはOracleのJDKを使用しているためだと思われます。Azure Function はZulu製のOpenJDK8系でないと正しく動作しませんでした。(以下はNG)

# java --version
openjdk 11.0.6 2020-01-14
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.6+10)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.6+10, mixed mode)

コメント

このブログの人気の投稿

docker-compose up で proxyconnect tcp: dial tcp: lookup proxy.example.com: no such host

docker-compose で起動したweb、MySQLに接続できない事象

【PHP】PHP_CodeSnifferを使う(コーディングルールのカスタマイズ)