Java / Mavenで「Xerces hell」を処理しますか?


732

私のオフィスでは、Xercesという単語を単に言及するだけで、開発者の殺意を呼び起こします。SOに関する他のXercesの質問をざっと見ると、ほとんどすべてのMavenユーザーがこの問題にいつか「触れられている」ことを示しているようです。残念ながら、問題を理解するには、Xercesの歴史について少し知識が必要です...

歴史

  • Xercesは、Javaエコシステムで最も広く使用されているXMLパーサーです。Javaで作成されたほとんどすべてのライブラリまたはフレームワークは、Xercesをある程度の能力で使用しています(直接ではない場合でも、推移的に)。

  • 公式バイナリに含まれるXerces jar は、現在のところバージョン管理されていません。たとえば、Xerces 2.11.0実装jarには名前が付けられxercesImpl.jar、名前は付けられませんxercesImpl-2.11.0.jar

  • XercesチームはMavenを使用しません。つまり、公式リリースをMaven Centralにアップロードしません。

  • Xercesは以前は単一のjarxerces.jarとしてリリースされていましたが、1つはAPI(xml-apis.jar)を含み、もう1つはそれらのAPIの実装を含む()の2つのjarに分割されましたxercesImpl.jar。多くの古いMaven POMは、依然としてへの依存を宣言していxerces.jarます。過去のある時点で、XercesもとしてリリースされましたxmlParserAPIs.jar。これは、一部の古いPOMも依存しています。

  • jarをMavenリポジトリーにデプロイする人がxml-apisおよびxercesImpl jarに割り当てたバージョンは、多くの場合異なります。たとえば、どちらもXerces 2.8.0からのものであっても、xml-apisにはバージョン1.3.03が与えられ、xercesImplにはバージョン2.8.0が与えられる場合があります。これは、人々がxml-apis jarに実装する仕様のバージョンをタグ付けすることがよくあるためです。ここには非常に良いが不完全な内訳があります

  • さらに複雑なことに、Xercesは、JREに含まれているJava API for XML Processing(JAXP)のリファレンス実装で使用されるXMLパーサーです。実装クラスはcom.sun.*名前空間の下に再パッケージ化されているため、一部のJREでは使用できない可能性があるため、直接アクセスするのは危険です。ただし、すべてのXerces機能がjava.*およびjavax.*APIを介して公開されるわけではありません。たとえば、Xercesシリアル化を公開するAPIはありません。

  • 混乱する混乱に加えて、ほとんどすべてのサーブレットコンテナ(JBoss、Jetty、Glassfish、Tomcatなど)は、Xercesとともに1つ以上の/libフォルダで出荷されます。

問題

紛争解決

上記の理由のいくつか、またはおそらくすべてのために、多くの組織は、自分のPOMでXercesのカスタムビルドを発行および使用しています。小さなアプリケーションでMaven Centralのみを使用している場合、これは実際には問題ではありませんが、ArtifactoryまたはNexusが複数のリポジトリ(JBoss、Hibernateなど)をプロキシしているエンタープライズソフトウェアではすぐに問題になります。

Artifactoryによってプロキシされたxml-apis

たとえば、組織Aは次のように公開xml-apisします。

<groupId>org.apache.xerces</groupId>
<artifactId>xml-apis</artifactId>
<version>2.9.1</version>

一方、組織Bは次のものと同じものjarを公開する場合があります。

<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
<version>1.3.04</version>

B jarはAよりも低いバージョンですがjar、Mavenはが異なるため、それらが同じアーティファクトであることを認識していません groupId。したがって、競合の解決は実行できず、両方 jarのが解決された依存関係として含まれます。

複数のxml-apiで解決された依存関係

クラスローダー地獄

上記のように、JREはJAXP RIのXercesに同梱されています。すべてのXerces Maven依存関係を<exclusion>sまたは<provided>、依存するサードパーティのコードは、使用しているJDKのJAXPで提供されるバージョンで動作する場合と動作しない場合があります。さらに、Xerces jarがサーブレットコンテナに同梱されており、これに対処する必要があります。これにはいくつかの選択肢があります。サーブレットのバージョンを削除して、コンテナがJAXPバージョンで実行されることを望んでいますか?サーブレットバージョンを残し、アプリケーションフレームワークがサーブレットバージョンで実行されることを望んでいますか?上記で概説した1つまたは2つの未解決の競合が製品に侵入した場合(大規模な組織で起こりやすい)、クラスローダーの地獄にすぐに気づき、実行時にクラスローダーが選択しているXercesのバージョンと、そのバージョンかどうかを疑問に思います。 WindowsとLinuxで同じjarを選択します(おそらくそうではありません)。

ソリューション?

