web-dev-qa-db-ja.com

JAXBはTomcat 9では利用できず、Java 9/10

[〜#〜] tldr [〜#〜]:On Java 9/10、TomcatのWebアプリ参照実装がクラスパスに存在していても、JAXBにアクセスできません。

Edit:いいえ、これは Java.lang.NoClassDefFoundErrorの解決方法:javax/xml/bind/JAXBException in = Java 9 -私が試したことセクションでわかるように、私はすでに提案された解決策を試しました。

状況

Tomcatで実行され、JAXBに依存するWebアプリがあります。 Java 9への移行中に、 通常の依存関係としてのJAXB参照実装 の追加を選択しました。

IDE Tomcatが埋め込まれている) からアプリを起動するとすべてが機能しましたが、実際のTomcatインスタンスで実行すると、このエラーが発生します。

Caused by: Java.lang.RuntimeException: javax.xml.bind.JAXBException:
    Implementation of JAXB-API has not been found on module path or classpath.
 - with linked exception:
[Java.lang.ClassNotFoundException: com.Sun.xml.internal.bind.v2.ContextFactory]
    at [... our-code ...]
Caused by: javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath.
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.Java:278) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.ContextFinder.find(ContextFinder.Java:421) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.Java:721) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.Java:662) ~[jaxb-api-2.3.0.jar:2.3.0]
    at [... our-code ...]
Caused by: Java.lang.ClassNotFoundException: com.Sun.xml.internal.bind.v2.ContextFactory
    at jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.Java:582) ~[?:?]
    at jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.Java:190) ~[?:?]
    at Java.lang.ClassLoader.loadClass(ClassLoader.Java:499) ~[?:?]
    at javax.xml.bind.ServiceLoaderUtil.nullSafeLoadClass(ServiceLoaderUtil.Java:122) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.ServiceLoaderUtil.safeLoadClass(ServiceLoaderUtil.Java:155) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.Java:276) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.ContextFinder.find(ContextFinder.Java:421) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.Java:721) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.Java:662) ~[jaxb-api-2.3.0.jar:2.3.0]
    at [... our-code ...]

注意:

JAXB-APIの実装がモジュールパスまたはクラスパスに見つかりませんでした。

これらはwebapps/$app/WEB-INF/libの関連ファイルです:

jaxb-api-2.3.0.jar
jaxb-core-2.3.0.jar
jaxb-impl-2.3.0.jar

ここで何が起こっていますか?

私が試したもの

TomcaのCLASSPATHへのJARの追加

setenv.shにあるTomcatのクラスパスにJARを追加すると役立つかもしれません。

CLASSPATH=
    .../webapps/$app/WEB-INF/lib/jaxb-api-2.3.0.jar:
    .../webapps/$app/WEB-INF/lib/jaxb-impl-2.3.0.jar:
    .../webapps/$app/WEB-INF/lib/jaxb-core-2.3.0.jar:
    .../webapps/$app/WEB-INF/lib/javax.activation-1.2.0.jar

いいえ:

Caused by: javax.xml.bind.JAXBException: ClassCastException: attempting to cast
jar:file:.../webapps/$app/WEB-INF/lib/jaxb-api-2.3.0.jar!/javax/xml/bind/JAXBContext.class to
jar:file:.../webapps/$app/WEB-INF/lib/jaxb-api-2.3.0.jar!/javax/xml/bind/JAXBContext.class.
Please make sure that you are specifying the proper ClassLoader.    
    at javax.xml.bind.ContextFinder.handleClassCastException(ContextFinder.Java:157) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.Java:300) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.Java:286) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.ContextFinder.find(ContextFinder.Java:409) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.Java:721) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.Java:662) ~[jaxb-api-2.3.0.jar:2.3.0]
    at de.disy.gis.webmapserver.factory.DefaultWmsRequestFactory.initializeCommandExtractor(DefaultWmsRequestFactory.Java:103) ~[cadenza-gis-webmapserver-7.7-SNAPSHOT.jar:7.6]
    at de.disy.gis.webmapserver.factory.DefaultWmsRequestFactory.lambda$new$0(DefaultWmsRequestFactory.Java:87) ~[cadenza-gis-webmapserver-7.7-SNAPSHOT.jar:7.6]

