Question Traiter avec "Xerces enfer" dans Java / Maven?


Dans mon bureau, la simple mention du mot Xerces est suffisante pour inciter les développeurs à la rage meurtrière. Un coup d'œil rapide sur les autres questions de Xerces sur les SO semble indiquer que presque tous les utilisateurs de Maven sont "touchés" par ce problème à un moment donné. Malheureusement, la compréhension du problème nécessite un peu de connaissances sur l'histoire de Xerces ...

Histoire

  • Xerces est l'analyseur XML le plus utilisé dans l'écosystème Java. Presque chaque bibliothèque ou framework écrit en Java utilise Xerces dans une certaine mesure (de manière transitoire, sinon directement).

  • Les pots Xerces inclus dans le binaires officiels sont, à ce jour, pas versionné. Par exemple, le fichier d'implémentation Xerces 2.11.0 est nommé xercesImpl.jar et pas xercesImpl-2.11.0.jar.

  • L'équipe Xerces n'utilise pas Maven, ce qui signifie qu'ils ne le font pas télécharger une version officielle Maven Central.

  • Xerces était autrefois libéré comme un seul pot (xerces.jar), mais a été divisé en deux pots, l'un contenant l'API (xml-apis.jar) et une contenant les implémentations de ces API (xercesImpl.jar). De nombreux POM Maven plus anciens déclarent encore une dépendance à xerces.jar. À un certain moment dans le passé, Xerces a également été libéré xmlParserAPIs.jar, dont dépendent certaines POM plus anciennes.

  • Les versions attribuées aux fichiers xml-apis et xercesImpl par ceux qui déploient leurs fichiers jars dans des référentiels Maven sont souvent différentes. Par exemple, xml-apis pourrait avoir la version 1.3.03 et xercesImpl pourrait avoir la version 2.8.0, même si les deux sont de Xerces 2.8.0. C'est parce que les gens marquent souvent le jar xml-apis avec la version des spécifications qu'il implémente. Il y a une très belle, mais incomplète répartition de cette ici.

  • Pour compliquer les choses, Xerces est l'analyseur XML utilisé dans l'implémentation de référence de l'API Java pour le traitement XML (JAXP), inclus dans le JRE. Les classes d'implémentation sont reconditionnées sous la com.sun.* namespace, ce qui rend dangereux d'y accéder directement, car ils peuvent ne pas être disponibles dans certains JRE. Cependant, toutes les fonctionnalités de Xerces ne sont pas exposées via java.* et javax.* Apis; par exemple, il n'y a pas d'API qui expose la sérialisation de Xerces.

  • Ajoutant au désordre confus, presque tous les récipients de servlet (JBoss, jetée, Glassfish, Tomcat, etc.), expédient avec Xerces dans un ou plusieurs de leur /lib Dossiers.

Problèmes

Résolution de conflit

Pour certaines - ou peut-être toutes - des raisons ci-dessus, de les organisations publient et consomment des versions personnalisées de Xerces dans leur POMs. Ce n'est pas vraiment un problème si vous avez une petite application et n'utilisez que Maven Central, mais cela devient rapidement un problème pour les logiciels d'entreprise où Artifactory ou Nexus est en train d'utiliser plusieurs référentiels (JBoss, Hibernate, etc.):

xml-apis proxied by Artifactory 

Par exemple, l'organisation A pourrait publier xml-apis comme:

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

Pendant ce temps, l'organisation B pourrait publier le même jar comme:

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

Bien que B jar est une version inférieure à celle de A jar, Maven ne sait pas qu'ils sont le même artefact parce qu'ils ont différents groupIds. Ainsi, il ne peut pas effectuer de résolution de conflit et les deux jars seront inclus en tant que dépendances résolues:

resolved dependencies with multiple xml-apis

Classloader Hell

Comme mentionné ci-dessus, le JRE est livré avec Xerces dans le JAXP RI. Bien qu'il serait bon de marquer toutes les dépendances Xerces Maven comme <exclusion>s ou comme <provided>, le code tiers dont vous dépendez peut ou non fonctionner avec la version fournie dans JAXP du JDK que vous utilisez. De plus, vous avez à faire face aux bocaux Xerces livrés dans votre conteneur de servlets. Cela vous laisse un certain nombre de choix: supprimez-vous la version de servlet et espérez que votre conteneur fonctionne sur la version JAXP? Vaut-il mieux quitter la version du servlet et espérer que vos frameworks applicatifs s'exécutent sur la version de servlet? Si un ou deux des conflits non résolus décrits ci-dessus réussissent à se glisser dans votre produit (ce qui est facile à réaliser dans une grande organisation), vous vous trouvez rapidement dans classloader hell, se demandant quelle version de Xerces le classloader choisit à l'exécution et si oui ou non choisira le même pot dans Windows et Linux (probablement pas).

