在Java / Maven中处理“ Xerces地狱”?


731

在我的办公室里,仅提及Xerces一词就足以引起开发人员的致命愤怒。粗略地浏览SO上的其他Xerces问题似乎表明,几乎所有Maven用户在某个时候都被该问题“感动”。不幸的是,了解问题需要对Xerces的历史有一点了解...

历史

  • Xerces是Java生态系统中使用最广泛的XML解析器。几乎所有用Java编写的库或框架都以某种能力使用Xerces(如果不是直接使用,则是透明地使用)。

  • 到目前为止,官方二进制文件中包含的Xerces jar 尚未进行版本控制。例如,Xerces 2.11.0实现jar被命名为xercesImpl.jarnot xercesImpl-2.11.0.jar

  • Xerces团队不使用Maven,这意味着他们不将正式发行版上载到Maven Central

  • Xerces过去以单个jarxerces.jar)的形式发布,但被拆分为两个jar,一个包含API(xml-apis.jar),一个包含这些API的实现(xercesImpl.jar)。许多较旧的Maven POM仍声明依赖于xerces.jar。在过去的某个时候,Xerces也发布为xmlParserAPIs.jar,某些较早的POM也依赖于此。

  • 将jar部署到Maven存储库的人分配给xml-apis和xercesImpl jar的版本通常是不同的。例如,即使xml-apis都来自Xerces 2.8.0,也可以给它xml版本1.3.03,给xercesImpl版本2.8.0。这是因为人们经常用实现的规范版本标记xml-apis jar。还有就是这是一个非常不错的,但不完全击穿这里

  • 使事情复杂化的是,Xerces是JRE中包含的XML处理Java API(JAXP)的参考实现中使用的XML解析器。实现类在com.sun.*名称空间下重新打包,这使得直接访问它们很危险,因为它们在某些JRE中可能不可用。但是,并非所有的Xerces功能都通过java.*javax.*API 公开;例如,没有API公开Xerces序列化。

  • 令人困惑的是,几乎所有servlet容器(JBoss,Jetty,Glassfish,Tomcat等)都随Xerces一起放在其一个或多个/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或as 会很好<provided>,您所依赖的第三方代码可能会或可能不会与您使用的JDK的JAXP中提供的版本一起使用。另外,您还可以将Xerces jars放在servlet容器中以应对。这给您提供了许多选择:是否删除servlet版本,并希望您的容器在JAXP版本上运行?离开servlet版本更好,希望您的应用程序框架在servlet版本上运行是否更好?如果上面概述的一两个未解决的冲突成功地渗入了您的产品(在大型组织中很容易发生),您很快就会陷入类加载器地狱中,想知道类加载器在运行时选择的是哪个版本的Xerces,是否将在Windows和Linux中选择相同的jar(可能不是)。

解决方案?

我们试过标志认证的所有Xerces的Maven依赖作为<provided>或作为<exclusion>,但这是给难以执行(尤其是大型团队)的文物有这么多的别名(xml-apisxercesxercesImplxmlParserAPIs,等)。此外,我们的第三方库/框架可能无法在JAXP版本或servlet容器提供的版本上运行。

我们如何最好地使用Maven解决这个问题?我们是否必须对依赖项进行这种细粒度的控制,然后依靠分层的类加载?是否有某种方法可以全局排除所有Xerces依赖关系,并强制我们所有的框架/库使用JAXP版本?


更新:Joshua Spiewak已将Xerces构建脚本的修补程序版本上载到XERCESJ-1454,从而可以上载到Maven Central。投票/观看/贡献这个问题,让我们一劳永逸地解决这个问题。


8
感谢您提出这个详细问题。我不明白xerces团队的动机。我想他们会为该产品感到自豪,并乐于使用其他产品,但是xerces和maven的当前状态令人遗憾。即使这样,即使对我来说没有意义,他们也可以做自己想做的事情。我不知道声纳类型的人有什么建议。
Travis Schneeberger

35
这可能不在主题之列,但这可能是我见过的更好的帖子。与问题更相关的是,您所描述的是我们可能遇到的最痛苦的问题之一。伟大的倡议!
让·雷米Revy

2
@TravisSchneeberger很多复杂性是因为Sun选择在JRE本身中使用Xerces。您几乎不能怪这是Xerces族。
托尔比约恩Ravn的安徒生

通常,我们尝试通过反复试验找到一个满足所有依赖库的Xerces版本,如果不可能的话,则重构为WAR将应用程序拆分为单独的WAR(单独的类加载器)。这个工具(我写了它)通过允许查询jar和类的类路径来帮助了解jhades.org上的情况-在服务器尚未启动的情况下也可以使用
Angular University

如果在Windows中从git bash启动servicemix时遇到此错误,请快速发表评论:而是从“普通” cmd启动它。
艾伯特·亨德里克斯

Answers:


111

自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/...
亨迪Irawan

这很令人困惑,但这是由于第三方上传了非版本的Xerces罐子,就像justingarrik在他的帖子中所说的那样。xml-apis 2.9.1与1.3.04相同,因此从这个意义上讲,1.4.01比1.3.04更新(且在数值上更大)。
liltitus13年

1
如果您的pom.xml中同时包含xercesImpl和xml-apis,请确保删除xml-apis依赖项!否则2.0.2抬起丑陋的头。
MikeJRamsey56 2016年

