817 Stimmen

Umgang mit der "Xerces-Hölle" in Java/Maven?

In meinem Büro reicht die bloße Erwähnung des Wortes Xerces aus, um bei den Entwicklern mörderische Wut hervorzurufen. Ein flüchtiger Blick auf die anderen Xerces-Fragen auf SO scheint darauf hinzuweisen, dass fast alle Maven-Benutzer irgendwann von diesem Problem "berührt" werden. Leider erfordert das Verständnis des Problems ein wenig Wissen über die Geschichte von Xerces...

Geschichte

  • Xerces ist der am häufigsten verwendete XML-Parser im Java-Ökosystem. Fast jede Bibliothek oder jedes Framework, das in Java geschrieben wurde, verwendet Xerces in irgendeiner Form (wenn auch nicht direkt, so doch indirekt).

  • Die Xerces-Gläser, die in der offizielle Binärdateien sind bis heute nicht versioniert. Das Xerces 2.11.0-Implementierungs-Jar heißt zum Beispiel xercesImpl.jar und nicht xercesImpl-2.11.0.jar .

  • Das Xerces-Team verwendet nicht Maven was bedeutet, dass sie nicht eine offizielle Veröffentlichung auf Maven-Zentrale .

  • Xerces war früher als einzelnes Gefäß freigegeben ( xerces.jar ), wurde aber in zwei Jars aufgeteilt, von denen eines die API ( xml-apis.jar ) und eine, die die Implementierungen dieser APIs enthält ( xercesImpl.jar ). Viele ältere Maven POMs deklarieren immer noch eine Abhängigkeit von xerces.jar . Irgendwann in der Vergangenheit wurde Xerces auch veröffentlicht als xmlParserAPIs.jar , auf die auch einige ältere POMs angewiesen sind.

  • Die Versionen, die den xml-apis und xercesImpl Jars von denjenigen zugewiesen werden, die ihre Jars in den Maven Repositories bereitstellen, sind oft unterschiedlich. Zum Beispiel könnte xml-apis die Version 1.3.03 und xercesImpl die Version 2.8.0 erhalten, obwohl beide aus Xerces 2.8.0 stammen. Das liegt daran, dass die Leute das xml-apis jar oft mit der Version der Spezifikationen, die es implementiert, kennzeichnen. Es gibt eine sehr schöne, aber unvollständige Aufschlüsselung dieses Sachverhalts aquí .

  • Erschwerend kommt hinzu, dass Xerces der XML-Parser ist, der in der Referenzimplementierung der Java API for XML Processing (JAXP) verwendet wird, die in der JRE enthalten ist. Die Implementierungsklassen sind unter dem Namen com.sun.* Namespace, was es gefährlich macht, direkt auf sie zuzugreifen, da sie in einigen JREs möglicherweise nicht verfügbar sind. Allerdings sind nicht alle Xerces-Funktionen über die java.* y javax.* APIs; zum Beispiel gibt es keine API, die die Xerces-Serialisierung offenlegt.

  • Zu dem verwirrenden Durcheinander kommt hinzu, dass fast alle Servlet-Container (JBoss, Jetty, Glassfish, Tomcat usw.) Xerces in einem oder mehreren ihrer /lib Mappen.

Probleme

Lösung von Konflikten

Aus einigen - oder vielleicht allen - der oben genannten Gründe sind viele Organisationen benutzerdefinierte Builds von Xerces in ihren POMs. Dies ist nicht wirklich ein Problem, wenn Sie eine kleine Anwendung haben und nur Maven Central verwenden, aber es wird schnell zu einem Problem für Unternehmenssoftware, bei der Artifactory oder Nexus mehrere Repositories (JBoss, Hibernate, etc.) vertritt:

xml-apis proxied by Artifactory

Organisation A könnte zum Beispiel Folgendes veröffentlichen xml-apis als:

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

In der Zwischenzeit könnte Organisation B die gleichen jar als:

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

Obwohl B's jar ist eine niedrigere Version als die von A jar kennt Maven nicht dass es sich um das gleiche Artefakt handelt, da sie unterschiedliche groupId s. Daher kann es keine Konfliktlösung durchführen und sowohl jar s werden als aufgelöste Abhängigkeiten aufgenommen:

resolved dependencies with multiple xml-apis

Classloader-Hölle

Wie bereits erwähnt, wird die JRE mit Xerces in der JAXP RI ausgeliefert. Es wäre zwar schön, alle Xerces-Maven-Abhängigkeiten als <exclusion> s oder als <provided> Der Code von Drittanbietern, auf den Sie angewiesen sind, kann mit der in JAXP bereitgestellten Version des von Ihnen verwendeten JDK funktionieren oder auch nicht. Darüber hinaus müssen Sie sich mit den Xerces-Jars auseinandersetzen, die in Ihrem Servlet-Container enthalten sind. Sie haben also mehrere Möglichkeiten: Löschen Sie die Servlet-Version und hoffen Sie, dass Ihr Container mit der JAXP-Version läuft? Ist es besser, die Servlet-Version beizubehalten und zu hoffen, dass Ihre Anwendungs-Frameworks mit der Servlet-Version laufen? Wenn es gelingt, einen oder zwei der oben beschriebenen ungelösten Konflikte in Ihr Produkt einzuschleusen (was in einem großen Unternehmen leicht passieren kann), finden Sie sich schnell in der Classloader-Hölle wieder und fragen sich, welche Version von Xerces der Classloader zur Laufzeit auswählt und ob er unter Windows und Linux das gleiche Jar auswählt (wahrscheinlich nicht).

Lösungen?