Solutions?

Nous avons essayé de marquer toutes les dépendances de Xerces Maven <provided> ou en tant que <exclusion>, mais ceci est difficile à appliquer (surtout avec une grande équipe) étant donné que les artefacts ont tellement d'alias (xml-apis, xerces, xercesImpl, xmlParserAPIs, etc.). De plus, nos bibliothèques / frameworks tiers peuvent ne pas fonctionner sur la version JAXP ou la version fournie par un conteneur de servlet.

Comment pouvons-nous mieux résoudre ce problème avec Maven? Devons-nous exercer un contrôle aussi fin sur nos dépendances, puis compter sur la classification par classes? Existe-t-il un moyen d'exclure globalement toutes les dépendances de Xerces et de forcer tous nos frameworks / libs à utiliser la version JAXP?


METTRE À JOUR: Joshua Spiewak a téléchargé une version corrigée des scripts de construction Xerces XERCESJ-1454 cela permet de télécharger vers Maven Central. Votez / regardez / contribuez à ce problème et résolvons ce problème une fois pour toutes.


617
2017-07-26 20:32


origine


Réponses:


Il y a 2,11,0 JARs (et sources JAR!) de xerces à Maven Central depuis le 20 février 2013! Voir Xerces à Maven Central. Je me demande pourquoi ils n'ont pas résolu https://issues.apache.org/jira/browse/XERCESJ-1454...

J'ai utilisé:

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

et toutes les dépendances ont résolu bien - même approprié xml-apis-1.4.01!

