Spring Boot + npm + Geb で入力フォームを作ってテストする ( その72 )( Executable Jar ファイルを作成しても実行できない原因を調査する+気づいた点を修正する )
概要
記事一覧はこちらです。
Spring Boot + npm + Geb で入力フォームを作ってテストする ( その71 )( Geb で入力画面1~3→確認画面→完了画面を通したテストを作成する ) の続きです。
- 今回の手順で確認できるのは以下の内容です。
- 実装すべきことが全て完了したので jar ファイルを作成して実行できることを確認しようとしたのですが、なぜか実行できませんでした。原因を調査します。
- 他に何点か気づいた点を修正します。
参照したサイト・書籍
目次
- Executable Jar ファイルを作成しても実行できない原因を調査する
- どのような問題が発生しているのか?
- build.gradle の mainClass を修正して再度実行する
java.lang.ClassNotFoundException: org.springframework.boot.SpringApplication
の原因とは?- build.gradle の dependencies で
implementation
→compile
に戻してみる - Spring Initializr を利用して Spring Boot 2.0.3 のプロジェクトを作って検証する
- Spring Initializr を利用して Spring Boot 1.5.14 のプロジェクトを作って検証する
- 結論
- build.gradle を変更する+動作確認
- 気づいた点を修正する
手順
Executable Jar ファイルを作成しても実行できない原因を調査する
どのような問題が発生しているのか?
clean タスク実行 → Rebuild Project 実行 → build タスクを実行して、build/libs の下に boot-npm-geb-sample-1.0.1-RELEASE.jar を作成します。
コマンドプロンプトから java -Dspring.profiles.active=develop -jar build\libs\boot-npm-geb-sample-1.0.1-RELEASE.jar
を実行すると Exception in thread "main" java.lang.ClassNotFoundException: ksbysample.webapp.lending.Application
というエラーメッセージが出力されて実行できませんでした。Application クラスのパッケージが lending になっているということは、build.gradle を修正するのを忘れていますね。。。
build.gradle の mainClass を修正して再度実行する
build.gradle の以下の点を変更します。
bootRepackage {
mainClass = 'ksbysample.webapp.bootnpmgeb.Application'
}
- bootRepackage の mainClass の設定を
ksbysample.webapp.lending.Application
→ksbysample.webapp.bootnpmgeb.Application
に変更します。
jar ファイルを作成し直してから java -Dspring.profiles.active=develop -jar build\libs\boot-npm-geb-sample-1.0.1-RELEASE.jar
を実行すると、今度は java.lang.ClassNotFoundException: org.springframework.boot.SpringApplication
のエラーメッセージが出力されて実行できません。
java.lang.ClassNotFoundException: org.springframework.boot.SpringApplication
の原因とは?
Spring Boot の jar ファイルが Executable Jar ファイル内に出力されていない? ExecutableJar ファイルの中を見てみると、BOOT-INF の下に lib ディレクトリが存在しないことが分かりました。
boot-npm-geb-sample-1.0.1-RELEASE.jar の BOOT-INF の下を見ると、以下のように lib ディレクトリがありませんが、
java -jar
コマンドで実行できる ksbysample-webapp-lending の ksbysample-webapp-lending-1.5.4-RELEASE.jar を開いてみると、BOOT-INF/lib ディレクトリが存在し、その下に jar ファイルが格納されています。
build.gradle の dependencies で implementation
→ compile
に戻してみる
原因がよく分からなかったのですが、build.gradle を見ていて、そう言えば Spring Boot + npm + Geb で入力フォームを作ってテストする ( その65 )( Gradle を 4.6 → 4.8.1 へ、Checkstyle を 8.8 → 8.11 へ、PMD を 6.4.0 → 6.5.0 へ、error-prone を 2.2.0 → 2.3.1 へバージョンアップする ) で依存関係の記述の仕方を compile
→ implementation
に変更したことを思い出しました。これが原因のような気がします。一旦 compile
に戻してみます。
build.gradle で1つだけ implementation
→ compile
に戻してみます。
dependencies { .......... // dependency-management-plugin によりバージョン番号が自動で設定されるもの // Appendix F. Dependency versions ( https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/html/appendix-dependency-versions.html ) 参照 compile("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.boot:spring-boot-starter-thymeleaf") { exclude group: "org.codehaus.groovy", module: "groovy" }
Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
jar ファイルを作成し直して開いてみると、今度は BOOT-INF/lib ディレクトリが存在し、その下に jar ファイルが格納されていました。
ということは、Spring Boot Gradle Plugin が implementation
に対応していないということでしょうか。。。
Spring Initializr を利用して Spring Boot 2.0.3 のプロジェクトを作って検証する
Spring Initializr を利用してプロジェクトを作成して、build.gradle に implementation
で記述していると Executable Jar ファイルを作成した時に BOOT-INF/libs を作成してくれるのか否かを検証してみます。まずは 2系の最新バージョンである 2.0.3 から。
IntelliJ IDEA で Spring Initializr を利用してプロジェクトを作成します。
プロジェクトが作成されたら build.gradle を開き、compile
→ implementation
、runtime
→ runtimeOnly
、testCompile
→ testImplementation
に変更します。あと devtools が runtime
で記述されていることに気づきました。あとで反映しましょう。
buildscript { ext { springBootVersion = '2.0.3.RELEASE' } repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'org.springframework.boot' apply plugin: 'io.spring.dependency-management' group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = 1.8 repositories { mavenCentral() } dependencies { implementation('org.springframework.boot:spring-boot-starter-web') runtimeOnly('org.springframework.boot:spring-boot-devtools') testImplementation('org.springframework.boot:spring-boot-starter-test') }
Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新した後、build タスクを実行して jar ファイルを作成します。2系から demo-0.0.1-SNAPSHOT.jar.original って出来なくなったんですね。
作成された jar ファイルを開くと BOOT-INF/lib ディレクトリが存在し、その下に jar ファイルが出力されていますね。。。?
Spring Initializr を利用して Spring Boot 1.5.14 のプロジェクトを作って検証する
1.5.14 のプロジェクトも作成してみます。
IntelliJ IDEA で Spring Initializr を利用してプロジェクトを作成します。
プロジェクトが作成されたら build.gradle を開き、compile
→ implementation
、runtime
→ runtimeOnly
、testCompile
→ testImplementation
に変更します。
buildscript { ext { springBootVersion = '1.5.14.RELEASE' } repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'org.springframework.boot' group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = 1.8 repositories { mavenCentral() } dependencies { implementation('org.springframework.boot:spring-boot-starter-web') runtimeOnly('org.springframework.boot:spring-boot-devtools') testImplementation('org.springframework.boot:spring-boot-starter-test') }
Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新した後、build タスクを実行して jar ファイルを作成します。
作成された jar ファイルを開くと BOOT-INF/lib ディレクトリが存在しませんでした。
結論
- 2系の Spring Boot Gradle Plugin は build.gradle の
implementation
に対応しているが、1系の Spring Boot Gradle Plugin は対応していない。 - 1系で build.gradle の dependencies に
compile
ではなくimplementation
で記述すると、Executable Jar ファイルを作成した時に BOOT-INF/lib ディレクトリが作成されず外部モジュールの jar ファイルが格納されないため、java -jar
コマンドで実行しようとしても実行できない。
build.gradle を変更する+動作確認
build.gradle の dependencies の以下の点を変更します。
dependencies { def spockVersion = "1.1-groovy-2.4" def domaVersion = "2.19.2" def lombokVersion = "1.18.0" def errorproneVersion = "2.3.1" def powermockVersion = "1.7.4" def seleniumVersion = "3.13.0" // dependency-management-plugin によりバージョン番号が自動で設定されるもの // Appendix F. Dependency versions ( https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/html/appendix-dependency-versions.html ) 参照 compile("org.springframework.boot:spring-boot-starter-web") compile("org.springframework.boot:spring-boot-starter-thymeleaf") { exclude group: "org.codehaus.groovy", module: "groovy" } compile("org.springframework.boot:spring-boot-starter-data-jpa") compile("org.springframework.boot:spring-boot-starter-freemarker") compile("org.springframework.boot:spring-boot-starter-mail") compile("org.springframework.boot:spring-boot-starter-security") runtimeOnly("org.springframework.boot:spring-boot-devtools") compile("org.springframework.session:spring-session") compile("org.codehaus.janino:janino") testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.springframework.security:spring-security-test") testImplementation("org.yaml:snakeyaml") testImplementation("org.mockito:mockito-core") // dependency-management-plugin によりバージョン番号が自動で設定されないもの、あるいは最新バージョンを指定したいもの compile("com.integralblue:log4jdbc-spring-boot-starter:1.0.2") compile("org.flywaydb:flyway-core:5.1.4") compile("com.h2database:h2:1.4.192") compile("com.github.rozidan:modelmapper-spring-boot-starter:1.0.0") compile("com.google.guava:guava:25.1-jre") compile("org.apache.commons:commons-lang3:3.7") testImplementation("org.dbunit:dbunit:2.5.4") testImplementation("org.assertj:assertj-core:3.10.0") testImplementation("org.spockframework:spock-core:${spockVersion}") testImplementation("org.spockframework:spock-spring:${spockVersion}") testImplementation("com.google.code.findbugs:jsr305:3.0.2") testImplementation("org.jsoup:jsoup:1.11.3") testImplementation("com.icegreen:greenmail:1.5.7") // for lombok annotationProcessor("org.projectlombok:lombok:${lombokVersion}") compileOnly("org.projectlombok:lombok:${lombokVersion}") // for Doma annotationProcessor("org.seasar.doma:doma:${domaVersion}") compile("org.seasar.doma:doma:${domaVersion}") domaGenRuntime("org.seasar.doma:doma-gen:${domaVersion}") domaGenRuntime("com.h2database:h2:1.4.192") // for Error Prone ( http://errorprone.info/ ) errorprone("com.google.errorprone:error_prone_core:${errorproneVersion}") compileOnly("com.google.errorprone:error_prone_annotations:${errorproneVersion}") // PowerMock testImplementation("org.powermock:powermock-module-junit4:${powermockVersion}") testImplementation("org.powermock:powermock-api-mockito:${powermockVersion}") // for Geb + Spock testImplementation("org.gebish:geb-spock:2.1") testImplementation("org.seleniumhq.selenium:selenium-chrome-driver:${seleniumVersion}") testImplementation("org.seleniumhq.selenium:selenium-firefox-driver:${seleniumVersion}") testImplementation("org.seleniumhq.selenium:selenium-support:${seleniumVersion}") testImplementation("org.seleniumhq.selenium:selenium-api:${seleniumVersion}") testImplementation("org.seleniumhq.selenium:selenium-remote-driver:${seleniumVersion}") }
implementation
→compile
に戻します。compile("org.springframework.boot:spring-boot-devtools")
→runtimeOnly("org.springframework.boot:spring-boot-devtools")
に変更します。
Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
clean タスク実行 → Rebuild Project 実行 → build タスクを実行して、build/libs の下に boot-npm-geb-sample-1.0.1-RELEASE.jar を作成します。boot-npm-geb-sample-1.0.1-RELEASE.jar を開くと BOOT-INF/lib ディレクトリが存在し jar ファイルが格納されていました。
コマンドプロンプトから java -Dspring.profiles.active=develop -jar build\libs\boot-npm-geb-sample-1.0.1-RELEASE.jar
を実行すると今度は正常に起動できました。
気づいた点を修正する
PowerMockito#verifyStatic(VerificationMode)
が deprecated になっていたので PowerMockito#verifyStatic(Class, VerificationMode)
に変更する
見つけたのは偶然ですが、InquiryInput02FormValidatorTest クラスの "メールアドレスの Validation のテスト"()
テストメソッドで PowerMockito#verifyStatic(VerificationMode)
を呼び出しているところに取り消し線が引かれていました。deprecated になっており、PowerMockito#verifyStatic(Class, VerificationMode)
に変更するよう PowerMockito#verifyStatic(VerificationMode)
の JavaDoc に記述されていましたので、変更します。
src/test/groovy/ksbysample/webapp/bootnpmgeb/web/inquiry/form/InquiryInput02FormValidatorTest.groovy の以下の点を変更します。
@Test void "メールアドレスの Validation のテスト"() { .......... then: "入力チェックエラーは発生しない" assert errors.hasErrors() == false assert errors.getAllErrors().size() == 0 // EmailValidator.validate が呼び出されていることをチェックする PowerMockito.verifyStatic(EmailValidator, Mockito.times(1)) EmailValidator.validate("taro.tanaka@sample.co.jp") .......... }
PowerMockito.verifyStatic(Mockito.times(1))
→PowerMockito.verifyStatic(EmailValidator, Mockito.times(1))
に変更します。
履歴
2018/07/23
初版発行。