Wir haben versucht, alle Xerces-Maven-Abhängigkeiten als <provided> oder als <exclusion> Dies ist jedoch schwer durchzusetzen (insbesondere bei einem großen Team), da die Artefakte so viele Aliasnamen haben ( xml-apis , xerces , xercesImpl , xmlParserAPIs , etc.). Außerdem laufen unsere Bibliotheken/Frameworks von Drittanbietern möglicherweise nicht auf der JAXP-Version oder der von einem Servlet-Container bereitgestellten Version.

Wie können wir dieses Problem am besten mit Maven lösen? Müssen wir eine so feinkörnige Kontrolle über unsere Abhängigkeiten ausüben und uns dann auf ein abgestuftes Classloading verlassen? Gibt es eine Möglichkeit, alle Xerces-Abhängigkeiten global auszuschließen und alle unsere Frameworks/Libs zu zwingen, die JAXP-Version zu verwenden?


UPDATE : Joshua Spiewak hat eine gepatchte Version der Xerces-Bau-Skripte auf XERCESJ-1454 die den Upload zu Maven Central ermöglicht. Stimmen Sie ab, beobachten Sie, tragen Sie zu diesem Thema bei und lassen Sie uns dieses Problem ein für alle Mal beheben.

11voto

Derek Bennett Punkte 723

Sie sollten zuerst debuggen, um den Grad Ihrer XML-Hölle zu ermitteln. Meiner Meinung nach besteht der erste Schritt darin, Folgendes hinzuzufügen

-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

in die Befehlszeile. Wenn das funktioniert, schließen Sie die Bibliotheken aus. Wenn nicht, dann fügen Sie

-Djaxp.debug=1

in der Befehlszeile.

8voto

Daniel Punkte 3963

Es gibt noch eine weitere Option, die hier nicht untersucht wurde: die Deklaration von Xerces-Abhängigkeiten in Maven als opcional :

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

Im Grunde bedeutet dies, dass alle unterhaltsberechtigten Personen gezwungen werden, Folgendes zu melden ihre Version von Xerces oder ihr Projekt lässt sich nicht kompilieren. Wenn sie diese Abhängigkeit außer Kraft setzen wollen, können sie das gerne tun, aber dann haben sie das potenzielle Problem selbst in der Hand.

Dies schafft einen starken Anreiz für nachgelagerte Projekte:

  • Treffen Sie eine aktive Entscheidung. Nehmen sie die gleiche Version von Xerces oder etwas anderes?
  • Testen Sie das Parsing (z. B. durch Unit-Tests) und das Laden von Klassen, um den Klassenpfad nicht zu überladen.

Nicht alle Entwickler behalten den Überblick über neu eingeführte Abhängigkeiten (z. B. mit mvn dependency:tree ). Auf diese Weise werden sie sofort auf die Angelegenheit aufmerksam gemacht.

In unserer Organisation funktioniert das sehr gut. Vor seiner Einführung lebten wir in der gleichen Hölle, die der OP beschreibt.

6voto

teknopaul Punkte 6101

Jedes Maven-Projekt sollte aufhören, von Xerces abhängig zu sein, sie sind es wahrscheinlich nicht wirklich. XML-APIs und eine Impl sind seit 1.4 Teil von Java. Es gibt keine Notwendigkeit, von xerces oder XML APIs abhängig zu sein, es ist so, als würde man sagen, man sei von Java oder Swing abhängig. Dies ist implizit.

Wenn ich der Chef eines Maven-Repo wäre, würde ich ein Skript schreiben, das rekursiv die Xerces-Abhängigkeiten entfernt, und ein Read Me schreiben, das besagt, dass dieses Repo Java 1.4 benötigt.

Alles, was tatsächlich kaputt geht, weil es Xerces direkt über org.apache imports referenziert, braucht einen Code-Fix, um es auf den Stand von Java 1.4 zu bringen (und das ist seit 2002 geschehen) oder eine Lösung auf JVM-Ebene über endorsed libs, nicht in maven.

2voto

Ondra Žižka Punkte 39898

Was helfen würde, abgesehen vom Ausschluss, sind modulare Abhängigkeiten.

Mit einer flachen Klassenladung (eigenständige Anwendung) oder halbhierarchisch (JBoss AS/EAP 5.x) war dies ein Problem.

Aber mit modularen Frameworks wie OSGi y JBoss-Module ist das nicht mehr so schmerzhaft. Die Bibliotheken können unabhängig voneinander jede beliebige Bibliothek verwenden.

Natürlich ist es immer noch am empfehlenswertesten, bei einer einzigen Implementierung und Version zu bleiben, aber wenn es keinen anderen Weg gibt (indem man zusätzliche Funktionen von mehreren Bibliotheken verwendet), dann kann die Modularisierung Sie retten.

Ein gutes Beispiel für JBoss Modules in Aktion ist natürlich, JBoss AS 7 / EAP 6 / WildFly 8 , für die es in erster Linie entwickelt wurde.

Beispiel einer Moduldefinition:

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

Im Vergleich zu OSGi ist JBoss Modules einfacher und schneller. Obwohl bestimmte Funktionen fehlen, ist es für die meisten Projekte, die (meist) unter der Kontrolle eines Anbieters stehen, ausreichend und ermöglicht einen atemberaubend schnellen Start (aufgrund der Auflösung paralellisierter Abhängigkeiten).

Beachten Sie, dass es eine Modularisierungsbemühungen für Java 8 im Gange aber AFAIK ist das in erster Linie dazu gedacht, die JRE selbst zu modularisieren, ich bin nicht sicher, ob das auch für Anwendungen gilt.

2voto

thrau Punkte 2600

Offensichtlich xerces:xml-apis:1.4.01 ist nicht mehr in Maven Central, was aber das ist, was xerces:xercesImpl:2.11.0 Referenzen.

Das funktioniert bei mir:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X