Grooy スクリプトをそのまま渡して実行する Spring Boot+Picocli ベースのコマンドラインアプリを作成する ( その2 )( groovy-script-executor.jar を作成する )
概要
記事一覧はこちらです。
Grooy スクリプトをそのまま渡して実行する Spring Boot+Picocli ベースのコマンドラインアプリを作成する ( その1 )( 概要 ) の続きです。
- 今回の手順で確認できるのは以下の内容です。
参照したサイト・書籍
- picocli - a mighty tiny command line interface
https://picocli.info/
目次
- https://github.com/ksby/groovy-script-executor を clone して Gradle の Multi-project を作成する
- groovy-script-executor サブプロジェクトを作成して実装する
- Hello, World を出力する簡単な Groovy スクリプトを作成して動作確認する
- 起動時オプションで
-Dfile.encoding=UTF-8
等を指定したいので gse.bat を作成する
手順
https://github.com/ksby/groovy-script-executor を clone して Gradle の Multi-project を作成する
https://github.com/ksby/groovy-script-executor を clone した後、Spring Initializr で demo プロジェクトを作成してから以下のファイルをコピーします。
- gradle ディレクトリ
- gradlew
- gradlew.bat
gradlew init
コマンドを実行します。
settings.gradle を以下の内容に書き替えます。
rootProject.name = 'groovy-script-executor' rootDir.eachFileRecurse { f -> if (f.name == "build.gradle") { String relativePath = f.parentFile.absolutePath - rootDir.absolutePath String projectName = relativePath.replaceAll("[\\\\\\/]", ":") if (projectName != ":buildSrc") { include projectName } } }
groovy-script-executor サブプロジェクトを作成して実装する
IntelliJ IDEA のメインメニューから「File」-「New」-「Project...」を選択して「New Project」ダイアログを表示した後、以下の値を入力します。Dependencies では何も選択せず「Finish」ボタンをクリックします。
作成されたサブプロジェクトの以下の点を変更します。
- src ディレクトリ、build.gradle 以外のディレクトリ・ファイルを削除します。
- GroovyScriptExecutorApplication クラスの名前を Application に変更します。
- src/test の下をクリアした後、その下に .gitkeep を作成します。
build.gradle を以下の内容にします。
buildscript { ext { group "ksby" version "1.0.0-RELEASE" } repositories { mavenCentral() gradlePluginPortal() } } plugins { id 'java' id 'groovy' id 'org.springframework.boot' version '2.5.6' id 'io.spring.dependency-management' version '1.0.11.RELEASE' } sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 [compileJava, compileTestGroovy, compileTestJava]*.options*.encoding = "UTF-8" [compileJava, compileTestGroovy, compileTestJava]*.options*.compilerArgs = ["-Xlint:all,-options,-processing,-path"] bootJar { duplicatesStrategy = DuplicatesStrategy.INCLUDE } jar { enabled = false } springBoot { buildInfo() } configurations { compileOnly.extendsFrom annotationProcessor // annotationProcessor と testAnnotationProcessor、compileOnly と testCompileOnly を併記不要にする testAnnotationProcessor.extendsFrom annotationProcessor testImplementation.extendsFrom compileOnly // JUnit 4 が依存関係に入らないようにする all { exclude group: "junit", module: "junit" } } repositories { mavenCentral() } 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.1") } } dependencies { def groovyVersion = "3.0.9" def picocliVersion = "4.6.1" def lombokVersion = "1.18.22" def spockVersion = "2.0-groovy-3.0" // 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") testImplementation("org.springframework.boot:spring-boot-starter-test") // dependency-management-plugin によりバージョン番号が自動で設定されないもの、あるいは最新バージョンを指定したいもの implementation("com.univocity:univocity-parsers:2.9.1") testImplementation("org.assertj:assertj-core:3.21.0") // for Groovy implementation("org.codehaus.groovy:groovy-all:${groovyVersion}") // for Picocli implementation("info.picocli:picocli-spring-boot-starter:${picocliVersion}") // for lombok // testAnnotationProcessor、testCompileOnly を併記しなくてよいよう configurations で設定している annotationProcessor("org.projectlombok:lombok:${lombokVersion}") compileOnly("org.projectlombok:lombok:${lombokVersion}") // for JUnit 5 // junit-jupiter で junit-jupiter-api, junit-jupiter-params, junit-jupiter-engine の3つが依存関係に追加される testImplementation("org.junit.jupiter:junit-jupiter") testRuntimeOnly("org.junit.platform:junit-platform-launcher") // for Spock testImplementation("org.spockframework:spock-core:${spockVersion}") testImplementation("org.spockframework:spock-spring:${spockVersion}") } def jvmArgsForTask = [ "-ea", "-Dfile.encoding=UTF-8", "-XX:TieredStopAtLevel=1", "-Dspring.main.lazy-initialization=true" ] def printTestCount = { desc, result -> if (!desc.parent) { // will match the outermost suite println "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)" } } bootRun { jvmArgs = jvmArgsForTask } clean { doLast { project.file("out").deleteDir() project.file("src/main/generated").deleteDir() project.file("src/test/generated_tests").deleteDir() } } test { jvmArgs = jvmArgsForTask // for JUnit 5 useJUnitPlatform() testLogging { afterSuite printTestCount } }
Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
ksby.cmdapp.groovyscriptexecutor の下に command パッケージを作成した後、GroovyScriptExecutorCommand クラスを新規作成して以下のコードを記述します。
package ksby.cmdapp.groovyscriptexecutor.command; import groovy.lang.Binding; import groovy.lang.GroovyShell; 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.*; @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; @Override public Integer call() throws IOException { Binding binding = new Binding(); GroovyShell shell = new GroovyShell(binding); shell.run(groovyScript, args); return ExitCode.OK; } @Override public int getExitCode(Throwable exception) { if (exception instanceof Exception) { return 1; } return ExitCode.OK; } @Override public String[] getVersion() { return new String[]{buildProperties.getVersion()}; } }
src/main/java/ksby/cmdapp/groovyscriptexecutor/Application.java を以下のコードに書き替えます。
package ksby.cmdapp.groovyscriptexecutor; import ksby.cmdapp.groovyscriptexecutor.command.GroovyScriptExecutorCommand; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.ExitCodeGenerator; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import picocli.CommandLine; import static picocli.CommandLine.IFactory; @SpringBootApplication public class Application implements CommandLineRunner, ExitCodeGenerator { private int exitCode; private final GroovyScriptExecutorCommand groovyScriptExecutorCommand; private final IFactory factory; public Application(GroovyScriptExecutorCommand groovyScriptExecutorCommand, IFactory factory) { this.groovyScriptExecutorCommand = groovyScriptExecutorCommand; this.factory = factory; } public static void main(String[] args) { System.exit(SpringApplication.exit(SpringApplication.run(Application.class, args))); } @Override public void run(String... args) throws Exception { exitCode = new CommandLine(groovyScriptExecutorCommand, factory) .setExitCodeExceptionMapper(groovyScriptExecutorCommand) .execute(args); } @Override public int getExitCode() { return exitCode; } }
src/main/resources/application.properties に以下の内容を記述します。
spring.main.banner-mode=off logging.level.root=OFF
clean タスク実行 → Rebuild Project 実行 → build タスクを実行します。
build/libs/groovy-script-executor-1.0.0-RELEASE.jar が生成されるので D:\tmp の下にコピーした後、java -jar groovy-script-executor-1.0.0-RELEASE.jar -H
、java -jar groovy-script-executor-1.0.0-RELEASE.jar -V
が動作することを確認します。
Hello, World を出力する簡単な Groovy スクリプトを作成して動作確認する
src/main の下に groovy ディレクトリを新規作成し、その下に .gitkeep を作成します。
src/main/groovy の下に HelloWorld.groovy を新規作成した後、以下のコードを記述します。
class HelloWorld { static void main(args) { println "Hello, World" } }
src/main/groovy/HelloWorld.groovy を D:\tmp の下に移動した後、java -jar groovy-script-executor-1.0.0-RELEASE.jar HelloWorld.groovy
を実行すると Hello, World
が出力されました。コマンド実行直後は build しているので、Hello, World
が出力されるまでに 4~5 秒程度かかります。
起動時オプションで -Dfile.encoding=UTF-8
等を指定したいので gse.bat を作成する
D:\tmp の下に gse.bat を新規作成し、以下のコードを記述します。
@echo off java -Dfile.encoding=UTF-8 ^ -XX:TieredStopAtLevel=1 ^ -Dspring.main.lazy-initialization=true ^ -jar groovy-script-executor-1.0.0-RELEASE.jar ^ %*
gse HelloWorld.groovy
を実行すると java -jar groovy-script-executor-1.0.0-RELEASE.jar HelloWorld.groovy
と同様に Hello, World
が出力されます。
履歴
2021/10/26
初版発行。