7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

javaでAzure functions

Last updated at Posted at 2018-06-10

#概要
azure functionsで、正式にjavaがサポートされたらしいので、ちょっとやってみます。
基本的には、下記の流れ通りでできましたが、途中何回かつまずいたので、つまずき内容を含めメモしておきます。
Java と Maven を使用して初めての関数を作成する (プレビュー)

#環境

  • windows10
  • eclipse

#まずはいろいろインストール
##.NET CORE
↓からインストールします。
https://www.microsoft.com/net/learn/get-started/windows
##node.js
###インストーラ
『Windows Installer (.msi)』を選ぶ。
https://nodejs.org/en/download/
###確認
インストールしたら、既にpathが通っている。
コマンドプロンプトで確認しよう。

> node -v
v8.9.4

##Azure Functions Core Tools
Azure functionsをローカルで実行したり、デバッグしたりできるツールです。
node.jsをインストールしたので、npmを使ってインストールします。

>npm install -g azure-functions-core-tools@core
C:\Users\xxx\AppData\Roaming\npm\func -> C:\Users\xxx\AppData\Roaming\npm\node_modules\azure-functions-core-tools\lib\main.js
C:\Users\xxx\AppData\Roaming\npm\azurefunctions -> C:\Users\xxx\AppData\Roaming\npm\node_modules\azure-functions-core-tools\lib\main.js
C:\Users\xxx\AppData\Roaming\npm\azfun -> C:\Users\xxx\AppData\Roaming\npm\node_modules\azure-functions-core-tools\lib\main.js

azure-functions-core-tools@2.0.1-beta.22 postinstall C:\Users\xxx\AppData\Roaming\npm\node_modules\azure-functions-core-tools
node lib/install.js

[==================] Downloading Azure Functions Cli
+ azure-functions-core-tools@2.0.1-beta.22
added 46 packages in 8.164s

##Azure CLI
名前の通り、Azureをコマンドラインで色々できるツールです。
下記を参考にインストール。
https://docs.microsoft.com/ja-jp/cli/azure/install-azure-cli-windows
#プロジェクトを作成
コマンドプロンプトでeclipseのworkspaceに移り、下記コマンドを実行。
GUIで新規プロジェクトを作ってもいいと思います。

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

groupId等を色々聞いてくるので、好きな名前をつけよう。

Define value for property 'groupId': jp.suzuq
Define value for property 'artifactId': azure.functions
Define value for property 'version' 1.0-SNAPSHOT: : 0.0.1
Define value for property 'package' jp.suzuq: : jp.suzuq.azure.functions
Define value for property 'appName' azure.functions-20180222185249620: :
Define value for property 'appRegion' westus: : jp

#さっそくビルド
プロジェクトができた段階で、簡単なfunctionクラスが作成されています。
まずはこれをビルド&デプロイして動作確認してみましょう。

mvn clean package

そのままやったら、下記のエラーでビルドが失敗した。

Failed to execute goal com.microsoft.azure:azure-functions-maven-plugin:0.1.10:package (package-functions) on project azure.functions: Execution package-functions of goal com.microsoft.azure:azure-functions-maven-plugin:0.1.10:package failed: A required class was missing while executing com.microsoft.azure:azure-functions-maven-plugin:0.1.10: package: com/microsoft/azure/serverless/functions/annotation/DocumentDBInput

azure-functions-maven-pluginが何かマズいみたいなので、バージョンを最新の0.2.1に変えてみたらうまくいきました。

pom.xml
                <plugin>
                    <groupId>com.microsoft.azure</groupId>
                    <artifactId>azure-functions-maven-plugin</artifactId>
                    <version>0.2.1</version>
                </plugin>

#ローカルでテスト実行
Azureにデプロイする前に、ローカルで動かしてみます。

mvn azure-functions:run

コンソールに、functionsのロゴが表示されて…

[INFO] Starting running Azure Functions...

                  %%%%%%
                 %%%%%%
            @   %%%%%%    @
          @@   %%%%%%      @@
       @@@    %%%%%%%%%%%    @@@
     @@      %%%%%%%%%%        @@
       @@         %%%%       @@
         @@      %%%       @@
           @@    %%      @@
                %%
                %

下記の表示が出たら準備完了。

Http Functions:

	hello: http://localhost:7071/api/hello

指示された通り、curlで下記を実行してみます。
curlがなければ、何かhttpを投げられるツールを使ってください。

curl -w '\n' -d hoge http://localhost:7071/api/hello

コンソールにログが出力され、実行されている事がわかります。

