App Service における Java SE stack への Jar デプロイの挙動

# はじめに
以前、[App Service における Tomcat アプリケーションへの war ファイルのデプロイについて](https://qiita.com/georgeOsdDev@github/items/c8184d7b8676f10982d9) と題して Tomcat 構成への war デプロイについて書きましたが、今回は Java SE Stack への Jar ファイルデプロイについて書きます。

https://qiita.com/georgeOsdDev@github/items/c8184d7b8676f10982d9

# TL;DR
- jar ファイルのデプロイには、Kudu publish API `/api/publish?type=jar` を使おう(azure-webapp-maven-plugin や AZ CLIも内部的にはこのAPIを呼び出している)
- Kudu publish API を使うとjar ファイルは、`%HOME%\site\wwwroot\app.jar`(Linux の場合は `$HOME/site/wwwroot/app.jar`) として保存される
- app.jar はアプリケーション起動時に、JavaBootstrapper によってローカルコピーされたのちに、実行されることになる

# 検証環境

OS: Windows
SKU: B1
Java: AutoUpdate
JavaSE: AutoUpdate

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4914/8fb9a29f-8c5b-6336-c64f-fa77b1a548b8.png)


# デプロイしてみる
今回は mvn を利用してみますが、curl 等でも基本的には同じです。

` mvn clean package azure-webapp:deploy`

ローカルのコンソールには以下のようなログが出力されることになります。
```
[INFO] Trying to deploy artifact to toshida-java-se...
[INFO] Deploying (/プロジェクトパス/gs-spring-boot/complete/target/spring-boot-complete-0.0.1-SNAPSHOT.jar)[jar]  ...
[INFO] Successfully deployed the artifact to https://アプリ名.azurewebsites.net
```

この時 App Service (Kudu) 側で何が実行されたかは、Kudu のログからわかります。
成功していれば以下のように、POST_api-publish_200_3s.xml などといったログファイルが存在しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4914/5ad60e6b-9c0e-f8a5-f1da-3a0797f91de0.png)

このファイルの中身をみると
1行目には受信リクエストの詳細が記載されています `POST /api/publish?type=jar` が呼び出されていることなどがわかります。
また、2行目移行にはリクエストを受け取って Kudu が実行した処理のログが残されています。
概ね以下のような処理となっています。受信リクエストからjarファイルを `D:\local\Temp\zipdeploy\extracted\app.jar` として保存、デプロイ処理として、`D:\local\Temp\zipdeploy\extracted\app.jar`　を home\site\wwwrootに配置、アプリケーションの再起動がトリガーされます。

```POST_api-publish.xml 1行目
実際には改行されていません。記事に不要な項目は削除しています。
<step title="Incoming Request" 
date="2022-12-12T09:48:35.270" 
url="/api/publish?type=jar" 
method="POST" 
type="request" 
pid="4680,2,31" 
Date="Mon, 12 Dec 2022 09:48:34 GMT" 
Content-Length="19734840" 
Content-Type="application/octet-stream" 
Accept="*/*" 
Authorization="Bas..." 
Host="アプリ名.scm.azurewebsites.net" 
User-Agent="azure-webapp-maven-plugin/2.5.0 installationId:f99d07082807b507a4734481c82a4a2cbfcd1f8f477050ca681bb818ca5df41c sessionId:ca87405e-5628-4021-ab02-ce50e9f02038 azsdk-java-com.azure.resourcemanager.resources.fluentcore.policy/2.10 (11.0.10; Linux; 5.10.102.1-microsoft-standard-WSL2)" 
x-ms-return-client-request-id="true" 
```

ここまでは概ね、[App Service における Tomcat アプリケーションへの war ファイルのデプロイについて]で記載した、`/api/publish?type=war` の動作とほぼ同様です。保存ファイル名が app.war か app.jar で違う程度です。
以下のように wwwroot 配下には app.jar が保存されることになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4914/a9312118-b372-b9cc-b370-56488a0abe98.png)

ただし、?type=war の場合と違ってパスを指定してデプロイすることはできず、保存先は必ず `app.jar` となります。
> - type=jar: JAR パッケージを /home/site/wwwroot/app.jar にデプロイします。 path パラメーターは無視されます


# アプリケーション起動処理

Tomcat スタックの場合と同様に、Windows 環境では w3wp.exe のプロセスの配下に、JavaBootstrapper が存在することがわかります。
Tomcat スタックとの違って、catalina.bat に該当する cmd が JavaBootstrapper と java の間にはありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4914/d636d83c-32a7-9e22-e9d1-2396440ab42a.png)