64

坦白说,使用JAXP版本,我们 遇到的几乎所有东西都可以正常工作,因此我们始终排除xml-apisxercesImpl


13
您可以为此添加pom.xml代码段吗?
chzbrgla

10
当我尝试这种方法时,我会java.lang.NoClassDefFoundError: org/w3c/dom/ElementTraversal在运行时抛出JavaMelody和Spring 。
大卫·摩尔

为了增加David Moles的响应-我已经看到有六个可传递依赖项需要ElementTraversal。Spring和Hadoop中最常见的各种事情。
Scott Carey 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。
谢尔盖·波诺马列夫

42

您可以将Maven强制实施器插件与禁止的依赖关系规则一起使用。这将允许您禁止所有不需要的别名,而只允许您想要的别名。违反这些规则将使您的项目的Maven构建失败。此外,如果此规则适用于企业中的所有项目,则可以将插件配置放入公司的父pom中。

看到:


33

我知道这不能完全回答问题,但是对于来自google的ppl,碰巧使用Gradle进行了依赖管理:

我设法摆脱了Gradle这样的所有xerces / Java8问题:

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

36
很好,使用maven大约需要4000行XML。
teknopaul

那没有解决问题。还有其他针对Android-Gradle人员的提示吗?
nyxee

2
@teknopaul XML仅用于配置。Groovy是一种高级编程语言。有时,您可能希望使用XML来实现其明确性,而不是出于其魔术性。
Dragas

16

我想您需要回答一个问题:

是否存在一个xerces * .jar,您的应用程序中的所有内容都可以使用?

如果不是这样的话,那么您基本上会一头雾水,必须使用OSGI之类的东西,它允许您同时加载不同版本的库。请注意,它基本上用类加载器问题代替了jar版本问题。

如果存在这样的版本,则可以使您的存储库针对所有依赖项返回该版本。这是一个丑陋的骇客,最终会在您的类路径中多次使用相同的xerces实现,但比拥有多个不同版本的xerces更好。

您可以排除对xerces的所有依赖关系,然后将其添加到要使用的版本中。

我想知道您是否可以编写某种版本解析策略作为maven的插件。这可能是最好的解决方案,但如果可行,则需要进行一些研究和编码。

对于运行时环境中包含的版本,必须确保将其从应用程序类路径中删除,或者在考虑服务器的lib文件夹之前首先考虑将应用程序jar用于类加载。

总结一下:这是一团糟,并且不会改变。


1
(在所有标准容器中)来自不同ClassLoader加载的同一jar中的同一类仍然是ClassCastException
Ajax

3
究竟。这就是为什么我这样写:警告:它基本上用类加载器问题代替了jar版本的问题
Jens Schauder 2012年

7

这里还没有探讨另一个选项:在Maven中将Xerces依赖项声明为optional

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

基本上,这是强迫所有家属声明他们的 Xerces版本,否则其项目将无法编译。如果他们想重写此依赖关系,欢迎这样做,但是这样他们将拥有潜在的问题。

这为下游项目提供了强烈的动力:

  • 做出积极的决定。他们使用相同版本的Xerces还是使用其他版本?
  • 实际测试他们的解析(例如通过单元测试)和类加载,以及不要弄乱他们的类路径。

并非所有开发人员都跟踪新引入的依赖项(例如 mvn dependency:tree)。这种方法将立即引起他们注意。

它在我们的组织中运作良好。在引入之前,我们曾经生活在OP描述的相同地狱中。


我应该在version元素内使用dot-dot-dot,还是需要使用2.6.2这样的真实版本?
chrisinmtown

3
@chrisinmtown真实版本。
丹尼尔(Daniel)

6

每个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中)在JVM级别进行解决。


执行详细的重构时,还需要在Java文件和配置的文本中搜索包和类的名称。您会发现开发人员已将Impl类的FQN放入由Class.forName和类似构造使用的常量字符串中。
德里克·贝内特

假定所有SAX实现都执行相同的操作,这是不正确的。xercesImpl库允许使用java.xml.parser库缺少的配置选项。
Amalgovinus

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

除了排除之外,有用的是模块依赖性。

使用一个单一的类加载(独立的应用程序)或半分层的(JBoss AS / EAP 5.x),这是一个问题。

但是使用OSGiJBoss Modules这样的模块化框架,这不再是一件麻烦事了。这些库可以独立使用它们想要的任何库。

当然,仍然仍然建议仅使用一个实现和版本,但是如果没有其他方法(使用更多库中的额外功能),则模块化可以为您省钱。

自然地,JBoss AS 7是一个很好的JBoss模块实例。 / 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模块更加简单快捷。尽管缺少某些功能,但对于大多数项目(大多数)都在一个供应商的控制下,这已经足够了,并且可以实现惊人的快速启动(由于并行化的依赖关系得以解决)。

请注意,Java 8正在进行模块化工作,但是AFAIK主要是对JRE本身进行模块化,不确定它是否适用于应用程序。


jboss模块是关于静态模块化的。OSGi所提供的运行时模块化与它几乎没有关系-我会说它们是相互补充的。这是一个很好的系统。
eis 2014年

*补充而不是赞美
Robert Mikes 2015年

2

显然xerces:xml-apis:1.4.01不再在Maven 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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.