[2018/05/02 9:51:28] Function started (Id=0aed5288-e5ff-47e2-868b-8455d7731b70)
[2018/05/02 9:51:28] Executing 'Functions.hello' (Reason='This function was programmatically called via the host APIs.', Id=0aed5288-e5ff-47e2-868b-8455d7731b70)
[2018/05/02 9:51:28] Java HTTP trigger processed a request.
[2018/05/02 9:51:28] Function "hello" (ID: 40832889-9057-4664-885e-c84407660a4a) invoked by Java Worker
[2018/05/02 9:51:28] Function completed (Success, Id=0aed5288-e5ff-47e2-868b-8455d7731b70, Duration=5ms)
[2018/05/02 9:51:28] Executed 'Functions.hello' (Succeeded, Id=0aed5288-e5ff-47e2-868b-8455d7731b70)

200番のレスポンスとともに、実行結果が返されました。

Hello, hoge'

#デプロイ
いよいよデプロイしてみます。

##azureにログイン
azure CLIを使ってログインしておきます。
PCでpowershellを開いて「az login」。

> az login
To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code XXXXXXXXX to authenticate.

指示された通り、https://microsoft.com/devicelogin へ行ってpowershellのコンソールに表示されたコードを入力。
「az login」コマンドが終了して、ログインしたazureの情報が示されます。

[
  {
    "cloudName": "AzureCloud",
    "id": "xxxxxxxxxxxxxxxxxxxxxxx",
    "isDefault": true,
    "name": "Microsoft Azure",
    "state": "Enabled",
    "tenantId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "user": {
      "name": "xxxx@xxxxxxxxxxxxxxxxxxxxxx",
      "type": "user"
    }
  }
]

##mavenでデプロイ
eclipseに戻って、下記mavenコマンドを実行します。

mvn azure-functions:deploy

なんかエラー出た…。

[ERROR] Failed to execute goal com.microsoft.azure:azure-functions-maven-plugin:0.2.1:deploy (default-cli) on project azure.functions: Status code 400, {"error":{"code":"LocationNotAvailableForResourceGroup","message":"The provided location 'jp' is not available for resource group. List of available regions is 'centralus,eastasia,southeastasia,eastus,eastus2,westus,westus2,northcentralus,southcentralus,westcentralus,northeurope,westeurope,japaneast,japanwest,brazilsouth,australiasoutheast,australiaeast,westindia,southindia,centralindia,canadacentral,canadaeast,uksouth,ukwest,koreacentral,koreasouth,francecentral'."}}

regionがどうこう言ってるので、pom.xmlのregionっぽい設定を「jp」から「japaneast」に変えてみる。

pom.xml
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <functionAppName>azure.functions-9999999.azurewebsites.net</functionAppName>
        <functionAppRegion>japaneast</functionAppRegion>
    </properties>

今度は違うエラーが。

[ERROR] Failed to execute goal com.microsoft.azure:azure-functions-maven-plugin:0.2.1:deploy (default-cli) on project azure.functions: The host name azure.functions-20180222185249620.azurewebsites.net is invalid. OnError while emitting onNext value: retrofit2.Response.class

function名を変えてみる。
function名は、azureにデプロイされるjarのファイル名にもなるので、ここでもう一度ビルドしておく。

pom.xml
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <functionAppName>testFunction</functionAppName>
        <functionAppRegion>japaneast</functionAppRegion>
    </properties>

またエラー。

[ERROR] Failed to execute goal com.microsoft.azure:azure-functions-maven-plugin:0.2.1:deploy (default-cli) on project azure.functions: {
[ERROR] "message": "An error has occurred.",
[ERROR] "exceptionMessage": "An error occurred when trying to create a controller of type 'DeploymentController'. Make sure that the controller has a parameterless public constructor.",
[ERROR] "exceptionType": "System.InvalidOperationException",
[ERROR] "stackTrace": "   at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)\r\n   at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)\r\n   at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()",
[ERROR] "innerException": {
[ERROR] "message": "An error has occurred.",
[ERROR] "exceptionMessage": "The user name or password is incorrect.\r\n",
[ERROR] "exceptionType": "System.IO.IOException",
[ERROR] "stackTrace": "   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)\r\n   at System.IO.Directory.InternalCreateDirectory(String fullPath, String path, Object dirSecurityObj, Boolean checkHost)\r\n   at System.IO.Directory.InternalCreateDirectoryHelper(String path, Boolean checkHost)\r\n   at System.IO.Directory.CreateDirectory(String path)\r\n   at System.IO.Abstractions.DirectoryWrapper.CreateDirectory(String path)\r\n   at Microsoft.Web.Deployment.WebApi.PathHelper.GetLogFilesFolderPath(IEnvironment environment)\r\n   at Microsoft.Web.Deployment.WebApi.DeploymentController..ctor()\r\n   at lambda_method(Closure )\r\n   at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)"
[ERROR] }
[ERROR] }: OnError while emitting onNext value: retrofit2.Response.class

ちょっと何言ってるかわかんないので、そのままもう一度やってみたら、なぜかSUCCESS。

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 35.832 s
[INFO] Finished at: 2018-05-10T18:55:38+09:00
[INFO] Final Memory: 48M/551M
[INFO] ------------------------------------------------------------------------

