Spring Boot 2.0.x の Web アプリを 2.1.x へバージョンアップする ( その15 )( JDK を 8u202 → 11.0.2+9 に変更する )
概要
記事一覧はこちらです。
Spring Boot 2.0.x の Web アプリを 2.1.x へバージョンアップする ( その14 )( Docker で複数の Tomcat を起動して動作確認する ) の続きです。
- 今回の手順で確認できるのは以下の内容です。
- プロジェクトで使用する JDK を 8u202 → 11.0.2+9 に変更します。
参照したサイト・書籍
AdoptOpenJDK
https://adoptopenjdk.net/index.html?variant=openjdk11&jvmVariant=hotspotPowermock 2.0.0-beta.5 problem with jdk9 modules
https://github.com/powermock/powermock/issues/864JDK 9への移行 - ランタイム・アクセス警告の理解
https://docs.oracle.com/javase/jp/9/migrate/toc.htm#GUID-7BB28E4D-99B3-4078-BDC4-FC24180CE82B
目次
- AdoptOpenJDK の jdk-11.0.2+9 をインストールする
- ksbysample-webapp-lending プロジェクトが使用する JDK を jdk-11.0.2+9 に変更する
- build.gradle を変更する
- clean タスク → Rebuild Project → build タスクを実行してみる
- build 時のエラーの原因を調査する
SampleHelperTest$異常処理のテスト.SampleHelper_encryptを呼ぶとRuntimeExceptionをthrowする
テストの java.lang.IllegalAccessError- WARNING: Illegal reflective access by org.codehaus.groovy.vmplugin.v7.Java7$1 (file:/C:/Users/root/.gradle/caches/modules-2/files-2.1/org.codehaus.groovy/groovy/2.5.6/6936e700f0fb1b50bac0698ada4347a769d40199/groovy-2.5.6.jar) to constructor java.lang.invoke.MethodHandles$Lookup(java.lang.Class,int)
- 続きます。。。
手順
AdoptOpenJDK の jdk-11.0.2+9 をインストールする
https://adoptopenjdk.net/index.html にアクセスした後、「OpenJDK 11 (LTS)」「HotSpot」を選択して「Latest release jdk-11.0.2+9」のボタンをクリックし OpenJDK11U-jdk_x64_windows_hotspot_11.0.2_9.zip をダウンロードします。
OpenJDK11U-jdk_x64_windows_hotspot_11.0.2_9.zip を解凍して作成された jdk-11.0.2+9 ディレクトリを D:\Java\ の下へ移動します。
環境変数 JAVA_HOME のパスを D:\Java\jdk-11.0.2+9 へ変更します。
コマンドプロンプトから java -version を実行し、11.0.2+9 に変更されていることを確認します。
ksbysample-webapp-lending プロジェクトが使用する JDK を jdk-11.0.2+9 に変更する
ksbysample-webapp-lending プロジェクトを開いた状態で IntelliJ IDEA のメインメニューから「File」-「Project Structure...」を選択します。
「Project Structure」ダイアログが表示されます。画面左側で「Project Settings」-「Project」を選択後、画面右側の「Project SDK」の「New...」ボタンをクリックし、表示されるメニューから「JDK」を選択します。
「Select Home Directory for JDK」ダイアログが表示されます。D:\Java\jdk-11.0.2+9 を選択した後、「OK」ボタンをクリックします。
「Default Project Structure」ダイアログに戻るので、今度は「Project SDK」の「Edit」ボタンをクリックします。
画面左側で「Platform Settings」-「SDKs」が選択された状態になるので、画面右上の入力フィールドで "11" → "11.0.2+9" へ変更します。
「OK」ボタンをクリックして「Project Structure」ダイアログを閉じます。
メイン画面に戻ると画面右下に「Indexing...」の表示が出るので、終了するまで待ちます。
build.gradle を変更する
build.gradle の以下の点を変更します。
plugins { .......... } sourceCompatibility = 11 targetCompatibility = 11 wrapper { gradleVersion = "5.2.1" distributionType = Wrapper.DistributionType.ALL } ..........
sourceCompatibility = 1.8
→sourceCompatibility = 11
に変更します。targetCompatibility = 1.8
→targetCompatibility = 11
に変更します。
変更後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
clean タスク → Rebuild Project → build タスクを実行してみる
clean タスク → Rebuild Project は何のエラーも出ずに終了しましたが、build タスクを実行すると BUILD FAILED になりました。
以下の警告が出ており、
WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by org.codehaus.groovy.vmplugin.v7.Java7$1 (file:/C:/Users/root/.gradle/caches/modules-2/files-2.1/org.codehaus.groovy/groovy/2.5.6/6936e700f0fb1b50bac0698ada4347a769d40199/groovy-2.5.6.jar) to constructor java.lang.invoke.MethodHandles$Lookup(java.lang.Class,int) WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.vmplugin.v7.Java7$1 WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release
テストが1つ失敗しています。
ksbysample.webapp.lending.SampleHelperTest > ksbysample.webapp.lending.SampleHelperTest$異常処理のテスト.SampleHelper_encryptを呼ぶとRuntimeExceptionをthrowする FAILED java.lang.IllegalAccessError
build 時の警告とエラーの原因を調査する
SampleHelperTest$異常処理のテスト.SampleHelper_encryptを呼ぶとRuntimeExceptionをthrowする
テストの java.lang.IllegalAccessError
失敗したテストは src/test/groovy/ksbysample/webapp/lending/SampleHelperTest.groovy の以下のクラスでした。
@RunWith(PowerMockRunner) @PowerMockRunnerDelegate(SpringRunner) @SpringBootTest @PrepareForTest(BrowfishUtils) @PowerMockIgnore("javax.management.*") static class 異常処理のテスト { @Autowired private SampleHelper sampleHelper @Test void "SampleHelper_encryptを呼ぶとRuntimeExceptionをthrowする"() { // setup: PowerMockito.mockStatic(BrowfishUtils) PowerMockito.when(BrowfishUtils.encrypt(Mockito.any())) .thenThrow(new NoSuchPaddingException()) // expect: assertThatExceptionOfType(RuntimeException).isThrownBy({ sampleHelper.encrypt("test") }) } }
テストを直接実行してみると、
java.lang.IllegalAccessError: class javax.xml.parsers.FactoryFinder (in unnamed module @0x89c65d5) cannot access class jdk.xml.internal.SecuritySupport (in module java.xml) because module java.xml does not export jdk.xml.internal to unnamed module @0x89c65d5
のエラーメッセージが出力されています。
Web で調べてみると Powermock 2.0.0-beta.5 problem with jdk9 modules の Issue が見つかりました。@PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "org.w3c.dom.*"})
を指定すればエラーを回避できるようです。
src/test/groovy/ksbysample/webapp/lending/SampleHelperTest.groovy を以下のように変更します。
@RunWith(PowerMockRunner) @PowerMockRunnerDelegate(SpringRunner) @SpringBootTest @PrepareForTest(BrowfishUtils) @PowerMockIgnore(["javax.management.*", "com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "org.w3c.dom.*"]) static class 異常処理のテスト {
@PowerMockIgnore("javax.management.*")
→@PowerMockIgnore(["javax.management.*", "com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "org.w3c.dom.*"])
に変更します。groovy なのでアノテーションの配列の指定は{ ... }
ではなく[ ... ]
になります。
テストを実行すると今度は成功しました。
またこの時点で再度 clean タスク → Rebuild Project → build タスクを実行してみると、警告は出たままですが 最後に BUILD SUCCESSFUL が出力されるようになりました。
WARNING: Illegal reflective access by org.codehaus.groovy.vmplugin.v7.Java7$1 (file:/C:/Users/root/.gradle/caches/modules-2/files-2.1/org.codehaus.groovy/groovy/2.5.6/6936e700f0fb1b50bac0698ada4347a769d40199/groovy-2.5.6.jar) to constructor java.lang.invoke.MethodHandles$Lookup(java.lang.Class,int)
JDK 9への移行 - ランタイム・アクセス警告の理解 によると、警告が出ているのは JDKの内部部分にアクセスするためにコードでリフレクションが使用されているためとのことです。
例えば上の WARNING だと org.codehaus.groovy.vmplugin.v7.Java7$1 が java.lang.invoke.MethodHandles$Lookup にリフレクションでアクセスしていることが原因で、--add-opens java.base/java.lang.invoke=ALL-UNNAMED
を JVM のオプションに指定すれば警告が出なくなります。
build タスクを実行し、WARNING が出たら --add-opens java.base/.....=ALL-UNNAMED
オプションを追加するを繰り返したところ、groovy で6個、powermock で1個の WARNING が出力されました。
WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass (file:/C:/Users/root/.gradle/caches/modules-2/files-2.1/org.codehaus.groovy/groovy/2.5.6/6936e700f0fb1b50bac0698ada4347a769d40199/groovy-2.5.6.jar) to method java.lang.Object.finalize()
WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass (file:/C:/Users/root/.gradle/caches/modules-2/files-2.1/org.codehaus.groovy/groovy/2.5.6/6936e700f0fb1b50bac0698ada4347a769d40199/groovy-2.5.6.jar) to method java.util.AbstractCollection.hugeCapacity(int)
WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass (file:/C:/Users/root/.gradle/caches/modules-2/files-2.1/org.codehaus.groovy/groovy/2.5.6/6936e700f0fb1b50bac0698ada4347a769d40199/groovy-2.5.6.jar) to method java.lang.reflect.AnnotatedElement.lambda$getDeclaredAnnotationsByType$0(java.lang.annotation.Annotation,java.lang.annotation.Annotation)
WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass (file:/C:/Users/root/.gradle/caches/modules-2/files-2.1/org.codehaus.groovy/groovy/2.5.6/6936e700f0fb1b50bac0698ada4347a769d40199/groovy-2.5.6.jar) to method java.security.SecureClassLoader.getProtectionDomain(java.security.CodeSource)
WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedConstructor$1 (file:/C:/Users/root/.gradle/caches/modules-2/files-2.1/org.codehaus.groovy/groovy/2.5.6/6936e700f0fb1b50bac0698ada4347a769d40199/groovy-2.5.6.jar) to constructor java.io.File(java.lang.String,java.io.File)
WARNING: Illegal reflective access by org.powermock.reflect.internal.WhiteboxImpl (file:/C:/Users/root/.gradle/caches/modules-2/files-2.1/org.powermock/powermock-reflect/2.0.0/cd452bc345ec9f88ec5efecd41139de0cb1d4265/powermock-reflect-2.0.0.jar) to method java.lang.ref.Reference.clone()
WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass (file:/C:/Users/root/.gradle/caches/modules-2/files-2.1/org.codehaus.groovy/groovy/2.5.6/6936e700f0fb1b50bac0698ada4347a769d40199/groovy-2.5.6.jar) to field java.net.SocketTimeoutException.serialVersionUID
--add-opens java.base/.....=ALL-UNNAMED
オプションは build.gradle に以下のように定義します。
def jvmArgsForTask = [ "-ea", "-Dfile.encoding=UTF-8", "-Dsun.nio.cs.map=x-windows-iso2022jp/ISO-2022-JP" ] // JDK 11 に変更後、test タスク実行時に groovy と powermock が JDKの内部部分にアクセスするためにコードでリフレクションを使用 // していて WARNING が出るため、JVM の起動時オプションの --add-opens を指定して WARNING が出ないようにする def jvmArgsAddOpens = [ "--add-opens=java.base/java.io=ALL-UNNAMED", "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.lang.invoke=ALL-UNNAMED", "--add-opens=java.base/java.lang.ref=ALL-UNNAMED", "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED", "--add-opens=java.base/java.net=ALL-UNNAMED", "--add-opens=java.base/java.security=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED" ] 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)" } } // -ea -Dspring.profiles.active=develop -Dfile.encoding=UTF-8 -Dsun.nio.cs.map=x-windows-iso2022jp/ISO-2022-JP bootRun { jvmArgs = jvmArgsForTask + ["-Dspring.profiles.active=develop"] } // -ea -Dspring.profiles.active=unittest -Dfile.encoding=UTF-8 -Dsun.nio.cs.map=x-windows-iso2022jp/ISO-2022-JP task testJUnit4AndSpock(type: Test) { jvmArgs = jvmArgsForTask + jvmArgsAddOpens + ["-Dspring.profiles.active=unittest"] testLogging { afterSuite printTestCount } } test.dependsOn testJUnit4AndSpock test { jvmArgs = jvmArgsForTask + jvmArgsAddOpens + ["-Dspring.profiles.active=unittest"] // for JUnit 5 useJUnitPlatform() testLogging { afterSuite printTestCount } }
def jvmArgsAddOpens = [ ... ]
を追加します。- testJUnit4AndSpock タスクと test タスク内の jvmArgs に値をセットしている箇所に
jvmArgsAddOpens +
を追加します。
clean タスク → Rebuild Project → build タスクを実行すると WARNING が出なくなりました。
また IntelliJ IDEA の Edit Configuration で JUnit の VM Options にも --add-opens java.base/.....=ALL-UNNAMED
オプションを指定する必要があります。
VM Options に -ea -Dspring.profiles.active=unittest -Dfile.encoding=UTF-8 -Dsun.nio.cs.map=x-windows-iso2022jp/ISO-2022-JP
のみで --add-opens java.base/.....=ALL-UNNAMED
オプションを1つも設定していないと、
Project Tool Window で src/test/groovy/ksbysample と src/test/java/ksbysample でコンテキストメニューを表示して「Run 'Tests in ''ksbysample'」を選択してテストを実行するとテストは全て成功しますが、WARNING: An illegal reflective access operation has occurred
のメッセージが表示されます。
JUnit の VM Options を -ea -Dspring.profiles.active=unittest -Dfile.encoding=UTF-8 -Dsun.nio.cs.map=x-windows-iso2022jp/ISO-2022-JP -ea -Dspring.profiles.active=unittest -Dfile.encoding=UTF-8 -Dsun.nio.cs.map=x-windows-iso2022jp/ISO-2022-JP --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.lang.ref=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED
に変更するとメッセージが表示されなくなります。
続きます。。。
思ったより簡単に JDK 11 へ切り替えられました。引っかかったのは groovy か powermock 関連でした。
次回は app の Docker イメージを JDK 11 ベースに作り直してから Tomcat を起動して動作確認を行います。またサービスからも起動して動作確認します。
履歴
2019/03/16
初版発行。
2019/03/30
* build.gradle を変更する を追加しました。
* IntelliJ IDEA の Edit Configuration で JUnit の VM Options には
--add-opens java.base/.....=ALL-UNNAMEDオプションを指定する必要はありません。
と書いていましたが、指定する必要があったので修正しました。