Traiter avec "Xerces hell" en Java / Maven?

dans mon bureau, la simple mention du mot Xerces suffit à inciter la rage meurtrière des développeurs. Un coup d'œil rapide aux autres questions de Xerces sur SO semble indiquer que presque tous les utilisateurs de Maven sont "touchés" par ce problème à un moment donné. Malheureusement, comprendre le problème exige un peu de connaissance de L'histoire de Xerces...

histoire

  • Xerces est le plus largement utilisé analyseur XML dans l'écosystème Java. Presque toutes les bibliothèques ou les cadres écrits en Java utilisent Xerces dans une certaine mesure (de façon transitoire, si ce n'est directement).

  • les pots Xerces inclus dans le les "binaires officiels ne sont, à ce jour, pas suivis de versions. Par exemple, le jar de mise en œuvre Xerces 2.11.0 s'appelle xercesImpl.jar et non xercesImpl-2.11.0.jar .

  • La Xerces équipe ne pas utiliser Maven , ce qui signifie qu'ils ne téléchargez une version officielle de Maven Central .

  • Xerces utilisé pour être libéré comme un seul pot ( xerces.jar ), mais a été divisé en deux pots, un contenant L'API ( xml-apis.jar ) et un contenant les implémentations de ces API ( xercesImpl.jar ). De nombreuses mamans plus âgées déclarent encore dépendre de xerces.jar . À un certain moment dans le passé, Xerces a également été publié sous le nom de xmlParserAPIs.jar , dont certains vieux POMs dépendent également.

  • les versions attribuées aux jars xml-apis et xercesImpl par ceux qui déploient leurs jars dans des dépôts Maven sont souvent différentes. Par exemple, xml-apis pourrait être donné la version 1.3.03 et xercesImpl pourrait être donné la version 2.8.0, même si les deux sont de Xerces 2.8.0. C'est parce que les gens étiquettent souvent le jar xml-apis avec la version des spécifications qu'elle met en œuvre. Il y a une très belle, mais incomplète ventilation de ce 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 de mise en œuvre sont Reconditionnées sous l'espace de noms com.sun.* , ce qui rend dangereux d'y accéder directement, car elles peuvent ne pas être disponibles dans certains JREs. Cependant, pas toutes les fonctionnalités de Xerces sont exposées via les API java.* et javax.* ; par exemple, aucune API n'expose la sérialisation de Xerces.

  • ajoutant au désordre confus, presque tous les conteneurs servlet (JBoss, Jetty, Glassfish, Tomcat, etc.), navire avec Xerces dans un ou plusieurs de leurs /lib des dossiers.

Problèmes

Résolution De Conflits

pour une partie - ou peut-être la totalité-des raisons ci-dessus, plusieurs les organisations publient et consomment des constructions sur mesure de Xerces dans leurs POM. 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 proxy plusieurs dépôts (JBoss, Hibernate, etc.):

xml-apis proxied by Artifactory

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

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

<groupId>xml-apis</groupId>

<artifactId>xml-apis</artifactId>

<version>1.3.04</version>

bien que b jar soit une version plus basse que A jar , Maven ne sait pas qu'ils sont le même objet, parce qu'ils ont de différent groupId S. Elle ne peut donc pas résoudre les conflits et les deux jar s sera inclus comme résolu dépendances:

resolved dependencies with multiple xml-apis

Chargeur De Classe De L'Enfer

comme indiqué ci-dessus, les navires JRE avec Xerces dans le JAXP RI. Alors qu'il serait bien de marquer toutes les dépendances de 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. En outre, vous avez les pots Xerces expédiés dans votre contenant de service à traiter. Cela vous laisse un certain nombre de choix: est-ce que vous supprimez la version servlet et espérez que votre conteneur tourne sur la version JAXP? Est-il préférable de laisser la version servlet, et d'espérer que vos cadres d'application fonctionnent sur la version servlet? Si l'un ou les deux de la les conflits non résolus décrits ci-dessus parviennent à se glisser dans votre produit (facile à se produire dans une grande organisation), vous vous retrouvez rapidement dans classloader hell, vous demandez quelle version de Xerces le classloader est en train de choisir à l'exécution et si oui ou non il va choisir le même bocal dans Windows et Linux (probablement pas).

Solutions?

nous avons essayé de marquer toutes les dépendances de Xerces Maven comme <provided> ou comme <exclusion> , mais c'est difficile à appliquer (surtout avec une grande équipe) étant donné que les artéfacts ont tellement d'alias ( xml-apis , xerces , xercesImpl , xmlParserAPIs , etc.). En outre, nos libs / frameworks tiers ne peuvent pas fonctionner sur la version JAXP ou la version fournie par un conteneur servlet.

comment résoudre ce problème avec Maven? Devons-nous exercer un contrôle aussi fin sur nos dépendances, et ensuite compter sur un chargement de classe par niveaux? Est-il une façon d'exclure globalement toutes les dépendances Xerces, et de forcer tous nos cadres / libs à utiliser la version JAXP?


mise à jour : Joshua Spiewak a téléchargé une version corrigée des scripts de construction Xerces vers XERCESJ-1454 qui permet de télécharger vers Maven Central. Votez/observez / contribuez à cette question et réglons ce problème une fois pour toutes.

630
demandé sur seanf 2012-07-27 00:32:59

11 réponses