azure portalでデプロイしたfunctionを確認しよう。
サイドメニューの「Function App」から。

azurePortal.png

#実行してみる
↑の画像の右上「関数のURLの取得」からURLをコピーできる。
コピーしたURLにcurlからリクエストを投げてみましょう。

curl -w '\n' https://functionName.azurewebsites.net/api/hello -d AzureFunctions
Hello, AzureFunctions

無事動きました!

#オリジナルのfunctionを作る
とりあえずデフォルトで用意されたfunctionは動いたので、オリジナルのやつを作っていきましょう。
さっき実行したのは、workspaceに自動生成されたこのプログラム。
これを参考に、自分の関数を作っていきます。

Function.java
package jp.suzuq.azure.functions;

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

/**
 * Azure Functions with HTTP Trigger.
 */
public class Function {
    /**
     * This function listens at endpoint "/api/hello". Two ways to invoke it using "curl" command in bash:
     * 1. curl -d "HTTP Body" {your host}/api/hello
     * 2. curl {your host}/api/hello?name=HTTP%20Query
     */
    @FunctionName("hello")
    public HttpResponseMessage<String> hello(
            @HttpTrigger(name = "req", methods = {"get", "post"}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
            final ExecutionContext context) {
        context.getLogger().info("Java HTTP trigger processed a request.");

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

        if (name == null) {
            return request.createResponse(400, "Please pass a name on the query string or in the request body");
        } else {
            return request.createResponse(200, "Hello, " + name);
        }
    }
}

##タイマートリガーを作る
デフォルトのは、httpリクエストがトリガーとなって動くfunctionだったので、タイマーがトリガーとなって動くのを作ってみます。

TimerFunction.java
package jp.suzuq.azure.functions;

import com.microsoft.azure.serverless.functions.ExecutionContext;
import com.microsoft.azure.serverless.functions.annotation.FunctionName;
import com.microsoft.azure.serverless.functions.annotation.TimerTrigger;

public class TimerFunction {
    @FunctionName("timer")
    public void timer(
            @TimerTrigger(name = "timer", schedule = "0 */5 * * * *") String timerInfo,
            final ExecutionContext context) 
    {
        context.getLogger().info("timerInfo : " + timerInfo);

    }
}

メソッドの第一引数に@TimerTriggerアノテーションをつけてあげます。
んで、「schedule」にcron形式でスケジュールを記載します。これがfunctionの実行スケジュールになります。

##ライブラリを追加
なんでjavaでAzure functionsを作りたかったかって、javaの色んなライブラリを使いたかったから。
社内mavenリポジトリに、javaの便利クラスがたくさんあって、普段の開発で使っているので、Azure functionsでも使えたらいいな、と思ったのです。

pom.xmlに追加してデプロイしてみます。

pom.xml
		<dependency>
			<groupId>jp.hoge</groupId>
			<artifactId>HogeCommon</artifactId>
			<version>1.20.0</version>
		</dependency>

動かしてみると、なぜかfunctionがタイムアウトになり、途中で強制終了してしまいます。
なぜタイムアウト?と色々悩みましたが、結局のところpomに追加したライブラリが、ビルド済モジュールに含まれていなかったようです。ならClassNotFoundExceptionを出してくれよ…

ビルド済モジュールは、eclipse workspaceのtargetフォルダを見るか、実際にデプロイされたものを見たければAzure portalからKuduでダウンロードできます。

スケッチ.png

スケッチ2.png

ビルドのしかたが悪かったという事で、下記を参考にpom.xmlを修正しました。
How to add dependency JAR in java azure functions

pom.xml
			<plugin>
			   <groupId>org.apache.maven.plugins</groupId>
			   <artifactId>maven-assembly-plugin</artifactId>
			   <configuration>
			      <appendAssemblyId>false</appendAssemblyId>
			      <descriptorRefs>
			         <descriptorRef>jar-with-dependencies</descriptorRef>
			      </descriptorRefs>
			      <archive />
			   </configuration>
			   <executions>
			      <execution>
			         <id>make-assembly</id>
			         <phase>package</phase>
			         <goals>
			            <goal>single</goal>
			         </goals>
			      </execution>
			   </executions>
			</plugin>

わ~い!動いた!
1528452015300.png

まだまだ色々試してみたいですが、とりあえずここまで。

#感想
社内ライブラリをAzure Functionsで使う事ができて満足。
でもやっぱ色々と準備するのが面倒ですね。
簡単なfunctionなら、やっぱjavascriptで作るのが一番簡単。
javaは今回のように社内の既存資産を使いまわしたり、ローカルでデバッグできるのがいいですね。
用途によって使い分けたいと思います。

#参考になったサイト
Java と Maven を使用して初めての関数を作成する (プレビュー)
javaでトリガーを設定する
Azure CLIインストール&ログイン(Windows版)
How to add dependency JAR in java azure functions

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?