Grooy スクリプトをそのまま渡して実行する Spring Boot+Picocli ベースのコマンドラインアプリを作成する ( その10 )( GraalVM で groovy-script-executor の Windows 版 Native Image を作成する )
概要
記事一覧はこちらです。
- 今回の手順で確認できるのは以下の内容です。
- GraalVM で groovy-script-executor の Windows 版 Native Image を作成します。
参照したサイト・書籍
Using GraalVM and Native Image on Windows 10
https://medium.com/graalvm/using-graalvm-and-native-image-on-windows-10-9954dc071311Build Great Native CLI Apps in Java with Graalvm and Picocli
https://www.infoq.com/articles/java-native-cli-graalvm-picocli/2.3.2. Enabling the Annotation Processor
https://picocli.info/#_enabling_the_annotation_processorVisual Studio - ダウンロード
https://visualstudio.microsoft.com/ja/downloads/GraalVM
https://www.graalvm.org/
目次
- build.gradle に
annotationProcessor("info.picocli:picocli-codegen:${picocliVersion}")
を追加する - Visual Studio 2022 Community をインストールする
- GraalVM Community 21.3.0 をインストールする
- groovy-script-executor の Windows 版 Native Image(gse.exe、gse-servlet.exe)を作成する
- 動作確認
手順
build.gradle に annotationProcessor("info.picocli:picocli-codegen:${picocliVersion}")
を追加する
https://picocli.info/#_enabling_the_annotation_processor を参考に build.gradle を以下のように変更します。
.......... [compileJava, compileTestGroovy, compileTestJava]*.options*.encoding = "UTF-8" [compileJava, compileTestGroovy, compileTestJava]*.options*.compilerArgs = [ "-Xlint:all,-options,-processing,-path", "-Aproject=${project.group}/${project.name}" ] .......... dependencies { .......... def picocliVersion = "4.6.2" .......... // for Picocli implementation("info.picocli:picocli-spring-boot-starter:${picocliVersion}") annotationProcessor("info.picocli:picocli-codegen:${picocliVersion}") ..........
[compileJava, compileTestGroovy, compileTestJava]*.options*.compilerArgs
に"-Aproject=${project.group}/${project.name}"
を追加します。- dependencies block に以下の行を追加します。
annotationProcessor("info.picocli:picocli-codegen:${picocliVersion}")
Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
clean タスク実行 → Rebuild Project 実行 → build タスクを実行して生成された build/libs/groovy-script-executor-1.0.0-RELEASE.jar を D:\tmp にコピーします。
Visual Studio 2022 Community をインストールする
Windows 版 Native Image を作成するのに Visual Studio 2022 Community が必要なのでインストールします。
ダウンロード ページの「コミュニティ」の「無料ダウンロード」ボタンをクリックして vs_community__~.exe(~ の部分には英数字の文字列が入ります)をダウンロードします。
vs_community__~.exe を実行してインストールします。以下の画面が表示されたら「個別のコンポーネント」の「MSVC v143 VS 2022 C++ x64/x86 ビルドツール(最新)」をチェックした後「インストール」ボタンをクリックします。
インストールが完了すると「Visual Studio Installer」に「Visual Studio Community 2022」が表示されます。
GraalVM Community 21.3.0 をインストールする
https://www.graalvm.org/downloads/ の「GraalVM Community 21.3.0」の「DOWNLOAD FROM GITHUB」ボタンをクリックすると https://github.com/graalvm/graalvm-ce-builds/releases/tag/vm-21.3.0 へ遷移するので、その下の「Java 17 based」の「Windows (amd64)」の link
をクリックして graalvm-ce-java17-windows-amd64-21.3.0.zip をダウンロードします。
D:\graalvm フォルダを新規作成した後、graalvm-ce-java17-windows-amd64-21.3.0.zip を解凍して作成された graalvm-ce-java17-21.3.0 フォルダをその下に移動します。
groovy-script-executor の Windows 版 Native Image(gse.exe、gse-servlet.exe)を作成する
Windows メニューから「Visual Studio 2022」-「x64 Native Tools Command Prompt for VS 2022」をクリックして実行します。
以下のコマンドを実行し native-image コマンドを使用できるようにします。
> cd /d d:\tmp > set PATH=D:\graalvm\graalvm-ce-java17-21.3.0\bin;%PATH% > gu install native-image
以下のコマンドを実行し gse.exe を作成します。gse.exe の後に指定しているのは gse.bat 内に記述していた起動時オプションです。ただし -XX:TieredStopAtLevel=1
は指定できませんでした(エラーメッセージが出力されて Native Image の生成が失敗します)。
> native-image -jar groovy-script-executor-1.0.0-RELEASE.jar gse.exe -Dfile.encoding=UTF-8 -Dspring.main.lazy-initialization=true
以下のコマンドを実行し gse-servlet.exe を作成します。gse-servlet.exe の後に指定しているのは gse-servlet.bat 内に記述していた起動時オプションです。
> native-image -jar groovy-script-executor-1.0.0-RELEASE.jar gse-servlet.exe -Dfile.encoding=UTF-8 -Dspring.main.lazy-initialization=true -Dspring.main.web-application-type=servlet -Dlogging.level.root=INFO
動作確認
まずは gse.exe の動作確認を行います。
gse.exe Helloworld.groovy
を実行すると Hello, World
の文字が出力されました。起動後に4~5秒かかるのは Groovy スクリプトを build している時間なので Native Image にしても変わりませんでした。
docker-compose up -d
でコンテナを起動して publications.csv を削除してから gse.exe PublicationsTableToFileUsingUnivocityParsers.groovy
を実行するとコマンドはエラーメッセージを出力せずに終了し、
publications.csv が生成されてデータが出力されていました。
gse.exe SftpClient.groovy --user=user01 --password=pass01 --upload-dir=upload --upload-file=publications.csv
コマンドを実行するとログが出力されて、
upload ディレクトリに publications.csv がアップロードされました。
次に gse-servlet.exe の動作確認を行います。
gse.exe StubServer.groovy
を実行すると何も出力されずにコマンドが終了しますが、
gse-servlet.exe StubServer.groovy
を実行するとログが出力されて Tomcat が起動し、
curl -v http://localhost:9080/stub
を実行すると {"key":123,"data":"xxxxxxxx"}
が返ってきました。
gse.exe、gse-servlet.exe どちらも問題なさそうです。
また環境変数 PATH から JDK(D:\java\jdk-17.0.1.12-hotspot\bin
)への PATH を削除してから gse.exe Helloworld.groovy
を実行してもコマンドが実行されました。作成された Native Image は JDK がなくても動作します(Groovy スクリプトの build にも JDK は必要ないようです)。
履歴
2022/01/01
初版発行。
Grooy スクリプトをそのまま渡して実行する Spring Boot+Picocli ベースのコマンドラインアプリを作成する ( その9 )( Gradle を 7.2 → 7.3.3 へ、Spring Boot を 2.5.6 → 2.6.2 へバージョンアップする )
概要
記事一覧はこちらです。
- 今回の手順で確認できるのは以下の内容です。
- Gradle を 7.2 → 7.3.3 へ、Spring Boot を 2.5.6 → 2.6.2 へバージョンアップします。
参照したサイト・書籍
目次
手順
Gradle を 7.2 → 7.3.3 へバージョンアップする
コマンドプロンプトから gradlew wrapper --gradle-version=7.3.3
、gradlew --version
コマンドを実行します。
gradle/wrapper/gradle-wrapper.properties は以下の内容になります。
distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists
Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
Spring Boot を 2.5.6 → 2.6.2 へバージョンアップする
build.gradle の以下の点を変更します。
.......... plugins { id 'java' id 'groovy' id 'org.springframework.boot' version '2.6.2' id 'io.spring.dependency-management' version '1.0.11.RELEASE' } .......... dependencyManagement { imports { // bomProperty に指定可能な property は以下の URL の BOM に記述がある // https://repo1.maven.org/maven2/org/springframework/boot/spring-boot-dependencies/2.5.6/spring-boot-dependencies-2.5.6.pom mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES) { // Spring Boot の BOM に定義されているバージョンから変更する場合には、ここに以下のように記述する // bomProperty "thymeleaf.version", "3.0.9.RELEASE" } mavenBom("org.junit:junit-bom:5.8.2") } } dependencies { .......... def picocliVersion = "4.6.2" .......... // dependency-management-plugin によりバージョン番号が自動で設定されないもの、あるいは最新バージョンを指定したいもの .......... implementation("net.logstash.logback:logstash-logback-encoder:7.0.1") ..........
- plugins block の以下の点を変更します。
id 'org.springframework.boot' version '2.5.6'
→id 'org.springframework.boot' version '2.6.2'
各種ライブラリのバージョンアップとして以下の点を変更します。
- dependencyManagement block の以下の点を変更します。
mavenBom("org.junit:junit-bom:5.8.1")
→mavenBom("org.junit:junit-bom:5.8.2")
- dependencies block の以下の点を変更します。
def picocliVersion = "4.6.1"
→def picocliVersion = "4.6.2"
implementation("net.logstash.logback:logstash-logback-encoder:6.6")
→implementation("net.logstash.logback:logstash-logback-encoder:7.0.1")
Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
clean タスク実行 → Rebuild Project 実行 → build タスクを実行して、"BUILD SUCCESSFUL" のメッセージが出力されることを確認します。
履歴
2021/12/29
初版発行。
Eclipse Temurin を 17+35 → 17.0.1+12 へ、IntelliJ IDEA を 2021.2.3 → 2021.2.4 へ、Git for Windows を 2.33.1 → 2.34.1 へバージョンアップ
Eclipse Temurin を 17+35 → 17.0.1+12 へバージョンアップする
※ksbysample-webapp-lending プロジェクトを開いた状態でバージョンアップしています。
https://adoptium.net/index.html?variant=openjdk17&jvmVariant=hotspot から OpenJDK17U-jdk_x64_windows_hotspot_17.0.1_12.msi をダウンロードします。
インストール時に削除されるかもしれないので D:\Java\jdk-17.0.0.35-hotspot → D:\Java\jdk-17.0.0.35-hotspotx にリネームします。
インストーラーを実行して D:\Java\jdk-17.0.1.12-hotspot へインストールした後、環境変数 JAVA_HOME のパスを D:\Java\jdk-17.0.1.12-hotspot へ変更します。
コマンドプロンプトから
java -version
を実行し、17.0.1
に変更されていることを確認します。D:\Java\jdk-17.0.0.35-hotspotx → D:\Java\jdk-17.0.0.35-hotspot に戻します。
IntelliJ IDEA を再起動します。
ksbysample-webapp-lending プロジェクトが使用する JDK を 17.0.1.12 に変更します。
IntelliJ IDEA のメイン画面が開いたら、メニューから「File」-「Project Structure...」を選択します。
「Project Structure」ダイアログが表示されます。「Project SDK」で D:\Java\jdk-17.0.1.12-hotspot を選択します。
「Project SDK」の「Edit」ボタンをクリックします。
「Project Structure」ダイアログが表示されます。画面左側で「Platform Settings」-「SDKs」を選択して、中央のリストから「17+35」を選択した後、リストの上の「-」ボタンをクリックして削除します。
中央のリストから「11」を選択した後、"11" → "17.0.1.12" へ変更します。
「OK」ボタンをクリックして「Project Structure for New Projects」ダイアログを閉じます。
メイン画面に戻ると画面右下に「Indexing...」の表示が出るので、終了するまで待ちます。
Gradle Tool Window の左上にある「Refresh All Gradle Projects」ボタンをクリックして更新します。
clean タスク実行 → Rebuild Project 実行 → build タスクを実行して、"BUILD SUCCESSFUL" のメッセージが出力されることを確認します。
Project Tool Window で src/test でコンテキストメニューを表示して「More Run/Debug」-「Run 'All Tests' with Coverage」を選択し、テストが全て成功することを確認します。
特に問題は発生しませんでした。Eclipse Temurin 17.0.1+12 で開発を進めます。
IntelliJ IDEA を 2021.2.3 → 2021.2.4 へバージョンアップする
IntelliJ IDEA の 2021.2.4 がリリースされているのでバージョンアップします。
- IntelliJ IDEA 2021.2.4 Is Out
https://blog.jetbrains.com/idea/2021/12/intellij-idea-2021-2-4-is-out/
※ksbysample-webapp-lending プロジェクトを開いた状態でバージョンアップしています。
IntelliJ IDEA のメインメニューから「Help」-「Check for Updates...」を選択します。
「IntelliJ IDEA and Plugin Updates」ダイアログが表示されます。右下に「Update and Restart」ボタンが表示されていますので、「Update and Restart」ボタンをクリックします。
Plugin の update も表示されました。このまま「Update」ボタンをクリックします。
Patch がダウンロードされて IntelliJ IDEA が再起動します。
IntelliJ IDEA が起動すると画面下部に「Indexing」のメッセージが表示されますので、終了するまで待機します。
IntelliJ IDEA のメインメニューから「Help」-「About」を選択し、2021.2.4 へバージョンアップされていることを確認します。
clean タスク実行 → Rebuild Project 実行 → build タスクを実行して、"BUILD SUCCESSFUL" のメッセージが出力されることを確認します。
Project Tool Window で src/test でコンテキストメニューを表示して「More Run/Debug」-「Run 'All Tests' with Coverage」を選択し、テストが全て成功することを確認します。
Git for Windows を 2.33.1 → 2.34.1 へバージョンアップする
Git for Windows の 2.34.1 がリリースされていたのでバージョンアップします。
https://gitforwindows.org/ の「Download」ボタンをクリックして Git-2.34.1-64-bit.exe をダウンロードします。
Git-2.34.1-64-bit.exe を実行します。
「Git 2.34.1 Setup」ダイアログが表示されます。インストーラーの画面を一通り見たいので「Only show new options」のチェックを外してから [Next >] ボタンをクリックします。
「Select Components」画面が表示されます。「Git LFS(Large File Support)」だけチェックした状態で [Next >]ボタンをクリックします。
「Choosing the default editor used by Git」画面が表示されます。「Use Vim (the ubiquitous text editor) as Git's default editor」が選択された状態で [Next >]ボタンをクリックします。
「Adjusting the name of the initial branch in new repositories」画面が表示されます。「Let Git decide」が選択されていることを確認後、[Next >]ボタンをクリックします。
「Adjusting your PATH environment」画面が表示されます。中央の「Git from the command line and also from 3rd-party software」が選択されていることを確認後、[Next >]ボタンをクリックします。
「Choosing the SSH executable」画面が表示されます。「Use bundled OpenSSL」が選択されていることを確認後、[Next >]ボタンをクリックします。
「Choosing HTTPS transport backend」画面が表示されます。「Use the OpenSSL library」が選択されていることを確認後、[Next >]ボタンをクリックします。
「Configuring the line ending conversions」画面が表示されます。一番上の「Checkout Windows-style, commit Unix-style line endings」が選択されていることを確認した後、[Next >]ボタンをクリックします。
「Configuring the terminal emulator to use with Git Bash」画面が表示されます。「Use Windows'default console window」が選択されていることを確認した後、[Next >]ボタンをクリックします。
「Choose the default behavior of
git pull
」画面が表示されます。「Default (fast-forward or merge)」が選択されていることを確認した後、[Next >]ボタンをクリックします。「Choose a credential helper」画面が表示されます。今回は「Git Credential Manager」を選択した後、[Next >]ボタンをクリックします。
「Configuring extra options」画面が表示されます。「Enable file system caching」だけがチェックされていることを確認した後、[Next >]ボタンをクリックします。
「Configuring experimental options」画面が表示されます。何もチェックせずに [Install]ボタンをクリックします。
インストールが完了すると「Completing the Git Setup Wizard」のメッセージが表示された画面が表示されます。中央の「View Release Notes」のチェックを外した後、[Next >]ボタンをクリックしてインストーラーを終了します。
コマンドプロンプトを起動して
git --version
を実行し、git のバージョンがgit version 2.34.1.windows.1
になっていることを確認します。特に問題はないようですので、2.34.1 で作業を進めたいと思います。
Grooy スクリプトをそのまま渡して実行する Spring Boot+Picocli ベースのコマンドラインアプリを作成する ( 番外編 )( ConEmu 上で WSL 2 の Ubuntu-20.04 の bash を起動する+Ubuntu-20.04 の別インスタンスを作成する )
概要
記事一覧はこちらです。
GraalVM で groovy-script-executor の Linux 用の Native Image を作成するために WSL 2 で稼働する Ubuntu-20.04 に容易にアクセスする方法等を調べます。
参照したサイト・書籍
Bash on Ubuntu on Windows (WSL)
https://conemu.github.io/en/wsl.htmlWSL: Am I running version 1 or version 2?
https://askubuntu.com/questions/1177729/wsl-am-i-running-version-1-or-version-2Windows への PowerShell のインストール
https://docs.microsoft.com/ja-jp/powershell/scripting/install/installing-powershell-on-windows?view=powershell-7.2PowerShell / PowerShell - Releases
https://github.com/PowerShell/PowerShell/releases/How to install multiple instances of Ubuntu in WSL2
https://cloudbytes.dev/snippets/how-to-install-multiple-instances-of-ubuntu-in-wsl2WSL の基本的なコマンド
https://docs.microsoft.com/ja-jp/windows/wsl/basic-commandsDeveloping on Amazon Linux 2 using Windows
https://aws.amazon.com/jp/blogs/developer/developing-on-amazon-linux-2-using-windows/yosukes-dev / AmazonWSL - Releases
https://github.com/yosukes-dev/AmazonWSL/releases
目次
- Ubuntu-20.04 を以前インストールはしていたが、WSL 1 or WSL 2 のどちらで動いているのか?
- ConEmu で
{Bash::bash}
を起動しようとするとwslbridge error: failed to start backend process
のエラーが出力されて起動しない問題を解消する - Ubuntu-20.04 の別インスタンスを起動する
手順
Ubuntu-20.04 を以前インストールはしていたが、WSL 1 or WSL 2 のどちらで動いているのか?
WSL: Am I running version 1 or version 2? を読むと wsl -l -v
コマンドで分かるそうなので実行してみると、Ubuntu-20.04 は VERSION 1(WSL 1) になっていました。
wsl --set-version Ubuntu-20.04 2
コマンドを実行して VERSION 2(WSL 2)に変換します。
ConEmu で {Bash::bash}
を起動しようとすると wslbridge error: failed to start backend process
のエラーが出力されて起動しない問題を解消する
WSL 2 に変換後、ConEmu で {Bash::bash}
を起動しようとすると以下のエラーメッセージが表示されて起動できなくなるので、起動できるようにします。
まず ConEmu の左上のアイコンをクリックしてメニューを表示後、「Settings...」を選択します。
「Settings」ダイアログが表示されるので、左側のツリーから「Startup」-「Tasks」を選択した後、中央のリストから {Bash::bash}
を選択し、右側の「Commands」を
set "PATH=%ConEmuBaseDirShort%\wsl;%PATH%" & %ConEmuBaseDirShort%\conemu-cyg-64.exe --wsl -cur_console:pm:/mnt
→wsl.exe -cur_console:pm:/mnt
に変更して「Save settings」ボタンをクリックして保存します。
再度 {Bash::bash}
を起動しようとすると今度は無事起動しました。タブのアイコンもペンギンになっていました。
Ubuntu-20.04 の別インスタンスを起動する
作業するのに PowerShell を使用します。PowerShell のバージョンを確認すると 5.1 だったので 7.2.1 にバージョンアップします。
https://github.com/PowerShell/PowerShell/releases/tag/v7.2.1 から PowerShell-7.2.1-win-x64.msi をダウンロードしてインストールします。
ConEmu の {Shells::PowerShell}
と {Shells::PowerShell (Admin)}
の設定を変更します。
powershell.exe
→pwsh.exe
powershell.exe -new_console:a
→pwsh.exe -new_console:a
D:\WSL\Ubuntu-20.04-2\ ディレクトリを新規作成した後、以下のコマンドを実行して Ubuntu-20.04-2 インスタンスを作成します。
PS> cd D:\WSL\Ubuntu-20.04-2\ PS> Invoke-WebRequest -Uri https://cloud-images.ubuntu.com/releases/hirsute/release/ubuntu-21.04-server-cloudimg-amd64-wsl.rootfs.tar.gz -OutFile $env:TMP\ubuntu-21.04.tar.gz PS> wsl --import Ubuntu-20.04-2 D:\WSL\Ubuntu-20.04-2 $env:TMP\ubuntu-21.04.tar.gz
wsl -l -v
コマンドを実行すると作成した Ubuntu-20.04-2 インスタンスは VERSION 1 になっていたので、wsl --set-version Ubuntu-20.04-2 2
コマンドを実行します。
WSL の基本的なコマンド を見ると wsl --set-default-version
コマンドで既定の WSL バージョンを設定できるそうなので、wsl --set-default-version 2
コマンドを実行して WSL 2 がデフォルトになるように設定します。
wsl -d Ubuntu-20.04-2
コマンドを実行すれば作成した Ubuntu-20.04-2 インスタンスに接続できます。
wsl --unregister Ubuntu-20.04-2
コマンドを実行すると作成したインスタンスが削除されます。
履歴
2021/12/29
初版発行。
Grooy スクリプトをそのまま渡して実行する Spring Boot+Picocli ベースのコマンドラインアプリを作成する ( その8 )( @SpringBootApplication アノテーションを付与した Groovy スクリプトで REST API サーバを作成する )
概要
記事一覧はこちらです。
今回は特別なことは何もしていないのに、妙に難しかったです。。。
参照したサイト・書籍
目次
- 今回実装する Groovy スクリプトの全体図
- build.gradle に記述しているモジュールを spring-boot-starter → spring-boot-starter-web に変更する
- Application.java の main メソッド内の処理を spring.main.web-application-type の値で切り替えるよう変更する
- application.properties に spring.main.web-application-type=none を追加する
- gse-servlet.bat を新規作成する
- JSON データを返す REST API サーバを起動する Groovy スクリプトを作成する
- 動作確認
手順
今回実装する Groovy スクリプトの全体図
- StubServer.groovy という Groovy スクリプトを作成します。
- groovy-script-executor と StubServer.groovy の2つで Tomcat が起動します。ポート番号は同じものが使用できませんので、groovy-script-executor は 8080番ポートを、StubServer.groovy は 9080番ポートを使用します。
build.gradle に記述しているモジュールを spring-boot-starter → spring-boot-starter-web に変更する
groovy-script-executor/build.gradle を以下のように変更します。
dependencies { .......... // dependency-management-plugin によりバージョン番号が自動で設定されるもの // Dependency Versions ( https://docs.spring.io/spring-boot/docs/current/reference/html/dependency-versions.html#dependency-versions ) 参照 implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.integration:spring-integration-sftp") ..........
implementation("org.springframework.boot:spring-boot-starter")
→implementation("org.springframework.boot:spring-boot-starter-web")
に変更します。
Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
Application.java の main メソッド内の処理を spring.main.web-application-type の値で切り替えるよう変更する
groovy-script-executor/src/main/java/ksby/cmdapp/groovyscriptexecutor/Application.java を以下のように変更します。
@SpringBootApplication public class Application implements CommandLineRunner, ExitCodeGenerator { .......... public static void main(String[] args) { String springMainWebApplicationType = System.getProperty("spring.main.web-application-type"); ApplicationContext context = SpringApplication.run(Application.class, args); if (StringUtils.equals(springMainWebApplicationType, "none")) { System.exit(SpringApplication.exit(context)); } } ..........
application.properties に spring.main.web-application-type=none を追加する
デフォルトでは groovy-script-executor で Tomcat が起動しないようにしたいので、groovy-script-executor/src/main/resources/application.properties に spring.main.web-application-type=none
を追加します。
spring.main.banner-mode=off spring.main.web-application-type=none logging.level.root=ERROR logging.level.com.jcraft.jsch=ERROR logging.level.ksby.cmdapp.groovyscriptexecutor.script=INFO logging.level.org.springframework.integration.expression.ExpressionUtils=ERROR
build タスクを実行し、生成した groovy-script-executor.jar を D:\tmp にコピーします。
gse-servlet.bat を新規作成する
groovy-script-executor/src/main/groovy/ksby/cmdapp/groovyscriptexecutor/script の下に gse.bat をコピーして gse-servlet.bat を新規作成し、以下の内容を記述します。
@echo off java -Dfile.encoding=UTF-8 ^ -XX:TieredStopAtLevel=1 ^ -Dspring.main.lazy-initialization=true ^ -Dspring.main.web-application-type=servlet ^ -Dlogging.level.root=INFO ^ -jar groovy-script-executor-1.0.0-RELEASE.jar ^ %*
- gse.bat との違いは以下の2行を追記している点です。
-Dspring.main.web-application-type=servlet ^
-Dlogging.level.root=INFO ^
-Dlogging.level.root=INFO ^
は Tomcat 起動時のログを出力しておいた方が動作が分かりやすいので追加しています。
gse-servlet.bat を D:\tmp にコピーします。
JSON データを返す REST API サーバを起動する Groovy スクリプトを作成する
groovy-script-executor/src/main/groovy/ksby/cmdapp/groovyscriptexecutor/script の下に StubServer.groovy を新規作成し、以下のコードを記述します。
package ksby.cmdapp.groovyscriptexecutor.script import org.springframework.boot.SpringApplication import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController @SpringBootApplication // @RestController アノテーションはここに付ける @RestController @RequestMapping("/stub") class StubServer { static void main(String[] args) { // application.properties で指定した設定は groovy-script-executor に反映されて // Groovy スクリプトには反映されないので、Groovy スクリプトに設定したい項目は // main メソッドで System.setProperty(...) を呼び出して設定する System.setProperty("server.port", "9080") // args に null が渡されるが、null のまま SpringApplication.run(...) を呼び出すと // エラーになるので、args = new String[0] をセットする if (args == null) { args = new String[0] } SpringApplication.run(StubServer.class, args) } // フィールドに private を付けないこと static class ResponseData { int key String data } @GetMapping ResponseData stub() { return new ResponseData(key: 123, data: "xxxxxxxx") } }
StubServer.groovy を D:\tmp にコピーします。
動作確認
gse-servlet.bat StubServer.groovy
コマンドを実行すると Tomcat が 8080番ポートと 9080番ポートで起動します。
curl -v http://localhost:8080/stub
コマンドで 8080番ポートにアクセスしても HTTP ステータスコード = 404 が返ってきます。
curl -v http://localhost:9080/stub
コマンドで 9080番ポートにアクセスすると HTTP ステータスコード = 200 と {"key":123,"data":"xxxxxxxx"}
の JSON データが返ってきます。
Ctrl+C を入力して groovy-script-executor を終了します。
docker-compose up -d
コマンドを実行して SFTP サーバを起動した後、gse SftpClient.groovy --user=user01 --password=pass01 --upload-dir=upload --upload-file=publications.csv
コマンドを実行すると SFTP クライアントの Groovy スクリプトも問題なく動作してファイルをアップロードしました。
履歴
2021/12/22
初版発行。
Grooy スクリプトをそのまま渡して実行する Spring Boot+Picocli ベースのコマンドラインアプリを作成する ( その7 )( @SpringBootApplication アノテーションを付与した Groovy スクリプトで SFTP クライアントを作成する )
概要
記事一覧はこちらです。
今回から @SpringBootApplication アノテーションを付与した Groovy スクリプトを作成してみます。
- 今回の手順で確認できるのは以下の内容です。
- Spring Integration の SFTP Adapters を利用して簡単な SFTP クライアントを作成します。
参照したサイト・書籍
atmoz/sftp
https://hub.docker.com/r/atmoz/sftpSFTP Adapters
https://docs.spring.io/spring-integration/docs/current/reference/html/sftp.html#sftp@Unmatched annotation
https://picocli.info/#unmatched-annotationConcatenate Two Arrays in Java
https://www.baeldung.com/java-concatenate-arraysspring-integration/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpRemoteFileTemplateTests.java
https://github.com/spring-projects/spring-integration/blob/main/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpRemoteFileTemplateTests.java
目次
- SFTP クライアントの仕様をまとめる
- docker-compose.yml に SFTP サーバを起動するための設定を追加する
- build.gradle に Spring Integration の SFTP Adapter を依存関係に追加する
- Groovy スクリプト側で Picocli の @Option アノテーションが使用できるよう groovy-script-executor.jar を変更する
- Groovy スクリプトを配置する package を sample → ksby.cmdapp.groovyscriptexecutor.script に変更する
- groovy-script-executor.jar 内に SFTP のアップロード・ダウンロード処理のための helper クラスを作成する
- Groovy スクリプトで SFTP クライアントを作成する
- groovy-script-executor.jar と Groovy スクリプトが出力するログを調整する
- 動作確認
- メモ書き
手順
SFTP クライアントの仕様をまとめる
- ファイルのアップロード・ダウンロードを以下のコマンドで実行できるようにします。
gse SftpClient.groovy --host=<ホスト名> --port=<ポート番号> --user=<ユーザ名> --password=<パスワード> --upload-dir=<アップロード先ディレクトリ> --upload-file=<アップロードするファイル>
gse SftpClient.groovy --host=<ホスト名> --port=<ポート番号> --user=<ユーザ名> --password=<パスワード> --download-src=<ダウンロード元ファイル> --download-dst=<ダウンロード先ファイル>
- これまで Groovy スクリプトは sample package の下に作成していましたが、groovy-script-executor.jar 内に作成する Spring の Component のメソッドを呼び出せるようにするために ksby.cmdapp.groovyscriptexecutor.script package の下に作成します(これまで作成した Groovy スクリプトもこの下に移動します)。
- SFTP のアップロード・ダウンロード処理は groovy-script-executor.jar 内に Spring の Component として作成します。Groovy スクリプトはこの Component のメソッドを呼び出します。またこの Component は ComponentScan の記述を省略するため ksby.cmdapp.groovyscriptexecutor.script.helper package の下に作成します。
docker-compose.yml に SFTP サーバを起動するための設定を追加する
atmoz/sftp を利用して SFTP サーバを構築します。Spring Boot + Spring Integration でいろいろ試してみる ( その29 )( Docker Compose でサーバを構築する、FTP+SFTPサーバ編 ) も参考にしてください。
まず docker/sftp-server/config ディレクトリを作成し、この下に users.conf を新規作成して以下の内容を記述します。このファイルは改行コードを LF にします。
user01:pass01:::upload,download
.gitattributes を以下のように変更します。
# # https://help.github.com/articles/dealing-with-line-endings/ # # These are explicitly windows files and should use crlf *.bat text eol=crlf docker/sftp-server/config/users.conf text eol=lf
docker/sftp-server/config/users.conf text eol=lf
を追加します。
docker-compose.yml の一番下に sftp-server の設定を追加します。
############################################################################# # sftp-server # sftp-server: image: atmoz/sftp:alpine-3.7 container_name: sftp-server ports: - "22:22" volumes: - ./docker/sftp-server/config/users.conf:/etc/sftp/users.conf:ro
docker-compose up -d
コマンドを実行して atmoz/sftp の Docker Image を pull してサーバを起動します。
WinSCP で接続できることを確認します。
build.gradle に Spring Integration の SFTP Adapter を依存関係に追加する
groovy-script-executor/build.gradle を以下のように変更します。
dependencies { .......... // dependency-management-plugin によりバージョン番号が自動で設定されるもの // Dependency Versions ( https://docs.spring.io/spring-boot/docs/current/reference/html/dependency-versions.html#dependency-versions ) 参照 implementation("org.springframework.boot:spring-boot-starter") implementation("org.springframework.integration:spring-integration-sftp") ..........
implementation("org.springframework.integration:spring-integration-sftp")
を追加します。
Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
Groovy スクリプト側で Picocli の @Option アノテーションが使用できるよう groovy-script-executor.jar を変更する
今の groovy-script-executor.jar の実装ではコマンドライン引数を @Parameters アノテーションで Groovy スクリプト側で受け取ることは出来るのですが、@Option アノテーションで受け取ることが出来ません。
Groovy スクリプト側で @Option アノテーションを付与したフィールドを定義しても、groovy-script-executor.jar の GroovyScriptExecutorCommand クラスで先に @Option アノテーションで定義されているかがチェックされて、定義されていないとエラーになります。
GroovyScriptExecutorCommand クラスに @Unmatched アノテーション を付与したフィールドを定義しておくと @Option アノテーションで定義されていなくてもエラーにならず @Unmatched アノテーションを付与したフィールドにセットされますので、この動作を利用します。
groovy-script-executor/src/main/java/ksby/cmdapp/groovyscriptexecutor/command/GroovyScriptExecutorCommand.java を以下のように変更します。
package ksby.cmdapp.groovyscriptexecutor.command; import groovy.lang.Binding; import groovy.lang.GroovyShell; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ArrayUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.info.BuildProperties; import org.springframework.stereotype.Component; import java.io.File; import java.io.IOException; import java.util.concurrent.Callable; import static picocli.CommandLine.*; @Slf4j @Component @Command(name = "groovy-script-executor", mixinStandardHelpOptions = true, versionProvider = GroovyScriptExecutorCommand.class, description = "Groovyスクリプトを実行するコマンド") public class GroovyScriptExecutorCommand implements Callable<Integer>, IExitCodeExceptionMapper, IVersionProvider { @Autowired private BuildProperties buildProperties; @Parameters(index = "0", paramLabel = "Groovyスクリプト", description = "実行する Groovyスクリプトを指定する") private File groovyScript; @Parameters(index = "1..*", paramLabel = "引数", description = "Groovyスクリプトに渡す引数を指定する") private String[] args; @Unmatched private String[] unmatched; @Override public Integer call() throws IOException { try { Binding binding = new Binding(); GroovyShell shell = new GroovyShell(binding); shell.run(groovyScript, ArrayUtils.addAll(args, unmatched)); } catch (Exception e) { log.error("Groovyスクリプトでエラーが発生しました。", e); } return ExitCode.OK; } ..........
@Unmatched private String[] unmatched;
を追加します。- call メソッド内の以下の点を変更します。
shell.run(groovyScript, args);
→shell.run(groovyScript, ArrayUtils.addAll(args, unmatched));
に変更します。
Groovy スクリプトを配置する package を sample → ksby.cmdapp.groovyscriptexecutor.script に変更する
groovy-script-executor/src/main/groovy の下に ksby.cmdapp.groovyscriptexecutor.script package を新規作成し、sample package の下の Groovy スクリプトをこの下に移動します。移動後、sample package を削除します。
ksby.cmdapp.groovyscriptexecutor.script package の下の Groovy スクリプトが groovy-script-executor.jar に含まれないようにするために groovy-script-executor/build.gradle を以下のように変更します。
[compileJava, compileTestGroovy, compileTestJava]*.options*.encoding = "UTF-8" [compileJava, compileTestGroovy, compileTestJava]*.options*.compilerArgs = ["-Xlint:all,-options,-processing,-path"] sourceSets { main { compileGroovy { exclude "ksby/cmdapp/groovyscriptexecutor/script/*.groovy" } } } ..........
- SourceSets 内で
exclude "sample/*.groovy"
→exclude "ksby/cmdapp/groovyscriptexecutor/script/*.groovy"
に変更します。
また groovy-script-executor/src/main/java/ksby/cmdapp/groovyscriptexecutor の下にも script package を新規作成します。
groovy-script-executor.jar 内に SFTP のアップロード・ダウンロード処理のための helper クラスを作成する
groovy-script-executor/src/main/java/ksby/cmdapp/groovyscriptexecutor/script の下に helper.sftp package を新規作成した後、この下に SftpHelper.java を新規作成し以下のコードを記述します。
package ksby.cmdapp.groovyscriptexecutor.script.helper.sftp; import com.jcraft.jsch.ChannelSftp; import com.jcraft.jsch.SftpException; import org.springframework.expression.common.LiteralExpression; import org.springframework.integration.file.remote.ClientCallbackWithoutResult; import org.springframework.integration.file.support.FileExistsMode; import org.springframework.integration.sftp.session.DefaultSftpSessionFactory; import org.springframework.integration.sftp.session.SftpRemoteFileTemplate; import org.springframework.integration.support.MessageBuilder; import org.springframework.messaging.Message; import org.springframework.stereotype.Component; import java.io.File; @Component public class SftpHelper { public SftpRemoteFileTemplate createSftpRemoteFileTemplate( String host, int port, String user, String password) { DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(false); factory.setHost(host); factory.setPort(port); factory.setUser(user); factory.setPassword(password); factory.setAllowUnknownKeys(true); return new SftpRemoteFileTemplate(factory); } public void upload(SftpRemoteFileTemplate sftpRemoteFileTemplate, String uploadDir, File uploadFile) { sftpRemoteFileTemplate.setRemoteDirectoryExpression(new LiteralExpression(uploadDir)); Message<File> message = MessageBuilder.withPayload(uploadFile).build(); sftpRemoteFileTemplate.send(message, FileExistsMode.REPLACE); } public void download(SftpRemoteFileTemplate sftpRemoteFileTemplate, String downlaodSrc, String downloadDst) { sftpRemoteFileTemplate.executeWithClient( (ClientCallbackWithoutResult<ChannelSftp>) client -> { try { client.get(downlaodSrc, downloadDst); } catch (SftpException e) { throw new RuntimeException(e); } }); } }
Groovy スクリプトで SFTP クライアントを作成する
groovy-script-executor/src/main/groovy/ksby/cmdapp/groovyscriptexecutor/script の下に SftpClient.groovy を新規作成し、以下のコードを記述します。
package ksby.cmdapp.groovyscriptexecutor.script import groovy.util.logging.Slf4j import ksby.cmdapp.groovyscriptexecutor.script.helper.sftp.SftpHelper import org.springframework.boot.CommandLineRunner import org.springframework.boot.ExitCodeGenerator import org.springframework.boot.SpringApplication import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.integration.sftp.session.SftpRemoteFileTemplate import org.springframework.stereotype.Component import picocli.CommandLine import picocli.CommandLine.ArgGroup import picocli.CommandLine.Command import picocli.CommandLine.ExitCode import picocli.CommandLine.IExitCodeExceptionMapper import picocli.CommandLine.IFactory import picocli.CommandLine.Option import java.util.concurrent.Callable @Slf4j @SpringBootApplication class SftpClient implements CommandLineRunner, ExitCodeGenerator { private int exitCode private final SftpClientCommand sftpClientCommand private final IFactory factory SftpClient(SftpClientCommand sftpClientCommand, IFactory factory) { this.sftpClientCommand = sftpClientCommand this.factory = factory } static void main(String[] args) { System.exit(SpringApplication.exit(SpringApplication.run(SftpClient.class, args))); } @Override void run(String... args) throws Exception { exitCode = new CommandLine(sftpClientCommand, factory) .setExitCodeExceptionMapper(sftpClientCommand) .execute(args) } @Override int getExitCode() { return exitCode } @Component @Command(name = "SftpClient", mixinStandardHelpOptions = true, description = "SFTPサーバにファイルをアップロード・ダウンロードするコマンド") static class SftpClientCommand implements Callable<Integer>, IExitCodeExceptionMapper { @Option(names = "--host", required = false, description = "ホスト名") String host = "localhost" @Option(names = "--port", required = false, description = "ポート番号") int port = 22 @Option(names = ["-u", "--user"], description = "ユーザ名") String user @Option(names = ["-p", "--password"], description = "パスワード") String password @ArgGroup(exclusive = true, multiplicity = "1") SftpClientOperation sftpClientOperation static class SftpClientOperation { @ArgGroup(exclusive = false, multiplicity = "1") UploadOption uploadOption @ArgGroup(exclusive = false, multiplicity = "1") DownloadOption downloadOption } static class UploadOption { @Option(names = "--upload-dir", required = true, description = "アップロード先ディレクトリ") String uploadDir @Option(names = "--upload-file", required = true, description = "アップロードするファイル") File uploadFile } static class DownloadOption { @Option(names = "--download-src", required = true, description = "ダウンロード元ファイル") String downlaodSrc @Option(names = "--download-dst", required = true, description = "ダウンロード先ファイル") String downloadDst } private final SftpHelper sftpHelper SftpClientCommand(SftpHelper sftpHelper) { this.sftpHelper = sftpHelper } @Override Integer call() throws Exception { SftpRemoteFileTemplate sftpRemoteFileTemplate = sftpHelper.createSftpRemoteFileTemplate(host, port, user, password) if (sftpClientOperation.uploadOption != null) { log.info("{} へ {} をアップロードします", sftpClientOperation.uploadOption.uploadDir, sftpClientOperation.uploadOption.uploadFile) sftpHelper.upload(sftpRemoteFileTemplate, sftpClientOperation.uploadOption.uploadDir, sftpClientOperation.uploadOption.uploadFile) } else { log.info("{} を {} へダウンロードします", sftpClientOperation.downloadOption.downlaodSrc, sftpClientOperation.downloadOption.downloadDst) sftpHelper.download(sftpRemoteFileTemplate, sftpClientOperation.downloadOption.downlaodSrc, sftpClientOperation.downloadOption.downloadDst) } return ExitCode.OK } @Override int getExitCode(Throwable exception) { if (exception instanceof RuntimeException) { return 101 } return ExitCode.OK } } }
groovy-script-executor.jar と Groovy スクリプトが出力するログを調整する
groovy-script-executor/src/main/resources/application.properties を以下のように変更します。
spring.main.banner-mode=off logging.level.root=ERROR logging.level.com.jcraft.jsch=ERROR logging.level.ksby.cmdapp.groovyscriptexecutor.script=INFO logging.level.org.springframework.integration.expression.ExpressionUtils=ERROR
logging.level.root=OFF
→logging.level.root=ERROR
に変更します。- 以下の行を追加します。
logging.level.com.jcraft.jsch=ERROR
- SFTP のライブラリのログです。SFTP の処理の詳細を知りたい時にはこのログのレベルを変更します。
logging.level.ksby.cmdapp.groovyscriptexecutor.script=INFO
- Groovy スクリプトのログレベルを INFO にします。
logging.level.org.springframework.integration.expression.ExpressionUtils=ERROR
- あまり意味のない WARN ログが出力されるのでログレベルを ERROR に変更して出力されないようにします。
ログレベルは groovy-script-executor.jar で設定したので D:\tmp\application.properties を削除します。
コマンドラインで Groovy スクリプトを実行する際には JSON フォーマットのログは不便なので CONSOLE に戻します。groovy-script-executor/src/main/resources/logback-spring.xml を以下のように変更します。
.......... <if condition='isDefined("LOG_FILE")'> <then> <root> <appender-ref ref="${LOGGING_APPENDER}"/> </root> </then> <else> <root> <appender-ref ref="CONSOLE"/> <!--<appender-ref ref="JSON"/>--> </root> </else> </if> </configuration>
build タスクを実行し、生成した groovy-script-executor.jar を D:\tmp にコピーします。
動作確認
SFTP サーバの upload ディレクトリに何もファイルがないことを確認してから、
gse SftpClient.groovy --user=user01 --password=pass01 --upload-dir=upload --upload-file=publications.csv
コマンドを実行するとログが出力されて、
upload ディレクトリに publications.csv がアップロードされます。
次に gse SftpClient.groovy --user=user01 --password=pass01 --download-src=upload/publications.csv --download-dst=D:\tmp\sample.csv
コマンドを実行すると、
upload/publications.csv が D:\tmp の下に sample.csv としてダウンロードされます。
メモ書き
groovy-script-executor.jar 内の Application クラス(@SpringBootApplication を付与したクラス)が ComponentScan の対象に含まれると Groovy スクリプトは実行されるが help が表示される
SftpClient.groovy の @SpringBootApplication アノテーションに scanBasePackages = "ksby.cmdapp.groovyscriptexecutor"
を指定して ComponentScan の対象に groovy-script-executor.jar 内の Application クラスが含まれるようにしてから、
@Slf4j @SpringBootApplication(scanBasePackages = "ksby.cmdapp.groovyscriptexecutor") class SftpClient implements CommandLineRunner, ExitCodeGenerator {
gse SftpClient.groovy --user=user01 --password=pass01 --upload-dir=upload --upload-file=publications.csv
コマンドを実行すると Missing required parameter: 'Groovyスクリプト
のメッセージと help が表示されます。
ただし Groovy スクリプト自体は実行されており、ファイルはアップロードされていました。
メッセージと help が表示されるのは紛らわしいので、groovy-script-executor.jar 内の Application クラス(@SpringBootApplication を付与したクラス)が ComponentScan の対象に含まれないようにした方がよいでしょう。
履歴
2021/11/27
初版発行。
Grooy スクリプトをそのまま渡して実行する Spring Boot+Picocli ベースのコマンドラインアプリを作成する ( その6 )( Groovy スクリプトからログをコンソールやファイルに出力する )
概要
記事一覧はこちらです。
- 今回の手順で確認できるのは以下の内容です。
- Groovy スクリプトからログをコンソール、あるいはファイルに出力できるようにします。
参照したサイト・書籍
Runtime and compile-time metaprogramming
https://groovy-lang.org/metaprogramming.html- @groovy.util.logging.Slf4j の説明が https://groovy-lang.org/metaprogramming.html#xform-Slf4j にあります。
Unicode Support
https://conemu.github.io/en/UnicodeSupport.htmlChapter 3: Logback configuration - Conditional processing of configuration files
http://logback.qos.ch/manual/configuration.html#conditionalバッチファイルでファイルパスからファイル名や拡張子を自由に取り出す方法
https://orangeclover.hatenablog.com/entry/20101004/1286120668Get Log Output in JSON
https://www.baeldung.com/java-log-json-outputStructured logging with SLF4J and Logback
https://gquintana.github.io/2017/12/01/Structured-logging-with-SL-FJ-and-Logback.htmllogfellow / logstash-logback-encoder
https://github.com/logfellow/logstash-logback-encoderJava ログ収集
https://docs.datadoghq.com/ja/logs/log_collection/java/?tab=logback
目次
手順
Groovy スクリプトからログを出力する
CsvFileToBookTable.groovy にログ出力処理を追加する
groovy-script-executor/src/main/groovy/sample/CsvFileToBookTable.groovy にログ出力処理を追加します。
package sample import com.univocity.parsers.annotations.Parsed import com.univocity.parsers.common.processor.BeanListProcessor import com.univocity.parsers.csv.CsvParserSettings import com.univocity.parsers.csv.CsvRoutines import groovy.sql.Sql import groovy.util.logging.Slf4j @Slf4j class CsvFileToBookTable { static class CsvRecord { @Parsed(index = 0, field = "isbm") String isbm @Parsed(index = 1, field = "title_author") String title_author } static void main(args) { def sql = Sql.newInstance("jdbc:mysql://localhost:3306/testdb?sslMode=DISABLED&characterEncoding=utf8", "testdb_user", "xxxxxxxx", "org.mariadb.jdbc.Driver") sql.connection.autoCommit = false CsvParserSettings settings = new CsvParserSettings() settings.format.lineSeparator = "\r\n" settings.headerExtractionEnabled = true BeanListProcessor<CsvRecord> rowProcessor = new BeanListProcessor<>(CsvRecord) settings.processor = rowProcessor sql.execute("truncate table book") log.info("bookテーブルをtruncateしました。") new File("publications.csv").withReader { reader -> CsvRoutines csvRoutines = new CsvRoutines(settings) for (CsvRecord csvRecord : csvRoutines.iterate(CsvRecord, reader)) { String[] titleAndAuthor = csvRecord.title_author.split(" / ") def title = titleAndAuthor[0] def author = null if (titleAndAuthor.size() == 1) { log.warn("title_authorカラムにはauthorが記載されていません。") } else { author = titleAndAuthor[1] } sql.execute(""" insert into book (isbm, title, author) values (:isbm, :title, :author) """, isbm: csvRecord.isbm, title: title, author: author) log.info("bookテーブルに登録しました (isbm = {}, title = {}, author = {})", csvRecord.isbm, title, author) } } sql.commit() sql.close() } }
- クラスに
@Slf4j
アノテーションを付与します。これは Lombok のアノテーションではなく Groovy が提供している groovy.util.logging.Slf4j アノテーションです(Groovy では Lombok の@Slf4j
アノテーションは機能しません)。 - ログを出力する箇所に
log.info(...)
、log.warn(...)
を追加します。
コンソールにログを出力する
D:\tmp の下に application.properties を新規作成し、以下の内容を記述します。Groovy スクリプトと同じディレクトリにある application.properties の設定で groovy-script-executor.jar の groovy-script-executor/src/main/resources/application.properties の設定が上書きされます。
logging.level.root=INFO #sample パッケージのログだけ出力したい場合には、root → sample に変更する #logging.level.sample=INFO
gse CsvFileToBookTable.groovy
で Groovy スクリプトを実行するとログが出力されますが、gse.bat 内で -Dfile.encoding=UTF-8
を指定しているのでログの文字コードは UTF-8 になります。そのままでは Windows のコマンドプロンプトでは文字化けします。
chcp 65001 & cmd
コマンドを実行してから gse CsvFileToBookTable.groovy
を実行すれば文字化けしなくなります(Unicode Support 参照)。
ファイルにログを出力する
logback-spring.xml の記述で janino を使いたいので build.gradle に追加します。
dependencies { .......... // dependency-management-plugin によりバージョン番号が自動で設定されるもの // Dependency Versions ( https://docs.spring.io/spring-boot/docs/current/reference/html/dependency-versions.html#dependency-versions ) 参照 implementation("org.springframework.boot:spring-boot-starter") implementation("org.apache.commons:commons-lang3") implementation("org.codehaus.janino:janino") testImplementation("org.springframework.boot:spring-boot-starter-test") ..........
- dependencies block に
implementation("org.codehaus.janino:janino")
を追加します。
groovy-script-executor/src/main/resources の下に logback-spring.xml を新規作成し、以下の内容を記述します。logging.file.name が設定されていればログファイルへ、設定されていなければコンソールへログを出力します。
<?xml version="1.0" encoding="UTF-8"?> <configuration> <include resource="org/springframework/boot/logging/logback/defaults.xml"/> <include resource="org/springframework/boot/logging/logback/console-appender.xml"/> <property name="LOGGING_APPENDER" value="${logging.appender:-FILE}"/> <if condition='isDefined("LOG_FILE")'> <then> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <encoder> <pattern>${FILE_LOG_PATTERN}</pattern> </encoder> <file>${LOG_FILE}</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}</fileNamePattern> <maxHistory>30</maxHistory> </rollingPolicy> </appender> </then> </if> <if condition='isDefined("LOG_FILE")'> <then> <root> <appender-ref ref="${LOGGING_APPENDER}"/> </root> </then> <else> <root> <appender-ref ref="CONSOLE"/> </root> </else> </if> </configuration>
build タスクを実行し、生成した groovy-script-executor.jar を D:\tmp にコピーします。
Groovy スクリプトと同じディレクトリにログを出力するよう gse.bat を以下のように変更します。
@echo off java -Dfile.encoding=UTF-8 ^ -XX:TieredStopAtLevel=1 ^ -Dspring.main.lazy-initialization=true ^ -Dlogging.file.name=%~n1.log ^ -jar groovy-script-executor-1.0.0-RELEASE.jar ^ %*
-Dlogging.file.name=%~n1.log ^
を追加します。
gse CsvFileToBookTable.groovy
で Groovy スクリプトを実行するとコンソールには何も出力されず、
CsvFileToBookTable.groovy と同じディレクトリに CsvFileToBookTable.log が作成されて、その中にログが出力されます。
gse.bat 内の -Dlogging.file.name=%~n1.log ^
を削除すれば、コンソールにログが出力されます。
ログを JSON フォーマットで出力する
logstash-logback-encoder を導入してログファイルを JSON フォーマットで出力してみます。
build.gradle を以下のように変更します。
dependencies { .......... // dependency-management-plugin によりバージョン番号が自動で設定されないもの、あるいは最新バージョンを指定したいもの runtimeOnly("${postgresqlJdbcDriver}") runtimeOnly("${mariadbJdbcDriver}") implementation("com.univocity:univocity-parsers:2.9.1") implementation("net.logstash.logback:logstash-logback-encoder:6.6") testImplementation("org.assertj:assertj-core:3.21.0")
- dependencies block に
implementation("net.logstash.logback:logstash-logback-encoder:6.6")
を追加します。
logback-spring.xml を以下のように変更します。
<?xml version="1.0" encoding="UTF-8"?> <configuration> <include resource="org/springframework/boot/logging/logback/defaults.xml"/> <include resource="org/springframework/boot/logging/logback/console-appender.xml"/> <property name="LOGGING_APPENDER" value="${logging.appender:-FILE}"/> <appender name="JSON" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="net.logstash.logback.encoder.LogstashEncoder"> <jsonGeneratorDecorator class="net.logstash.logback.decorate.PrettyPrintingJsonGeneratorDecorator"/> <throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter"> <maxDepthPerThrowable>30</maxDepthPerThrowable> <maxLength>2048</maxLength> <shortenedClassNameLength>20</shortenedClassNameLength> <rootCauseFirst>true</rootCauseFirst> <inlineHash>true</inlineHash> </throwableConverter> </encoder> </appender> <if condition='isDefined("LOG_FILE")'> <then> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <encoder> <pattern>${FILE_LOG_PATTERN}</pattern> </encoder> <file>${LOG_FILE}</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}</fileNamePattern> <maxHistory>30</maxHistory> </rollingPolicy> </appender> </then> </if> <if condition='isDefined("LOG_FILE")'> <then> <root> <appender-ref ref="${LOGGING_APPENDER}"/> </root> </then> <else> <root> <!--<appender-ref ref="CONSOLE"/>--> <appender-ref ref="JSON"/> </root> </else> </if> </configuration>
<appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">...</appender>
を追加します。<appender-ref ref="CONSOLE"/>
をコメントアウトして<appender-ref ref="JSON"/>
を追加します。
今のままでは stack trace が JSON フォーマットで出力されないので、groovy-script-executor/src/main/java/ksby/cmdapp/groovyscriptexecutor/command/GroovyScriptExecutorCommand.java を以下のように変更します。
public class GroovyScriptExecutorCommand implements Callable<Integer>, IExitCodeExceptionMapper, IVersionProvider { .......... @Override public Integer call() throws IOException { try { Binding binding = new Binding(); GroovyShell shell = new GroovyShell(binding); shell.run(groovyScript, args); } catch (Exception e) { log.error("Groovyスクリプトでエラーが発生しました。", e); } return ExitCode.OK; } .......... }
- call メソッド内の処理を
try { ... } catch (Exception e) { log.error("Groovyスクリプトでエラーが発生しました。", e); }
で囲みます。
build タスクを実行し、生成した groovy-script-executor.jar を D:\tmp にコピーします。
gse.bat から -Dlogging.file.name=%~n1.log
を削除してログがコンソールに出力されるようにしてから gse CsvFileToBookTable.groovy
を実行すると、コンソールにログが JSON フォーマットで出力されます。
また CsvFileToBookTable.groovy 内でエラーが発生するよう変更してから実行すると stack trace も JSON フォーマットで出力されます。
履歴
2021/11/16
初版発行。