Et ce qui est le plus important (et ce qui n'était pas évident dans le passé) - le JAR de Maven Central est le même JAR que dans le Xerces-J-bin.2.11.0.zip Distribution.

Je ne pouvais cependant pas trouver xml-schema-1.1-beta version - il ne peut pas être un Maven classifier-ed version en raison de dépendances supplémentaires.


94
2018-03-07 07:30



Franchement, à peu près tout ce que nous avons rencontré fonctionne très bien avec la version JAXP, donc nous toujours exclure  xml-apis et xercesImpl.


56
2017-07-26 22:18



Vous pouvez utiliser le plugin maven enforcer avec la règle de dépendance interdite. Cela vous permettrait d'interdire tous les alias que vous ne voulez pas et n'autoriser que celui que vous voulez. Ces règles échoueront à la construction maven de votre projet en cas de violation. En outre, si cette règle s'applique à tous les projets d'une entreprise, vous pouvez placer la configuration du plug-in dans un pom parent d'entreprise.

voir:


40
2017-07-27 16:28



Je sais que cela ne répond pas exactement à la question, mais pour ppl venant de google qui arrive à utiliser Gradle pour leur gestion des dépendances:

J'ai réussi à me débarrasser de tous les problèmes de xerces / Java8 avec Gradle comme ceci:

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

24
2018-04-24 06:54



Je suppose qu'il y a une question à laquelle vous devez répondre:

Existe-t-il un fichier xerces * .jar avec lequel tout votre application peut vivre?

Sinon, vous êtes fondamentalement foutu et devrez utiliser quelque chose comme OSGI, qui vous permet d'avoir différentes versions d'une bibliothèque chargée en même temps. Soyez averti qu'il remplace fondamentalement les problèmes de version jar avec les problèmes de classloader ...

S'il existe une telle version, vous pouvez faire en sorte que votre référentiel renvoie cette version pour tous les types de dépendances. C'est un hack moche et se retrouverait avec la même implémentation de xerces dans votre classpath plusieurs fois mais mieux que d'avoir plusieurs versions différentes de xerces.

Vous pouvez exclure toutes les dépendances de xerces et en ajouter une à la version que vous voulez utiliser.

Je me demande si vous pouvez écrire une sorte de stratégie de résolution de version comme un plugin pour maven. Ce serait probablement la meilleure solution, mais si tout à fait possible besoin de recherche et de codage.

Pour la version contenue dans votre environnement d'exécution, vous devez vous assurer qu'elle est supprimée du chemin d'accès aux classes de l'application ou que les fichiers jar de l'application sont pris en compte en premier pour le chargement de la classe avant que le dossier lib du serveur ne soit pris en compte.

Donc, pour conclure: c'est un gâchis et cela ne changera pas.


16
2017-07-26 20:49



Il y a une autre option qui n'a pas été explorée ici: déclarer les dépendances de Xerces dans Maven optionnel:

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

Fondamentalement, ce que cela fait est de forcer toutes les personnes à charge à déclarer leur version de Xerces ou leur projet ne compilera pas. S'ils veulent contourner cette dépendance, ils sont invités à le faire, mais ils seront alors propriétaires du problème potentiel.

Cela crée une forte incitation pour les projets en aval à:

  • Prendre une décision active. Est-ce qu'ils vont avec la même version de Xerces ou utilisent autre chose?
  • En fait, testez leur analyse (par exemple par le biais de tests unitaires) et la répartition par classe ainsi que de ne pas encombrer leur chemin de classe.

Tous les développeurs ne gardent pas trace des dépendances nouvellement introduites (par exemple avec mvn dependency:tree). Cette approche portera immédiatement le problème à leur attention.

Cela fonctionne assez bien dans notre organisation. Avant son introduction, nous vivions dans le même enfer que décrit l'OP.


5
2017-10-02 09:52



Vous devriez d'abord déboguer, pour aider à identifier votre niveau d'enfer XML. À mon avis, la première étape consiste à ajouter

-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

à la ligne de commande. Si cela fonctionne, alors commencez à exclure les bibliothèques. Si non, alors ajoutez

-Djaxp.debug=1

à la ligne de commande.


4
2018-06-14 13:51



Ce qui aiderait, sauf pour l'exclusion, est les dépendances modulaires.

Avec une classe de chargement à plat (application autonome), ou semi-hiérarchique (JBoss AS / EAP 5.x) C'était un problème.

Mais avec des cadres modulaires comme OSGi et Modules JBoss, ce n'est plus tant de douleur. Les bibliothèques peuvent utiliser n'importe quelle bibliothèque, indépendamment.

Bien sûr, il est toujours recommandé de s'en tenir à une seule implémentation et à une seule version, mais s'il n'y a pas d'autre moyen (en utilisant des fonctionnalités supplémentaires de plus de librairies), la modularisation pourrait vous sauver.

Un bon exemple de modules JBoss en action est, naturellement, JBoss AS 7 / PAE 6 / WildFly 8, pour lequel il a été principalement développé.

Exemple de définition de module:

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

En comparaison avec OSGi, JBoss Modules est plus simple et plus rapide. Si certaines fonctionnalités sont manquantes, cela suffit pour la plupart des projets qui sont (pour la plupart) sous le contrôle d'un fournisseur, et permettent un démarrage rapide étonnant (en raison de la résolution de dépendances paralelisées).

Notez qu'il y a un effort de modularisation en cours pour Java 8, mais je pense que c'est principalement pour modulariser le JRE lui-même, pas sûr que ce soit applicable aux applications.


2
2018-06-22 02:59



Chaque projet maven devrait s'arrêter en fonction des xerces, ils ne le font probablement pas vraiment. Les API XML et un Impl font partie de Java depuis 1.4. Il n'est pas nécessaire de dépendre de xerces ou d'API XML, c'est comme dire que vous dépendez de Java ou de Swing. C'est implicite.

Si j'étais le patron d'un repo maven, j'écrirais un script pour supprimer récursivement les dépendances de xerces et j'écrirais un read me disant que ce repo nécessite Java 1.4.

Tout ce qui est réellement cassé parce qu'il fait directement référence à Xerces via les importations org.apache nécessite une correction de code pour l'amener au niveau Java 1.4 (ou depuis 2002) ou une solution au niveau de la JVM via des libs approuvées, pas en maven.


1
2018-02-05 11:58



Apparemment xerces:xml-apis:1.4.01 n'est plus dans Maven Central, qui est cependant ce que xerces:xercesImpl:2.11.0 les références.

Cela fonctionne pour moi:

<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
2017-10-04 16:45



Mon ami c'est très simple, voici un exemple:

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

Et si vous voulez vérifier dans le terminal (console de Windows pour cet exemple) que votre arbre maven n'a aucun problème:

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

0
2018-05-18 14:08