私たちは、全てのXerces Mavenの依存関係をマーキングしようとした<provided>かのように<exclusion>アーティファクトが非常に多くの別名(持っていることを考えると(特に大規模なチームで)が、これは強制することは困難であるxml-apisxercesxercesImplxmlParserAPIs、など)。さらに、サードパーティのlibs / frameworksは、JAXPバージョンまたはサーブレットコンテナによって提供されるバージョンでは実行できない場合があります。

Mavenでこの問題にどのように対処すればよいでしょうか?依存関係をこのように細かく制御してから、階層型クラスローディングに依存する必要がありますか?すべてのXerces依存関係をグローバルに除外し、すべてのフレームワーク/ライブラリに強制的にJAXPバージョンを使用させる方法はありますか?


更新:Joshua Spiewakが、XercesビルドスクリプトのパッチバージョンをXERCESJ-1454にアップロードしました。これにより、Maven Centralへのアップロードが可能になります。この問題に投票/監視/貢献し、この問題を一度だけ修正しましょう。


8
この詳細な質問をありがとう。xercesチームの動機がわかりません。私は彼らがその製品を誇りに思っており、他の製品を使用することに喜びを感じていると思いますが、xercesとmavenの現在の状態は不名誉です。たとえそれが私にとって意味がなくても、彼らは彼らが望むことをすることができます。sonatypeの人たちは何か提案があるのだろうか。
Travis Schneeberger 2012

35
これはおそらくトピックから外れていますが、これはおそらく私が今まで見た中でより良い投稿です。質問に関連して、あなたが説明することは私たちが遭遇することができる最も痛い問題の一つです。素晴らしいイニシアチブ!
ジャン・レミーレヴィ

2
@TravisSchneeberger複雑さの多くは、SunがJRE自体でXercesを使用することを選択したためです。そのためにXercesの人々を責めることはほとんどできません。
–ThorbjørnRavn Andersen 2014

通常、試行錯誤によってすべての依存ライブラリを満たすXercesのバージョンを見つけようとします。それができない場合は、WARにリファクタリングして、アプリケーションを個別のWAR(個別のクラスローダー)に分割します。このツールは、(私はそれを書いた)で何が起こっているか理解するのに役立ちますjhades.org jarファイルのクラスパスを照会できるようにすることによって、およびクラス-サーバが起動しないときには、まだの場合にも動作します
角度大学

Windowsでgit bashからservicemixを開始しているときにこのエラーが発生した場合の簡単なコメント:代わりに「通常の」cmdから開始してください。
Albert Hendriks

回答:


112

2013年2月20日以降、Maven CentralにはXercesの2.11.0 JAR (およびソースJAR!)があります!Maven CentralのXercesを参照してください。それらを解決していない私はなぜだろhttps://issues.apache.org/jira/browse/XERCESJ-1454を ...

私は使用しました:

<dependency>
    <groupId>xerces</groupId>
    <artifactId>xercesImpl</artifactId>
    <version>2.11.0</version>
</dependency>

そして、すべての依存関係は適切に解決されましたxml-apis-1.4.01

そして最も重要なこと(そして以前は明白でなかったこと)-Maven CentralのJARは公式Xerces-J-bin.2.11.0.zipディストリビューションのJARと同じです。

しかし、xml-schema-1.1-betaバージョンを見つけることができませんでした- classifier追加の依存関係があるため、Maven バージョンにすることはできません。


9
それより新しいの非常に混乱しxml-apis:xml-apis:1.4.01いますxml-apis:xml-apis:2.0.2?参照search.maven.org/...
Hendy Irawan

混乱を招きますが、ジャスティンガリクが彼の投稿で言っていたように、バージョン管理されていないXerces jarのサードパーティによるアップロードが原因です。xml-apis 2.9.1は1.3.04と同じなので、その意味で、1.4.01は1.3.04よりも新しい(そして数値的に大きい)です。
liltitus27 2013年

1
pom.xmlにxercesImplとxml-apisの両方がある場合、必ずxml-apis依存関係を削除してください!それ以外の場合、2.0.2は醜い頭を後ろに向けます。
MikeJRamsey56 2016年

64

率直に言って、私たちが遭遇したほとんどすべてがJAXPバージョンで問題なく機能するため常にとを除外 xml-apisxercesImplます。


13
そのためにpom.xmlスニペットを追加できますか?
chzbrgla 2013年

10
これを試すとjava.lang.NoClassDefFoundError: org/w3c/dom/ElementTraversal、実行時にJavaMelodyとSpringがスローされます。
David Moles、2014年

David Molesの応答に追加するには-半ダースの推移的な依存関係にElementTraversalが必要であることを確認しました。SpringとHadoopの最も一般的なさまざまなもの。
スコットキャリー2014年

2
java.lang.NoClassDefFoundError:org / w3c / dom / ElementTraversalが表示される場合は、xml-apis 1.4.01をpomに追加してみてください(他のすべての依存バージョンを除外してください)
Justin Rowe