今回は catalina.log がないので `jcmd` で java プロセスの詳細を見てみます。
Tomcat の場合に、war ファイルがコピーされていたローカルパスと同じような、`C:/DWASFiles/Sites/アプリ名/Temp/javaFiles<UUIDっぽい値>\site\wwwroot` 配下の app.jar をクラスパスに追加していることがわかります。そのほかに `azure.appservice.jar` というjar も追加されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4914/5a2c3d85-cb52-cd25-e4a2-19a7843578d5.png)


## スタートアップスクリプトによる検証

Tomcat スタックでの確認と同様に、`startup.cmd` を使って JavaBootstrapper によって、実行直前にどのような状態になっているのかを見てみます。

```bat:startup.cmd
set CURRENT_DATE=%date:~10,4%%date:~4,2%%date:~7,2%
set logfile=%HOME%\LogFiles\startup_%CURRENT_DATE%.log

echo "==== startup.cmd start ===" >> "%logfile%" 2>&1
date /t >> "%logfile%" 2>&1
time /t >> "%logfile%" 2>&1

 
echo "AZURE_JAVA_APP_PATH  = %AZURE_JAVA_APP_PATH%">> "%logfile%" 2>&1
echo "AZURE_SITE_HOME = %AZURE_SITE_HOME%">> "%logfile%" 2>&1

:: 以下の2行は%AZURE_JAVA_APP_PATH%の最後が \app.jar で終わることを知った上で足しています
echo %AZURE_JAVA_APP_PATH:~,-8%
dir %AZURE_JAVA_APP_PATH:~,-8% >> "%logfile%" 2>&1
```

### 実行結果

```startup_.log
Mon 12/12/2022 
10:50 AM
"AZURE_JAVA_APP_PATH  = C:\DWASFiles\Sites\<アプリ名>\Temp\javaFiles\8edff953-2dab-481e-b53e-3dee4422fe02\site\wwwroot\app.jar"
"AZURE_SITE_HOME = "
 Volume in drive C is Temporary Storage
 Volume Serial Number is AA7C-C278

 Directory of C:\DWASFiles\Sites\<アプリ名>\Temp\javaFiles\8edff953-2dab-481e-b53e-3dee4422fe02\site\wwwroot

12/12/2022  10:50 AM    <DIR>          .
12/12/2022  10:50 AM    <DIR>          ..
12/12/2022  09:48 AM        19,734,840 app.jar
               1 File(s)     19,734,840 bytes
               2 Dir(s)  11,674,025,984 bytes free
```

Tomcat スタックの場合は、`AZURE_SITE_HOME` がありましたが、今回は `AZURE_JAVA_APP_PATH` として、ローカル領域の `app.jar` が指定されていることがわかります。
そして、再起動を繰り返す度に `javaFiles`以下の例えば `8edff953-2dab-481e-b53e-3dee4422fe02` の値が変わることがわかります。

# app.jar 以外を使うには

`WEBSITE_JAVA_JAR_FILE_NAME` というデフォルト値が `app.jar` の設定があります。

https://learn.microsoft.com/ja-jp/azure/app-service/reference-app-settings?tabs=kudu%2Cjava
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4914/925a5013-8df5-52ea-3932-8e6c36d47a5c.png)

ソースを更新してパッケージしたjarファイルをFTPで `%HOME%\site\wwwroot\version2.jar` として配置し、`WEBSITE_JAVA_JAR_FILE_NAME` に `version2.jar` を設定してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4914/f3f4c8c4-dd33-e2b1-f851-9da536831d3f.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4914/8c4c278e-9866-d9d5-eb9d-a91d5c40127d.png)

startup.cmd で確認すると
`AZURE_JAVA_APP_PATH` が `C:\DWASFiles\Sites\<アプリ名>\Temp\javaFiles\fa1dbffc-425f-4389-8b18-f765b5b3c57f\site\wwwroot\version2.jar` のような値になっていることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4914/189bbfb6-32b7-5b34-ab85-6387323daf10.png)

Tomcat スタックの場合は、war ファイルを展開して Tomcat が読み込むことでコンテキストパスという概念が存在しそれがアプリケーションアクセス時の URL にも影響してきますが、Java SE スタックの場合は結局のところなんらかの jar ファイルをローカル領域にコピーして実行しているだけとなります。
アプリケーションが jar ファイルの名前を利用してなにか処理しているなど、 `app.jar` というファイル名を使いたくないというユースケースはあまり思い浮かびませんが、`AZURE_JAVA_APP_PATH` を利用することで `app.jar` 以外を利用することも可能です。
ただしその場合は、az cli, maven plugin をはじめ、/api/publish?type=jar に準拠するデプロイ方法は以外でファイルを配置する必要があります。

# 参考リンク

https://learn.microsoft.com/ja-jp/azure/app-service/configure-language-java?pivots=platform-windows#java-se

https://learn.microsoft.com/ja-jp/azure/app-service/deploy-zip?tabs=api#kudu-publish-api-reference