在Java/Maven中處理“Xerces hell”?




classloader dependency-management (8)

你可以使用maven執行者插件和禁止的依賴規則。 這將允許你禁止你不想要的所有別名,只允許你想要的別名。 這些規則違反了你的項目的Maven構建。 此外,如果此規則適用於企業中的所有項目,則可以將插件配置放入公司父項目中。

看到:

在我的辦公室裡,僅僅提到Xerces這個詞就足以激起開發者的憤怒。 粗略瀏覽SO上的其他Xerces問題似乎表明,幾乎所有的Maven用戶都在某個時候被這個問題“感動”了。 不幸的是,理解這個問題需要對Xerces的歷史有一些了解......

歷史

  • Xerces是Java生態系統中使用最廣泛的XML解析器。 幾乎每個使用Java編寫的庫或框架都以某種身份使用Xerces(即使不是直接傳遞)。

  • 包含在官方二進製文件中的Xerces罐子至今仍未進行版本控制。 例如,Xerces 2.11.0實現jar被命名為xercesImpl.jar而不是xercesImpl-2.11.0.jar

  • Xerces團隊不使用Maven ,這意味著他們不會將正式版本上傳到Maven Central

  • Xerces曾經作為一個單獨的jarxerces.jar )發布,但被分成了兩個jar,一個包含API( xml-apis.jar ),另一個包含這些API( xml-apis.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。 這裡有一個非常好的,但不完整的細分。

  • 複雜的是,Xerces是JRE中包含的用於XML處理的Java API的參考實現(JAXP)中使用的XML解析器。 實現類在com.sun.*名稱空間下重新打包,這使得直接訪問它們很危險,因為它們可能在某些JRE中不可用。 但是,並非所有的Xerces功能都通過java.*javax.* API公開; 例如,沒有公開Xerces序列化的API。

  • 除了混亂的混亂外,幾乎所有的servlet容器(JBoss,Jetty,Glassfish,Tomcat等)都在Xerces的一個或多個/lib文件夾中提供。

問題

解決衝突

對於上面的一些原因或者全部原因,許多組織在他們的POM中發布和使用Xerces的自定義版本。 如果你有一個小應用程序並且只使用Maven Central,這並不是一個問題,但是它很快就會成為Artifactory或Nexus代理多個存儲庫(JBoss,Hibernate等)的企業軟件的一個問題:

例如,組織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都將被包含為已解決的依賴關係:

類加載器地獄

如上所述,JRE在JAXP RI中與Xerces一起提供。 雖然將所有Xerces Maven依賴項標記為<exclusion><provided>是很好的,但您所依賴的第三方代碼可能使用或不使用您所使用的JDK的JAXP中提供的版本。 另外,你還有servlet容器中的Xerces罐子可以與之抗衡。 這給你留下了許多選擇:你是否刪除了servlet版本,並希望你的容器在JAXP版本上運行? 離開servlet版本更好嗎,並希望你的應用程序框架在servlet版本上運行? 如果上面列出的一個或兩個未解決的衝突設法隱藏到您的產品中(容易在大型組織中發生),您很快就會發現自己處於classloader地獄,想知道類加載器在運行時選擇哪個版本的Xerces,以及是否它將在Windows和Linux中選擇相同的jar(可能不是)。

解決方案?

我們已經嘗試將所有Xerces Maven依賴項標記為<provided><exclusion> ,但這很難實施(特別是對於大型團隊),因為這些工件有很多別名( xml-apisxercesxercesImplxmlParserAPIs等)。 此外,我們的第三方庫/框架可能不能運行在JAXP版本或servlet容器提供的版本上。

我們如何才能最好地解決Maven的這個問題? 我們是否必須對依賴關係進行細粒度的控制,然後依靠分層的類加載? 是否有某種方法可以全局排除所有的Xerces依賴關係,並強制所有的框架/庫使用JAXP版本?

更新 :Joshua Spiewak已經將Xerces構建腳本的補丁版本上傳到XERCESJ-1454 ,允許上傳到Maven Central。 投票/看/造成這個問題,讓我們一勞永逸地解決這個問題。


坦率地說,我們所遇到的幾乎所有的工作都可以在JAXP版本上正常工作,所以我們總是排除 xml-apisxercesImpl


我想有一個問題需要回答:

是否存在應用程序中的所有內容都可以使用的xerces * .jar?

如果不是的話,你基本上被搞砸了,而且必須使用類似OSGI的東西,它允許你同時加載不同版本的庫。 被警告說它基本上用類加載器的問題替換了jar版本問題......

如果存在這樣的版本,您可以使您的存儲庫為各種依賴項返回該版本。 這是一種醜陋的黑客攻擊,最終會在您的classpath中多次執行相同的xerces實現,但是會比擁有多個不同版本的xerces更好。

您可以將每個依賴項都排除在xerces上,並將其添加到要使用的版本中。

我想知道你是否可以寫一些類型的版本解析策略作為maven的插件。 這可能是最好的解決方案,但如果在所有可行的情況下需要一些研究和編碼。

對於包含在運行時環境中的版本,您必須確保將它從應用程序類路徑中移除,或者在考慮服務器的lib文件夾之前首先考慮應用程序jars用於類加載。

所以要結束它:這是一團糟,這不會改變。


我的朋友很簡單,這裡是一個例子:

<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>

如果你想檢查終端(這個例子中的Windows控制台),你的Maven樹沒有問題:

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

每個Maven項目都應該停止根據xerces,他們可能不會真的。 從1.4開始,XML API和Impl一直是Java的一部分。 不需要依賴xerces或XML API,就好像說你依賴於Java或Swing。 這是隱含的。

如果我是Maven回購的老闆,我會編寫一個腳本來遞歸移除xerces依賴關係,並寫一篇文章說我的回購需要Java 1.4。

任何因為直接通過org.apache導入引用Xerces而導致實際上中斷的東西,需要通過代碼修復將其提升到Java 1.4級別(自2002年以來已完成)或者通過支持的庫而不是maven中的解決方案。


自2013年2月20日起,Maven Central中有2.11.0個xerces的JAR (和源JARs!) ! 參見Maven Central的Xerces 。 我想知道他們為什麼還沒有解決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版本。


除了排除之外,什麼會有所幫助是模塊化的依賴關係。

通過一個平麵類加載(獨立應用程序)或半分層(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更簡單快捷。 雖然缺少某些功能,但對於大多數(大部分)受一家供應商控制的項目而言,這已足夠,並允許驚人的快速啟動(由於解除了兼容的依賴關係)。

請注意, Java 8正在進行模塊化工作 ,但AFAIK主要是為了模塊化JRE本身,並不確定它是否適用於應用程序。


顯然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>






xerces