web-dev-qa-db-ja.com

Java/Mavenで「Xerces hell」を処理する

私のオフィスでは、Word 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は以前は 単一のjarとしてリリースされたxerces.jar)でしたが、2つのjarに分割されました。1つはAPI(xml-apis.jar)を含み、もう1つはそれらのAPIの実装(xercesImpl.jar)を含みます。多くの古いMaven POMはまだxerces.jarへの依存を宣言しています。過去のある時点で、XercesもxmlParserAPIs.jarとしてリリースされました。これは一部の古いPOMにも依存しています。

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

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

  • さらに混乱を招くように、ほとんどすべてのサーブレットコンテナ(JBoss、Jetty、Glassfish、Tomcatなど)は、1つ以上の/libフォルダにXercesが同梱されています。

問題

紛争解決

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

xml-apis proxied by Artifactory

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

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

一方、組織Bは次のように同じjarname__を公開することがあります。

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

Bのjarname__は、Aのjarname__よりも低いバージョンですが、Mavenは、それらが異なるgroupIdname__sを持っているので、それらが同じ成果物であることを知りません。したがって、競合解決は実行できず、両方のjarname__sが解決された依存関係として含まれます。

resolved dependencies with multiple xml-apis

クラスローダー地獄

前述のように、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-apisxercesname__、xercesImplname__、xmlParserAPIsnameなど)があるため、これを実施するのは困難です。さらに、当社のサードパーティ製のライブラリ/フレームワークは、JAXPバージョンまたはサーブレットコンテナによって提供されるバージョンでは動作しない可能性があります。

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


_ update _ :Joshua Spiewakはパッチを当てたXercesビルドスクリプトをMaven Centralにアップロードできるように XERCESJ-1454 にアップロードしました。この問題に投票したり、見たり、貢献したりして、この問題を一度解決しましょう。

670
Justin Garrick

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バージョンは見つかりませんでした-追加の依存関係があるため、Maven classifier- edバージョンにすることはできません。

103

率直に言って、私たちが遭遇したほとんどすべてはJAXPバージョンでうまく動くので、 we always exclude xml-apisxercesImplです。

62
jtahlborn

Maven Enforcerプラグインを禁止された依存関係ルールと共に使用できます。これはあなたが望まないすべてのエイリアスを禁止し、あなたが欲しいものだけを許可することを可能にするでしょう。これらの規則は違反したときあなたのプロジェクトのMavenビルドを失敗させるでしょう。さらに、このルールが企業内のすべてのプロジェクトに適用される場合は、プラグイン設定を企業の親のpomに置くことができます。

見る:

42

私はこれが正確に質問に答えないことを知っています、しかし彼らの依存関係管理のためにGradleを使うことが偶然にグーグルから入ってくるpplのために:

私はこのようにGradleを使ってxerces/Java8の問題をすべて取り除きました。

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

私はあなたが答える必要がある1つの質問があると思います:

アプリケーション内のすべてが共存できるxerces * .jarがありますか?

そうでなければ、あなたは基本的に戸惑い、OSGIのようなものを使わなければならないでしょう。それはあなたが同時に異なるバージョンのライブラリをロードさせることを可能にします。それは基本的にクラスローダーの問題でjarバージョンの問題を置き換えることに注意してください...

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

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

Maven用のプラグインとして、ある種のバージョン解決戦略を書くことができるのでしょうか。これはおそらく最も良い解決策でしょうが、実行可能であれば何らかの研究とコーディングが必要な場合。

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

それをまとめると:それは混乱であり、それは変わらないでしょう。

16
Jens Schauder

あなたの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

コマンドラインに.

6
Derek Bennett

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

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

基本的にこれがすることはすべての扶養家族に 彼らの バージョンのXercesまたは彼らのプロジェクトを宣言させることを強制することです。彼らがこの依存関係を無効にしたい場合は、そうすることを歓迎しますが、それから彼らは潜在的な問題を所有するでしょう。

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

  • 積極的な決断をしなさい。彼らは同じバージョンのXercesを使いますか、それとも他の何かを使いますか?
  • クラスパスを乱雑にしないようにするだけでなく、実際に(単体テストを通して)解析とクラスロードをテストします。

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

それは私達の組織ではとてもうまくいきます。その導入前は、OPが説明していたのと同じ地獄に住んでいました。

6
Daniel

すべての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レベルにするためのコード修正、または承認されたlibsを通してJVMレベルでの解決策を必要とします。

3
teknopaul

どうやらxerces:xml-apis:1.4.01はもうMavenの中心にはなっていませんが、これは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>
2
thrau

私の友人はとても単純です。ここに例を挙げます。

<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>
            </exclusions>
        </dependency>

そして、あなたがあなたのMavenツリーに問題がないことを端末(この例ではウィンドウズコンソール)にチェックインしたいなら:

mvn dependency:tree -Dverbose | grep --color=always '(.* conflict\|^' | less -r
2
Eduardo

除外を除いて助けになるのは、モジュール式の依存関係です。

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

しかし、 OSGiJBoss Modules のようなモジュール式フレームワークでは、これはもうそれほど苦痛ではありません。これらのライブラリは、どちらを使用してもどちらでも構いません。

もちろん、それでも1つの実装とバージョンのみを使用することをお勧めしますが、他の方法がない場合(より多くのlibから追加の機能を使用する場合)、モジュール化すると節約できます。

実際に動作している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モジュールは単純で高速です。特定の機能を欠いていますが、(ほとんど)1つのベンダの管理下にあり、驚くべき高速起動を可能にするほとんどのプロジェクトには十分です(依存関係が解決されるため)。

Java 8では モジュール化の取り組みが進行中です AFAIKは主にJRE自体をモジュール化することを目的としていますが、アプリに適用できるかどうかはわかりません。

2
Ondra Žižka