1
ElementTraversalはXerces 11で追加された新しいクラスで、xml-apis:xml-apis:1.4.01依存関係で利用できます。したがって、クラスをプロジェクトに手動でコピーするか、依存関係全体を使用する必要がある場合があります。これにより、クラスローダーでクラスが重複します。ただし、JDK9にはこのクラスが含まれていたため、機能ではdepを削除する必要がある場合があります。
Sergey Ponomarev

42

Maven Enforcerプラグインと禁止された依存関係ルールを使用できます。これにより、不要なすべてのエイリアスを禁止し、必要なエイリアスのみを許可することができます。これらのルールに違反すると、プロジェクトのMavenビルドが失敗します。さらに、このルールが企業内のすべてのプロジェクトに適用される場合、プラグイン構成を企業の親pomに配置できます。

見る:


33

私はこれが質問に正確に答えていないことを知っていますが、たまたま依存関係管理のためにGradleを使用するgoogleから入ってくるpplの場合:

私はこのようにGradleですべてのxerces / Java8の問題をなんとか取り除くことができました。

configurations {
    all*.exclude group: 'xml-apis'
    all*.exclude group: 'xerces'
}

36
mavenでは、そのために約4000行のXMLが必要です。
teknopaul 2016

それは問題を解決しませんでした。Android-Gradleの人々のための他のヒントはありますか?
nyxee 2016年

2
@teknopaul XMLは、純粋に構成に使用されます。Groovyは高水準プログラミング言語です。場合によっては、マジックのグルーヴィーではなく、明示的なXMLを使用したいことがあります。
Dragas

16

答える必要のある質問が1つあると思います。

アプリケーションのすべてのものが一緒に使用できるxerces * .jarはありますか?

そうでなければ、基本的にねじ込まれ、OSGIのようなものを使用する必要があります。これにより、異なるバージョンのライブラリを同時にロードすることができます。それは基本的にjarバージョンの問題をクラスローダーの問題に置き換えることに注意してください...

そのようなバージョンが存在する場合、すべての種類の依存関係に対してリポジトリにそのバージョンを返すようにすることができます。これは醜いハックであり、クラスパスで同じxerces実装を複数回行うことになりますが、複数の異なるバージョンのxercesを使用するよりは優れています。

xercesへのすべての依存関係を除外し、使用するバージョンに1つ追加できます。

mavenのプラグインとして、ある種のバージョン解決戦略を記述できるかどうか疑問に思います。これはおそらく最も優れたソリューションですが、もし可能であれば、いくつかの調査とコーディングが必要です。

ランタイム環境に含まれるバージョンについては、それがアプリケーションのクラスパスから削除されるか、サーバーのlibフォルダーが考慮される前に、アプリケーションのjarがクラスローディングのために最初に考慮されることを確認する必要があります。

まとめると、混乱しますが、変更されません。


1
異なるClassLoadersによってロードされた同じjarからの同じクラスは、ClassCastExceptionです(すべての標準コンテナー)
Ajax

3
丁度。それが私が書いた理由です。それは基本的にjarバージョンの問題をクラスローダーの問題で置き換えることに注意してください
Jens Schauder

7

ここではまだ検討されていない別のオプションがあります。MavenでXercesの依存関係をオプションとして宣言します

<dependency>
   <groupId>xerces</groupId>
   <artifactId>xercesImpl</artifactId>
   <version>...</version>
   <optional>true</optional>
</dependency>

基本的にはこれが何をするかを宣言するために、すべての扶養を強制することです彼らのXercesのバージョンをまたはそのプロジェクトがコンパイルされません。この依存関係を上書きしたい場合は歓迎しますが、潜在的な問題を所有することになります。

これにより、下流のプロジェクトに次のような強いインセンティブが生まれます。

  • 積極的な決定を下します。彼らは同じバージョンのXercesを使用しますか、それとも他のものを使用しますか?
  • 実際にそれらの解析(ユニットテストなど)とクラスローディングをテストし、クラスパスを乱雑にしないようにします。

すべての開発者が新しく導入された依存関係を追跡するわけではありません(例:を使用mvn dependency:tree)。このアプローチはすぐに問題を彼らの注意にもたらします。

それは私たちの組織で非常にうまく機能します。紹介する前は、OPが説明しているのと同じ地獄に住んでいました。


バージョン要素内で文字通りドットドットドットを使用する必要がありますか、または2.6.2のような実際のバージョンを使用する必要がありますか?
chrisinmtown 2017

3
@chrisinmtown実際のバージョン。
ダニエル

6

すべてのmavenプロジェクトは、xercesに応じて停止する必要がありますが、実際には停止しません。XML APIとImplは、1.4以降Javaの一部となっています。XercesやXML APIに依存する必要はありません。JavaやSwingに依存していると言っているようなものです。これは暗黙的なものです。