il y a 2.11.0 pots (et des pots source!) de xerces à Maven Central depuis le 20 février 2013! Voir Xerces in 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 très bien - même le bon xml-apis-1.4.01 !

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

Je n'ai pas pu trouver la version xml-schema-1.1-beta - elle ne peut pas être une version classifier - ed à cause de dépendances supplémentaires.

95
répondu Grzegorz Grzybek 2013-06-18 16:26:13

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

57
répondu jtahlborn 2017-12-11 13:47:04

vous pouvez utiliser le plugin Maven enforcer avec la règle de dépendance bannie. Cela vous permettrait d'interdire tous les alias que vous ne voulez pas et autoriser uniquement 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 dans une entreprise, vous pouvez mettre la configuration du plugin dans une société mère pom.

voir:

41
répondu Travis Schneeberger 2012-07-27 18:49:39

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

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

configurations {
    all*.exclude group: 'xml-apis'
    all*.exclude group: 'xerces'
}
24
répondu netmikey 2015-04-24 06:54:38

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

existe-T-il un xerces*.jar que tout dans votre application peut vivre avec?

si ce n'est pas le cas, vous êtes essentiellement vissé et devrait utiliser quelque chose comme OSGI, ce 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 par des problèmes de classloader ...

si il existe une telle version que vous pouvez faire retourner cette version par votre dépôt pour toutes sortes de dépendances. C'est un hack Moche et finirait avec la même implémentation xerces dans votre classpath plusieurs fois, mais mieux que d'avoir plusieurs versions différentes de xerces.

vous pouvez exclure toutes les dépendances à 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. C'est probablement la solution la plus intéressante, mais si elle est réalisable, elle nécessite un peu de recherche et de codage.

pour la version contenue dans votre environnement d'exécution, vous devrez vous assurer qu'elle soit retirée de l'application classpath ou que les bocaux d'application soient considérés en premier pour le téléchargement de classe avant que le dossier lib du serveur ne soit considéré.

Donc, pour conclure: C'est un gâchis et ça ne changera pas.

16
répondu Jens Schauder 2012-10-27 03:44:54

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

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

essentiellement ce que cela fait est de forcer toutes les personnes à charge de déclarer leur version de Xerces ou leur projet ne sera pas compiler. Si ils veulent remplacer cette dépendance, ils sont invités à le faire, mais ensuite, ils seront propriétaires du problème potentiel.

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

  • prendre une décision active. Vont-ils avec la même version de Xerces ou utiliser autre chose?
  • testent en fait leur parsing (par exemple par des tests unitaires) et leur classloading ainsi que pour ne pas encombrer leur classpath.

tous les développeurs ne tiennent pas compte des dépendances nouvellement introduites (par exemple avec mvn dependency:tree ). Cette approche sera immédiatement la question à leur attention.

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

6
répondu Daniel 2015-10-02 09:52:51

vous devriez déboguer en premier, pour aider à identifier votre niveau D'enfer XML. À mon avis, la première étape est d'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. Dans la négative, ajouter

-Djaxp.debug=1

à la ligne de commande.

4
répondu Derek Bennett 2017-05-11 13:22:39

ce qui aiderait, à l'exception de l'exclusion, ce sont les dépendances modulaires.

avec un chargement de classe 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 tellement de douleur. Les bibliothèques peuvent utiliser la bibliothèque qu'elles veulent, indépendamment.

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

un bon exemple de Modules JBoss en action est, naturellement, JBoss AS 7 / EAP 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>

par rapport à OSGi, les Modules JBoss sont plus simples et plus rapides. Bien qu'il manque certaines fonctionnalités, il est suffisant pour la plupart des projets qui sont (la plupart du temps) sous le contrôle d'un fournisseur, et permet le démarrage rapide étourdissant (en raison de la résolution de dépendances paralysées).

notez qu'il y a un effort de modularisation en cours pour Java 8 , mais AFAIK qui est principalement pour modulariser la JRE elle-même, pas sûr si il sera applicable aux applications.

2
répondu Ondra Žižka 2013-06-22 03:10:34

chaque projet maven devrait cesser de dépendre de xerces, ils ne le font probablement pas vraiment. Les API XML et un Impl font partie de Java depuis la version 1.4. Il n'est pas nécessaire de dépendre des API xerces ou XML, c'est comme dire que vous dépendez de Java ou de Swing. Ce qui est implicite.

si j'étais le patron d'un Maven repo, j'écrirais un script pour supprimer récursivement les dépendances de xerces et écrire un read me qui dit que ce repo nécessite Java 1.4.

Tout ce qui casse parce qu'il fait référence à Xerces directement via org.apache imports a besoin d'un correctif de code pour l'amener au niveau Java 1.4 (et ce depuis 2002) ou d'une solution au niveau JVM via des libs approuvées, et non en maven.

2
répondu teknopaul 2016-02-05 11:58:43

apparemment xerces:xml-apis:1.4.01 n'est plus dans maven central, ce qui est cependant ce que xerces:xercesImpl:2.11.0 références.

ça marche 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>
2
répondu thrau 2016-10-04 16:45:02

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(Windows console pour cet exemple) que votre maven tree n'a pas de problèmes:

mvn dependency:tree -Dverbose | grep --color=always '(.* conflict\|^' | less -r
0
répondu Eduardo 2017-05-18 14:08:24