かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Grooy スクリプトをそのまま渡して実行する Spring Boot+Picocli ベースのコマンドラインアプリを作成する ( その2 )( groovy-script-executor.jar を作成する )

概要

記事一覧はこちらです。

Grooy スクリプトをそのまま渡して実行する Spring Boot+Picocli ベースのコマンドラインアプリを作成する ( その1 )( 概要 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • Spring Boot+Picocli ベースのコマンドラインアプリ(groovy-script-executor.jar)を作成します。
    • Hello, World を出力する簡単な Groovy スクリプトを作成して動作確認します。

参照したサイト・書籍

  1. picocli - a mighty tiny command line interface
    https://picocli.info/

目次

  1. https://github.com/ksby/groovy-script-executor を clone して Gradle の Multi-project を作成する
  2. groovy-script-executor サブプロジェクトを作成して実装する
  3. Hello, World を出力する簡単な Groovy スクリプトを作成して動作確認する
  4. 起動時オプションで -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 プロジェクトを作成してから以下のファイルをコピーします。

gradlew init コマンドを実行します。

f:id:ksby:20211024174915p:plain

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」ボタンをクリックします。

f:id:ksby:20211024180747p:plain f:id:ksby:20211024180837p:plain

作成されたサブプロジェクトの以下の点を変更します。

  • 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 タスクを実行します。

f:id:ksby:20211026200530p:plain

build/libs/groovy-script-executor-1.0.0-RELEASE.jar が生成されるので D:\tmp の下にコピーした後、java -jar groovy-script-executor-1.0.0-RELEASE.jar -Hjava -jar groovy-script-executor-1.0.0-RELEASE.jar -V が動作することを確認します。

f:id:ksby:20211026200800p:plain

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 秒程度かかります。

f:id:ksby:20211026201835p:plain

起動時オプションで -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 が出力されます。

f:id:ksby:20211026203900p:plain

履歴

2021/10/26
初版発行。

Grooy スクリプトをそのまま渡して実行する Spring Boot+Picocli ベースのコマンドラインアプリを作成する ( その1 )( 概要 )

概要

記事一覧はこちらです。

  • CSVファイルを読み込んで DB のテーブルを更新したり、DB のテーブルのデータを CSVファイルに出力したりするちょっとした Groovy スクリプトを build せずにそのまま実行したいと思ったので、作成してみることにします。
  • Groovy スクリプトは Groovy をインストールすれば groovy コマンドでそのまま実行出来ますが、今回は以下の内容で実現します。
    • Spring Boot+Picocli ベースのコマンドラインアプリ(groovy-script-executor-xxx.jar)を作成します。
    • コマンドラインアプリ(groovy-script-executor-xxx.jar)の中に必要なモジュールを全て入れておきます。
    • java -jar groovy-script-executor.jar <Groovyスクリプト> <引数...> のコマンドで実行できるようにします。
  • @SpringBootApplication アノテーションを付与した Groovy スクリプトのアプリケーションも実行できるといいな。。。 実行できれば Spring Integeration の SFTP Adapters を利用してファイルのアップロード・ダウンロードができるかもしれません。コマンドラインアプリを作成してから試してみることにします。
  • GraalVM で Windows の exe ファイル作成にもチャレンジしてみることにします。

Grooy スクリプトをそのまま渡して実行する Spring Boot+Picocli ベースのコマンドラインアプリを作成する ( 大目次 )

GitHubhttps://github.com/ksby/groovy-script-executor

  1. その1 ( 概要 )
  2. その2 ( groovy-script-executor.jar を作成する )

Spring Boot 2.4.x の Web アプリを 2.5.x へバージョンアップする ( その15 )( JUnit 4 を依存関係から削除する+他3点の気づいた点を変更する )

概要

記事一覧はこちらです。

Spring Boot 2.4.x の Web アプリを 2.5.x へバージョンアップする ( その14 )( google_checks.xml に Record に関する定義を反映する+bootBuildImage タスクで Java 17 ベースの Docker Image を作成して動作確認する ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • Spring Boot を 2.5系にバージョンアップしても JUnit 4 が依存関係に残ることに気づいたので除去します。
    • IntelliJ IDEA で Rebuild Project した時に生成される out ディレクトリが Gradle の clean タスクを実行しても残ることに気づいたので削除されるようにします。
    • Mockito.mockStatic を使用するテストでは try-with-resources 構文を使用してリソースが close されるようにしないといけないことに気づいたので修正します。
    • JDK 17 がサポートされた Lombok 1.18.22 がリリースされていたのでバージョンアップします。

参照したサイト・書籍

  1. How to exclude Gradle dependencies
    https://tomgregory.com/how-to-exclude-gradle-dependencies/

  2. @StandardException annotation for generating exception constructors
    https://github.com/projectlombok/lombok/pull/2702

目次

  1. 依存関係から JUnit 4 を除去する
  2. JUnit 4 のモジュールを利用しているクラスを変更・削除する
    1. src/test/java/ksbysample/common/test/extension/db/TestDataExtension.java
    2. src/test/java/ksbysample/common/test/extension/mail/MailServerExtension.java
    3. src/test/java/ksbysample/common/test/extension/mockmvc/SecurityMockMvcExtension.java
    4. src/test/java/ksbysample/common/test/helper/DescriptionWrapper.java
    5. src/test/groovy/ksbysample/webapp/lending/SampleHelperTest.groovy
  3. clean タスクで out ディレクトリが削除されるようにする
  4. Mockito.mockStatic を呼び出しているテストで try-with-resources 構文を使用してリソースが close されるようにする
  5. Lombok を 1.18.20 → 1.18.22 にバージョンアップする
  6. WebApplicationRuntimeException クラスを Lombok の @StandardException を利用して書き直す
  7. build タスクでエラーが出ないことを確認する

手順

依存関係から JUnit 4 を除去する

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その100 )( Gradle を 6.9.1 → 7.2 へ、Spring Boot を 2.4.10 → 2.5.4 へ、Geb を 4.1 → 5.0 へバージョンアップする2 ) の作業で Spring Boot を 2.5系にバージョンアップしても JUnit 4 が依存関係に残っていることに気づいたので、除去することにします。

今回は個々のモジュールに exclude を記述して除外するのではなく configurations block に exclude を記述する方法を採用します。build.gradle を以下のように変更します。

configurations {
    compileOnly.extendsFrom annotationProcessor

    // annotationProcessor と testAnnotationProcessor、compileOnly と testCompileOnly を併記不要にする
    testAnnotationProcessor.extendsFrom annotationProcessor
    testImplementation.extendsFrom compileOnly

    // JUnit 4 が依存関係に入らないようにする
    all {
        exclude group: "junit", module: "junit"
    }

    // for SpotBugs
    spotbugsStylesheets { transitive = false }
}
  • all { exclude group: "junit", module: "junit" } を追加します。

JUnit 4 のモジュールを利用しているクラスを修正する

src/test/java/ksbysample/common/test/extension/db/TestDataExtension.java

@Component
public class TestDataExtension implements BeforeEachCallback, AfterEachCallback {

    ..........

    @Override
    public void beforeEach(ExtensionContext context) {
        before(new ExtensionContextWrapper(context));
    }

    @Override
    public void afterEach(ExtensionContext context) {
        after(new ExtensionContextWrapper(context));
    }

    ..........
  • extends TestWatcher を削除します。
  • 以下のメソッドを削除します。
    • protected void starting(Description description) { ... }
    • protected void finished(Description description) { ... }

src/test/java/ksbysample/common/test/extension/mail/MailServerExtension.java

@Component
public class MailServerExtension implements BeforeEachCallback, AfterEachCallback {

    private GreenMail greenMail = new GreenMail(new ServerSetup(25, "localhost", ServerSetup.PROTOCOL_SMTP));

    @Override
    public void beforeEach(ExtensionContext context) {
        greenMail.start();
    }

    @Override
    public void afterEach(ExtensionContext context) {
        greenMail.stop();
    }

    ..........
  • extends ExternalResource を削除します。
  • 以下のメソッドを削除します。
    • protected void before() { ... }
    • protected void after() { ... }
  • beforeEach メソッド内の before();greenMail.start(); に変更します。
  • afterEach メソッド内の after();greenMail.stop(); に変更します。

src/test/java/ksbysample/common/test/extension/mockmvc/SecurityMockMvcExtension.java

@Component
@ConditionalOnWebApplication
public class SecurityMockMvcExtension implements BeforeEachCallback {

    ..........

    @Override
    public void beforeEach(ExtensionContext context) {
        // 認証ユーザ用MockMvc ( user = tanaka.taro@sample.com )
        UserDetails userDetailsTanakaTaro = userDetailsService.loadUserByUsername(MAILADDR_TANAKA_TARO);
        this.authTanakaTaro = MockMvcBuilders.webAppContextSetup(this.context)
                .defaultRequest(get("/").with(user(userDetailsTanakaTaro)))
                .apply(springSecurity())
                .build();

        // 認証ユーザ用MockMvc ( user = suzuki.hanako@test.co.jp )
        UserDetails userDetailsSuzukiHanako = userDetailsService.loadUserByUsername(MAILADDR_SUZUKI_HANAKO);
        this.authSuzukiHanako = MockMvcBuilders.webAppContextSetup(this.context)
                .defaultRequest(get("/").with(user(userDetailsSuzukiHanako)))
                .apply(springSecurity())
                .build();

        // 認証ユーザ用MockMvc ( user = ito.aoi@test.co.jp )
        UserDetails userDetailsItoAoi = userDetailsService.loadUserByUsername(MAILADDR_ITO_AOI);
        this.authItoAoi = MockMvcBuilders.webAppContextSetup(this.context)
                .defaultRequest(get("/").with(user(userDetailsItoAoi)))
                .apply(springSecurity())
                .build();

        // 非認証ユーザ用MockMvc
        this.noauth = MockMvcBuilders.webAppContextSetup(this.context)
                .apply(springSecurity())
                .build();
    }

}
  • extends ExternalResource を削除します。
  • before メソッド内のコードを beforeEach メソッドへ移動します。
  • 以下のメソッドを削除します。
    • protected void before() { ... }

src/test/java/ksbysample/common/test/helper/DescriptionWrapper.java

このファイルは使用されなくなったので削除します。

src/test/groovy/ksbysample/webapp/lending/SampleHelperTest.groovy

@RunWith(Enclosed) が記述されていたので削除します。

clean タスクで out ディレクトリが削除されるようにする

build.gradle の clean block を以下のように変更します。

clean {
    doLast {
        rootProject.file("out").deleteDir()
        rootProject.file("src/main/generated").deleteDir()
        rootProject.file("src/test/generated_tests").deleteDir()
    }
}
  • rootProject.file("out").deleteDir() を追加します。

Mockito.mockStatic を呼び出しているテストで try-with-resources 構文を使用してリソースが close されるようにする

src/test/groovy/ksbysample/webapp/lending/SampleHelperTest.groovy で以下のように書いているテストを、

    @SpringBootTest
    static class 異常処理のテスト {

        @Autowired
        private SampleHelper sampleHelper

        @Test
        void "SampleHelper_encryptを呼ぶとRuntimeExceptionをthrowする"() {
            // setup:
            Mockito.mockStatic(BrowfishUtils)
            Mockito.when(BrowfishUtils.encrypt(Mockito.any()))
                    .thenThrow(new NoSuchPaddingException())

            // expect:
            assertThatThrownBy(() -> {
                sampleHelper.encrypt("test")
            }).isInstanceOf(RuntimeException)
        }

    }

try-with-resources 構文を使用してリソースが close されるよう書き直します。

    @SpringBootTest
    static class 異常処理のテスト {

        @Autowired
        private SampleHelper sampleHelper

        @Test
        void "SampleHelper_encryptを呼ぶとRuntimeExceptionをthrowする"() {
            try (MockedStatic mockedBrowfishUtils = Mockito.mockStatic(BrowfishUtils)) {
                // setup:
                Mockito.when(BrowfishUtils.encrypt(Mockito.any()))
                        .thenThrow(new NoSuchPaddingException())

                // expect:
                assertThatThrownBy(() -> {
                    sampleHelper.encrypt("test")
                }).isInstanceOf(RuntimeException)
            }
        }

    }

Lombok を 1.18.20 → 1.18.22 にバージョンアップする

IntelliJ IDEA で build.gradle を見ていたら org.projectlombok:lombok: のところの色が変わっていて、マウスオーバーすると 1.8.22 がリリースされていることが分かりました。Alt+Enter を押すとバージョンも書き替えることも出来ます。この機能、すごい便利じゃないですか。。。

f:id:ksby:20211020062725p:plain

https://projectlombok.org/changelog を見ると v1.18.22 で PLATFORM: JDK17 support added. とのことなので、バージョンアップします。

build.gradle の以下の点を変更します。

dependencies {
    def spockVersion = "2.0-groovy-3.0"
    def jdbcDriver = "org.postgresql:postgresql:42.2.24"
    def domaVersion = "2.49.0"
    def lombokVersion = "1.18.22"
    def errorproneVersion = "2.9.0"

    ..........
  • def lombokVersion = "1.18.20"def lombokVersion = "1.18.22" に変更します。

Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。

WebApplicationRuntimeException クラスを Lombok の @StandardException を利用して書き直す

https://projectlombok.org/changelog の v1.18.22 のところを見ると @StandardException というアノテーションが追加されたとの記載があり、https://github.com/projectlombok/lombok/pull/2702 を見ると Exception クラスを継承して作成する時の基本的なコンストラクタを4つ自動生成してくれるとのこと。

便利そうなので WebApplicationRuntimeException クラスを @StandardException を利用して書き直してみます。

src/main/java/ksbysample/webapp/lending/exception/WebApplicationRuntimeException.java は現在以下のように実装していますが、

package ksbysample.webapp.lending.exception;

public class WebApplicationRuntimeException extends RuntimeException {

    @SuppressWarnings("PMD.FieldNamingConventions")
    private static final long serialVersionUID = 3845674924872653036L;

    public WebApplicationRuntimeException() {
        super();
    }

    public WebApplicationRuntimeException(String message) {
        super(message);
    }

    public WebApplicationRuntimeException(String message, Throwable cause) {
        super(message, cause);
    }

    public WebApplicationRuntimeException(Throwable cause) {
        super(cause);
    }

}

@StandardException を利用して書き直すと以下のようになります。@SuppressWarnings("PMD.FieldNamingConventions") も build 時に PMD が警告を出さないことに気づいたので削除します。

package ksbysample.webapp.lending.exception;

import lombok.experimental.StandardException;

import java.io.Serial;

@StandardException
public class WebApplicationRuntimeException extends RuntimeException {

    @Serial
    private static final long serialVersionUID = 3845674924872653036L;

}

build タスクでエラーが出ないことを確認する

最後に clean タスク実行 → Rebuild Project 実行 → build タスクを実行して、"BUILD SUCCESSFUL" のメッセージが出力されることを確認します。

f:id:ksby:20211020072446p:plain

履歴

2021/10/20
初版発行。

IntelliJ IDEA を 2021.2.2 → 2021.2.3 へ、Git for Windows を 2.33.0.2 → 2.33.1 へバージョンアップ

IntelliJ IDEA を 2021.2.2 → 2021.2.3 へバージョンアップする

IntelliJ IDEA の 2021.2.3 がリリースされているのでバージョンアップします。

※ksbysample-webapp-lending プロジェクトを開いた状態でバージョンアップしています。

  1. IntelliJ IDEA のメインメニューから「Help」-「Check for Updates...」を選択します。

  2. IntelliJ IDEA and Plugin Updates」ダイアログが表示されます。右下に「Update and Restart」ボタンが表示されていますので、「Update and Restart」ボタンをクリックします。

    f:id:ksby:20211019215125p:plain

  3. Plugin の update も表示されました。このまま「Update」ボタンをクリックします。

    f:id:ksby:20211019215212p:plain

  4. Patch がダウンロードされて IntelliJ IDEA が再起動します。

  5. IntelliJ IDEA が起動すると画面下部に「Indexing」のメッセージが表示されますので、終了するまで待機します(今回はかなり時間がかかりました)。

  6. IntelliJ IDEA のメインメニューから「Help」-「About」を選択し、2021.2.3 へバージョンアップされていることを確認します。

  7. clean タスク実行 → Rebuild Project 実行 → build タスクを実行して、"BUILD SUCCESSFUL" のメッセージが出力されることを確認します。

    f:id:ksby:20211019221231p:plain

  8. Project Tool Window で src/test でコンテキストメニューを表示して「More Run/Debug」-「Run 'All Tests' with Coverage」を選択し、テストが全て成功することを確認します。

    f:id:ksby:20211019221651p:plain

Git for Windows を 2.33.0.2 → 2.33.1 へバージョンアップする

Git for Windows の 2.33.1 がリリースされていたのでバージョンアップします。

  1. https://gitforwindows.org/ の「Download」ボタンをクリックして Git-2.33.1-64-bit.exe をダウンロードします。

  2. Git-2.33.1-64-bit.exe を実行します。

  3. 「Git 2.33.1 Setup」ダイアログが表示されます。インストーラーの画面を一通り見たいので「Only show new options」のチェックを外してから [Next >] ボタンをクリックします。

  4. 「Select Components」画面が表示されます。「Git LFS(Large File Support)」だけチェックした状態で [Next >]ボタンをクリックします。

  5. 「Choosing the default editor used by Git」画面が表示されます。「Use Vim (the ubiquitous text editor) as Git's default editor」が選択された状態で [Next >]ボタンをクリックします。

  6. 「Adjusting the name of the initial branch in new repositories」画面が表示されます。「Let Git decide」が選択されていることを確認後、[Next >]ボタンをクリックします。

  7. 「Adjusting your PATH environment」画面が表示されます。中央の「Git from the command line and also from 3rd-party software」が選択されていることを確認後、[Next >]ボタンをクリックします。

  8. 「Choosing the SSH executable」画面が表示されます。「Use bundled OpenSSL」が選択されていることを確認後、[Next >]ボタンをクリックします。

  9. 「Choosing HTTPS transport backend」画面が表示されます。「Use the OpenSSL library」が選択されていることを確認後、[Next >]ボタンをクリックします。

  10. 「Configuring the line ending conversions」画面が表示されます。一番上の「Checkout Windows-style, commit Unix-style line endings」が選択されていることを確認した後、[Next >]ボタンをクリックします。

  11. 「Configuring the terminal emulator to use with Git Bash」画面が表示されます。「Use Windows'default console window」が選択されていることを確認した後、[Next >]ボタンをクリックします。

  12. 「Choose the default behavior of git pull」画面が表示されます。「Default (fast-forward or merge)」が選択されていることを確認した後、[Next >]ボタンをクリックします。

  13. 「Choose a credential helper」画面が表示されます。「None」が選択されていることを確認した後、[Next >]ボタンをクリックします。

  14. 「Configuring extra options」画面が表示されます。「Enable file system caching」だけがチェックされていることを確認した後、[Next >]ボタンをクリックします。

  15. 「Configuring experimental options」画面が表示されます。何もチェックせずに [Install]ボタンをクリックします。

  16. インストールが完了すると「Completing the Git Setup Wizard」のメッセージが表示された画面が表示されます。中央の「View Release Notes」のチェックを外した後、[Next >]ボタンをクリックしてインストーラーを終了します。

  17. コマンドプロンプトを起動して git --version を実行し、git のバージョンが git version 2.33.1.windows.1 になっていることを確認します。

    f:id:ksby:20211019222725p:plain

  18. 特に問題はないようですので、2.33.1 で作業を進めたいと思います。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その104 )( Checkstyle を 8.32 → 9.0.1 へバージョンアップする )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その103 )( Eclipse Adoptium OpenJDK(Eclipse Temurin)を 11.0.12+7 → 17+35 へバージョンアップする ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • Checkstyle を 8.32 → 9.0.1 へバージョンアップします。

参照したサイト・書籍

目次

  1. Checkstyle を 8.32 → 9.0.1 へバージョンアップする

手順

Checkstyle を 8.32 → 9.0.1 へバージョンアップする

build.gradle の以下の点を変更します。

checkstyle {
    configFile = file("${rootProject.projectDir}/config/checkstyle/google_checks.xml")
    toolVersion = "9.0.1"
    sourceSets = [project.sourceSets.main]
}
  • toolVersion = "8.32"toolVersion = "9.0.1" に変更します。

https://github.com/ksby/ksbysample-webapp-lending/blob/master/config/checkstyle/google_checks.xml をコピーして config/checkstyle/google_checks.xml に上書きします。

Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。

clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると checkstyleMain タスクで警告が 10件出力されました。全て Javadoc コメントがありません。 [MissingJavadocType] で @interface と enum のクラスコメントがない箇所で出力されていたので、警告が出た箇所に Javadoc のコメントを追加します。

f:id:ksby:20211019025912p:plain

再度 clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると今度は警告なしで "BUILD SUCCESSFUL" が出力されました。

f:id:ksby:20211019031530p:plain

履歴

2021/10/19
初版発行。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その103 )( Eclipse Adoptium OpenJDK(Eclipse Temurin)を 11.0.12+7 → 17+35 へバージョンアップする )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その102 )( SpotBugs を 4.0.2 → 4.4.1 へ、PMD を 6.23.0 → 6.39.0 へ、error-prone を 2.3.4 → 2.9.0 へバージョンアップする ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • Eclipse Adoptium OpenJDK(Eclipse Temurin)を 11.0.12+7 → 17+35 へバージョンアップします。

参照したサイト・書籍

  1. Internal JDK Elements Strongly Encapsulated in JDK 17
    https://www.infoq.com/news/2021/06/internals-encapsulated-jdk17/

  2. How to avoid "Sharing is only supported for boot loader classes because bootstrap classpath has been appended" warning during debug with Java 11?
    https://newbedev.com/how-to-avoid-sharing-is-only-supported-for-boot-loader-classes-because-bootstrap-classpath-has-been-appended-warning-during-debug-with-java-11

目次

  1. Eclipse Adoptium OpenJDK(Eclipse Temurin)を 11.0.12+7 → 17+35 へバージョンアップする
  2. configureChromeDriverBinary タスクで java.lang.reflect.InvocationTargetException が発生してタスクが失敗する原因を調査する。。。が、対応方法が分からないので com.github.erdi.webdriver-binaries Plugin を外す
  3. test タスクで Caused by: org.modelmapper.internal.ErrorsException ... が発生するので ModelMapper を最新バージョンに上げる

手順

Eclipse Adoptium OpenJDK(Eclipse Temurin)を 11.0.12+7 → 17+35 へバージョンアップする

IntelliJ IDEA のメインメニューから「File」-「Project Structure...」を選択し、「Project Structure」ダイアログが表示されたら「Project SDK」で「17+35」を選択して「OK」ボタンをクリックします。

f:id:ksby:20211015223606p:plain

メイン画面に戻ると画面右下に「Indexing...」の表示が出るので、終了するまで待ちます。

build.gradle の以下の点を変更します。

sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
  • JavaVersion.VERSION_11JavaVersion.VERSION_17 に変更します。

Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。

clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると configureChromeDriverBinary タスクで失敗して BUILD FAILED が出力されました。

f:id:ksby:20211015225835p:plain

  • compileTestJava タスクで以下の WARNING が出力される。
WARNING: A terminally deprecated method in java.lang.System has been called
WARNING: System::setSecurityManager has been called by edu.umd.cs.findbugs.ba.jsr305.TypeQualifierValue (file:/C:/Users/root/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs/4.4.1/d50cf49af9b5074d5ab3d0f89889d753623f341e/spotbugs-4.4.1.jar)
WARNING: Please consider reporting this to the maintainers of edu.umd.cs.findbugs.ba.jsr305.TypeQualifierValue
WARNING: System::setSecurityManager will be removed in a future release
  • configureChromeDriverBinary タスクで java.lang.reflect.InvocationTargetException が発生してタスクが失敗する。
FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':configureChromeDriverBinary'.
> java.lang.reflect.InvocationTargetException

compileTestJava タスクの WARNING は SpotBugs が内部で System::setSecurityManager を呼び出しているのが原因のようなので、特に対応はせずにこのままとします。

ただし build.gradle を https://github.com/ksby/ksbysample-webapp-lending のものと比較して変更したい点があったので、以下のように変更します。

configurations {
    compileOnly.extendsFrom annotationProcessor

    // annotationProcessor と testAnnotationProcessor、compileOnly と testCompileOnly を併記不要にする
    testAnnotationProcessor.extendsFrom annotationProcessor
    testImplementation.extendsFrom compileOnly

    // for SpotBugs
    spotbugsStylesheets { transitive = false }
}

..........

dependencies {
    ..........

    // for SpotBugs
    spotbugs(configurations.spotbugsPlugins.dependencies)
    annotationProcessor("com.github.spotbugs:spotbugs-annotations:${spotbugs.toolVersion.get()}")
    spotbugsPlugins("com.h3xstream.findsecbugs:findsecbugs-plugin:1.11.0")

    ..........
}
  • configurations block の以下の点を変更します。
    • developmentOnly を削除します。
    • runtimeClasspath { extendsFrom developmentOnly }compileOnly.extendsFrom annotationProcessor に変更します。
  • dependencies block の以下の点を変更します。
    • compileOnly("com.github.spotbugs:spotbugs-annotations:${spotbugs.toolVersion.get()}")annotationProcessor("com.github.spotbugs:spotbugs-annotations:${spotbugs.toolVersion.get()}") に変更します。

configureChromeDriverBinary タスクで java.lang.reflect.InvocationTargetException が発生してタスクが失敗する原因を調査する。。。が、対応方法が分からないので com.github.erdi.webdriver-binaries Plugin を外す

build 時に --debug --stacktrace オプションを付けて実行し直してみます。

IntelliJ IDEA のメインメニューから「Run」-「Edit Configurations...」を選択して「Run/Debug Configurations」ダイアログを表示した後、「Gradle」-「boot-npm-geb-sample [build]」を選択後、画面右側の「Run」に --debug --stacktrace を追加します。

f:id:ksby:20211015234017p:plain

clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると Caused by: java.lang.IllegalAccessException: module jdk.proxy12 does not open jdk.proxy12 to unnamed module @1b604f19 が出力されました。

f:id:ksby:20211015234832p:plain

VM options に --add-opens=jdk.proxy12/jdk.proxy12=ALL-UNNAMED を指定してみます。

f:id:ksby:20211015235358p:plain f:id:ksby:20211016000107p:plain

clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると Caused by: java.lang.IllegalAccessException: module jdk.proxy4 does not open jdk.proxy4 to unnamed module @cb5822 が出力されたので、

f:id:ksby:20211016000636p:plain

VM options に --add-opens=jdk.proxy4/jdk.proxy4=ALL-UNNAMED を追加します。

f:id:ksby:20211016000831p:plain

clean タスク実行 → Rebuild Project 実行 → build タスクを実行しましたが、前と同じ Caused by: java.lang.IllegalAccessException: module jdk.proxy4 does not open jdk.proxy4 to unnamed module @cb5822 が出力されます。

f:id:ksby:20211016001405p:plain

これは Plugin の Java 17 対応待ちでしょうか。。。 対応方法が分からないので、今回は com.github.erdi.webdriver-binaries Plugin を外すことにします。

build.gradle で com.github.erdi.webdriver-binaries Plugin に関する記述をコメントアウトします。

plugins {
    id "java"
    id "groovy"
    id "eclipse"
    id "idea"
    id "org.springframework.boot" version "2.5.5"
    id "io.spring.dependency-management" version "1.0.11.RELEASE"
    id "net.ltgt.errorprone" version "2.0.2"
    id "checkstyle"
    id "com.github.spotbugs" version "4.7.7"
    id "pmd"
    id "com.github.node-gradle.node" version "3.1.1"
    id "com.gorylenko.gradle-git-properties" version "2.3.1"
// Java 17 で build タスクを実行すると java.lang.reflect.InvocationTargetException が発生し、
// 対応方法が不明なのでコメントアウトする
//    id "com.github.erdi.webdriver-binaries" version "2.6"
    id "org.seasar.doma.codegen" version "1.4.1"
}

..........

// Java 17 で build タスクを実行すると java.lang.reflect.InvocationTargetException が発生し、
// 対応方法が不明なのでコメントアウトする
//// https://github.com/webdriverextensions/webdriverextensions-maven-plugin-repository/blob/master/repository-3.0.json 参照
//webdriverBinaries {
//    chromedriver {
//        version = "94.0.4606.41"
//        architecture = "X86"
//        fallbackTo32Bit = true
//    }
//    geckodriver {
//        version = "0.29.1"
//        architecture = "X86_64"
//    }
//}
def drivers = ["chrome", "firefox"]
..........

またこのままでは WebDriver を認識できず gebTest タスクが成功しないので、src/test/resources/GebConfig.groovy の System.setProperty("webdriver.~ から始まる2行のコメントアウトを解除します。

System.setProperty("webdriver.gecko.driver", "D:/geckodriver/0.30.0/geckodriver.bat")
System.setProperty("webdriver.chrome.driver", "D:/chromedriver/94.0.4606.61/chromedriver.exe")
driver = {
    ChromeOptions chromeOptions = new ChromeOptions()
    new ChromeDriver(chromeOptions)
//    FirefoxOptions firefoxOptions = new FirefoxOptions()
//    new FirefoxDriver(firefoxOptions)
}
..........

build タスクに追加したオプションは全て削除します。

test タスクで Caused by: org.modelmapper.internal.ErrorsException ... が発生するので ModelMapper を最新バージョンに上げる

clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると、今度は test タスクでテストが大量に失敗しました。

f:id:ksby:20211017093537p:plain

失敗したテストでは Caused by: org.modelmapper.internal.ErrorsException at Errors.java:254 と出力されていました。

build.gradle では implementation("com.github.rozidan:modelmapper-spring-boot-starter:2.3.1") を指定するだけで ModelMapper のバージョンは明記しておらず、依存関係にある ModelMapper のバージョンを確認したところ 2.3.0 でしたので、最新バージョンの 2.4.4 を依存関係に追加してみることにします。

f:id:ksby:20211017094122p:plain

build.gradle に implementation("org.modelmapper:modelmapper:2.4.4") を追加します。

dependencies {
    ..........

    // dependency-management-plugin によりバージョン番号が自動で設定されないもの、あるいは最新バージョンを指定したいもの
    ..........
    implementation("com.github.rozidan:modelmapper-spring-boot-starter:2.3.1")
    implementation("org.modelmapper:modelmapper:2.4.4")
    implementation("com.google.guava:guava:31.0.1-jre")
    ..........

Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。

clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると無事 "BUILD SUCCESSFUL" が出力されました。

f:id:ksby:20211017095358p:plain

OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended のメッセージは test タスクの jvmArgs に -Xshare:off オプションを指定すれば出力されなくなりますが、今のままにします。

gebTest タスクも問題ありませんでした。

f:id:ksby:20211017101430p:plain

履歴

2021/10/17
初版発行。