これは明らかに同じクラスなので、明らかに2つのクラスローダーによってロードされています。 システムクラスローダーとアプリのクラスローダー が疑われるが、なぜJAXBContextの読み込みがシステムクラスローダーに一度だけ委任されるのか?プログラムの実行中に、アプリのクラスローダーの委任動作が変更されるように見えます。

モジュールを追加する

私は本当にJava.xml.bindを追加したくありませんが、とにかくこれをcatalina.shに追加して試してみました:

JDK_Java_OPTIONS="$JDK_Java_OPTIONS --add-modules=Java.xml.bind"

ただし、どちらも機能しません:

Caused by: Java.lang.ClassCastException:
Java.xml.bind/com.Sun.xml.internal.bind.v2.runtime.JAXBContextImpl
cannot be cast to com.Sun.xml.bind.v2.runtime.JAXBContextImpl
    at [... our-code ...]

異なるクラスとスタックトレースは別として、これは以前に起こったことと一致しています。クラスJAXBContextImplJava.xml.bind(システムクラスローダーである必要があります)ともう一度(私はJARからアプリのローダーによって仮定します)。

バグを検索する

Tomcatのバグデータベースを検索 見つかった #62559 。それは同じエラーでしょうか?

TomcatのlibにJARを追加する

Tomcatユーザーメーリングリストでのアドバイス に従って、JAXB JARをTomcatのCATALINA_BASE/libディレクトリに追加しましたが、アプリケーションのlibフォルダーと同じエラーが発生しました。

18
Nicolai

分析

最初にいくつかのランダムな事実:

Java 8:

  • クラスローダーをJAXB(oops)に渡さないため、スレッドのコンテキストクラスローダーを使用します。
  • 私たちの推測では、Tomcatは明示的にコンテキストクラスローダーを設定しないため、Tomcatをロードしたものと同じものになります:システムクラスローダー
  • システムクラスローダーがJDK全体を認識し、JAXB実装がそこに含まれるため

Java 9に入ります-ピアノの演奏が停止し、全員がスコッチを置きます:

  • 通常の依存関係としてのJAXB を追加したため、Webアプリのクラスローダーによってロードされます。
  • Java 8、JAXBはシステムクラスローダーを検索しますが、アプリのローダーは表示されません(逆方向のみ)
  • JAXBが実装を見つけられず、腹が立つ

解決

解決策は、JAXBが適切なクラスローダーを使用するようにすることです。次の3つの方法を知っています。

  • Thread.getCurrentThread().setContextClassLoader(this.getClass().getClassLoader());を呼び出しますが、それはあまり良い考えではありません
  • create a context resolver 、ただしJAX-WSが必要であり、悪を別のものに置き換えるような感覚
  • JAXBContext::newInstanceのパッケージ受け入れバリアントを使用します( Java EE 7 からのJavadoc)。これもクラスローダーを受け取り、正しいローダーを渡しますが、いくつかのリファクタリング

3番目のオプションを使用し、JAXBContext::newInstanceのパッケージ受け入れバリアントに向けてリファクタリングしました。面倒な作業ですが、問題は修正されました。

注意

ユーザーcurlals は重要な情報を提供しましたが、回答を削除しました。いくつかの編集を依頼したからではないことを願っています。すべてのクレジット/カルマはそれらに行く必要があります! @curlals:回答を復元および編集する場合、私はそれを受け入れ、賛成します。

9
Nicolai

以下とその依存関係を試してください。 最新バージョンのMavenリポジトリ を参照してください。

<dependency>
  <groupId>org.glassfish.jaxb</groupId>
  <artifactId>jaxb-runtime</artifactId>
  <version>2.3.0.1</version>
</dependency>

また、Java Service Loader記述子も含まれています。 Java 9+でのJAXBの使用 を参照してください

6