私がMavenリポジトリのボスだった場合、xercesの依存関係を再帰的に削除するスクリプトを作成し、このリポジトリにはJava 1.4が必要であることを示すread meを書きます。

org.apacheインポートを介してXercesを直接参照するために実際に機能しないものは、コードを修正してJava 1.4レベル(2002年以降に実施)にするか、mavenではなく、承認済みのライブラリを介してJVMレベルで解決する必要があります。


詳細なリファクタリングを実行するときは、Javaファイルと構成のテキストでパッケージとクラスの名前も検索する必要があります。開発者は、ImplクラスのFQNを、Class.forNameおよび同様の構成体で使用される定数文字列に配置していることがわかります。
デレクベネット

これは、すべてのSAX実装が同じことを行うと想定していますが、これは正しくありません。xercesImplライブラリーは、java.xml.parserライブラリーにはない構成オプションを許可します。
アマルゴビナス

6

XML地獄のレベルを特定するために、最初にデバッグする必要があります。私の意見では、最初のステップは追加することです

-Djavax.xml.parsers.SAXParserFactory=com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
-Djavax.xml.transform.TransformerFactory=com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
-Djavax.xml.parsers.DocumentBuilderFactory=com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl

コマンドラインに。それが機能する場合は、ライブラリの除外を開始します。そうでない場合は、追加します

-Djaxp.debug=1

コマンドラインに。


2

除外する以外に役立つのは、モジュール依存関係です。

1つのフラットクラスローディング(スタンドアロンアプリ)、または半階層(JBoss AS / EAP 5.x)では、これは問題でした。

しかし、OSGiJBoss Modulesなどのモジュール式フレームワークを使用すると、これはそれほど苦痛ではなくなります。ライブラリは、必要に応じて独立してライブラリを使用できます。

もちろん、単一の実装とバージョンのみを使用することをお勧めしますが、他の方法がない場合(より多くのライブラリの追加機能を使用)、モジュール化することで節約できる可能性があります。

実際のJBossモジュールの良い例は、当然、JBoss AS 7 / EAP 6 / WildFly 8であり、主に開発されました。

モジュール定義の例:

<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.jboss.msc">
    <main-class name="org.jboss.msc.Version"/>
    <properties>
        <property name="my.property" value="foo"/>
    </properties>
    <resources>
        <resource-root path="jboss-msc-1.0.1.GA.jar"/>
    </resources>
    <dependencies>
        <module name="javax.api"/>
        <module name="org.jboss.logging"/>
        <module name="org.jboss.modules"/>
        <!-- Optional deps -->
        <module name="javax.inject.api" optional="true"/>
        <module name="org.jboss.threads" optional="true"/>
    </dependencies>
</module>

OSGiと比較して、JBoss Modulesはシンプルで高速です。特定の機能はありませんが、(主に)1つのベンダーの管理下にあるほとんどのプロジェクトには十分であり、(並列化された依存関係の解決により)見事な高速ブートが可能です。

注意がありますことをモジュール化の努力は、Java 8のために進行中の JRE自体をモジュール化するために、主だが、私の知る限りではなく、必ずそれがアプリケーションに適用されますか。


jbossモジュールは静的モジュール化についてです。OSGiが提供しなければならないランタイムモジュール化とはほとんど関係がありません。私は、それらが互いに補完し合っていると思います。それは素晴らしいシステムです。
2014年

*賛辞ではなく補完
ロバートマイク2015年

2

どうやらxerces:xml-apis:1.4.01Maven Centralにはもはや存在しないようですが、それがxerces:xercesImpl:2.11.0参照するものです。

これは私にとってはうまくいきます:

<dependency>
  <groupId>xerces</groupId>
  <artifactId>xercesImpl</artifactId>
  <version>2.11.0</version>
  <exclusions>
    <exclusion>
      <groupId>xerces</groupId>
      <artifactId>xml-apis</artifactId>
    </exclusion>
  </exclusions>
</dependency>
<dependency>
  <groupId>xml-apis</groupId>
  <artifactId>xml-apis</artifactId>
  <version>1.4.01</version>
</dependency>

1

とてもシンプルな私の友人、ここに例を示します:

<dependency>
    <groupId>xalan</groupId>
    <artifactId>xalan</artifactId>
    <version>2.7.2</version>
    <scope>${my-scope}</scope>
    <exclusions>
        <exclusion>
        <groupId>xml-apis</groupId>
        <artifactId>xml-apis</artifactId>
    </exclusion>
</dependency>

そして、ターミナル(この例ではWindowsコンソール)で、Mavenツリーに問題がないことを確認したい場合は、次のようにします。

mvn dependency:tree -Dverbose | grep --color=always '(.* conflict\|^' | less -r
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.