Pregunta ¿Tratar con "Xerces hell" en Java / Maven?


En mi oficina, la mera mención de la palabra Xerces es suficiente para incitar a la ira asesina de los desarrolladores. Una mirada superficial a las otras preguntas de Xerces sobre SO parece indicar que casi todos los usuarios de Maven están "tocados" por este problema en algún momento. Desafortunadamente, comprender el problema requiere un poco de conocimiento sobre la historia de Xerces ...

Historia

  • Xerces es el analizador XML más utilizado en el ecosistema de Java. Casi todas las bibliotecas o marcos escritos en Java usan Xerces en cierta capacidad (de manera transitiva, si no directamente).

  • Los jarros Xerces incluidos en el binarios oficiales son, hasta el día de hoy, no versionadas. Por ejemplo, el archivo de implementación de Xerces 2.11.0 se llama xercesImpl.jar y no xercesImpl-2.11.0.jar.

  • El equipo de Xerces no usa Maven, lo que significa que no cargar un lanzamiento oficial a Maven Central.

  • Xerces solía ser lanzado como un solo frasco (xerces.jar), pero se dividió en dos jarrones, uno que contiene la API (xml-apis.jar) y uno que contiene las implementaciones de esas API (xercesImpl.jar) Muchos POM antiguos de Maven aún declaran una dependencia en xerces.jar. En algún momento en el pasado, Xerces también fue lanzado como xmlParserAPIs.jar, de lo que también dependen algunos POM antiguos.

  • Las versiones asignadas a los archivos xml-apis y xercesImpl por aquellos que implementan sus archivos jar en los repositorios Maven a menudo son diferentes. Por ejemplo, xml-apis podría tener la versión 1.3.03 y xercesImpl podría tener la versión 2.8.0, aunque ambos sean de Xerces 2.8.0. Esto se debe a que las personas suelen etiquetar el contenedor xml-apis con la versión de las especificaciones que implementa. Hay un desglose muy bueno, pero incompleto de esto aquí.

  • Para complicar las cosas, Xerces es el analizador XML utilizado en la implementación de referencia de la API de Java para Procesamiento XML (JAXP), incluido en el JRE. Las clases de implementación se vuelven a empaquetar bajo com.sun.* espacio de nombres, lo que hace que sea peligroso acceder a ellos directamente, ya que pueden no estar disponibles en algunos JRE. Sin embargo, no todas las funciones de Xerces están expuestas a través de java.* y javax.* APIs; por ejemplo, no hay API que expone la serialización de Xerces.

  • Además del confuso desorden, casi todos los contenedores de servlets (JBoss, Jetty, Glassfish, Tomcat, etc.) se envían con Xerces en uno o más de sus /lib carpetas.

Problemas

La resolución de conflictos

Para algunos (o tal vez todos) de los motivos anteriores, muchos organizaciones publican y consumen compilaciones personalizadas de Xerces en su POMs. Esto no es realmente un problema si tiene una aplicación pequeña y solo está usando Maven Central, pero se convierte rápidamente en un problema para el software empresarial donde Artifactory o Nexus está aprovisionando múltiples repositorios (JBoss, Hibernate, etc.):

xml-apis proxied by Artifactory 

Por ejemplo, la organización A podría publicar xml-apis como:

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

Mientras tanto, la organización B podría publicar el mismo jar como:

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

Aunque B jar es una versión inferior a la de A jar, Maven no sabe que son el mismo artefacto porque tienen diferentes groupIds. Por lo tanto, no puede realizar la resolución de conflictos y ambos jars se incluirán como dependencias resueltas:

resolved dependencies with multiple xml-apis

Classloader Hell

Como se mencionó anteriormente, el JRE se envía con Xerces en JAXP RI. Si bien sería bueno marcar todas las dependencias de Xerces Maven como <exclusion>s o como <provided>, el código de terceros del que dependa puede o no funcionar con la versión provista en JAXP del JDK que está utilizando. Además, tiene los jarros Xerces enviados en su contenedor de servlet con los que lidiar. Esto le deja con varias opciones: ¿elimina la versión de servlet y espera que su contenedor se ejecute en la versión de JAXP? ¿Es mejor dejar la versión del servlet y esperar que sus frameworks de aplicaciones se ejecuten en la versión del servlet? Si uno o dos de los conflictos no resueltos descritos anteriormente logran introducirse en su producto (es fácil de lograr en una organización grande), rápidamente se encuentra en el infierno de los cargadores de clases, preguntándose qué versión de Xerces está buscando el ejecutor de clases en tiempo de ejecución y si elegirá el mismo jar en Windows y Linux (probablemente no).

Soluciones?

Intentamos marcar todas las dependencias de Xerces Maven como <provided> o como un <exclusion>, pero esto es difícil de hacer cumplir (especialmente con un equipo grande) dado que los artefactos tienen tantos alias (xml-apis, xerces, xercesImpl, xmlParserAPIs, etc.) Además, nuestros libs / frameworks de terceros pueden no ejecutarse en la versión de JAXP o en la versión proporcionada por un contenedor de servlet.

¿Cómo podemos abordar este problema con Maven? ¿Tenemos que ejercer un control tan detallado sobre nuestras dependencias y luego confiar en la carga de clases escalonada? ¿Hay alguna manera de excluir globalmente todas las dependencias de Xerces y forzar a todos nuestros frameworks / libs a usar la versión de JAXP?


ACTUALIZAR: Joshua Spiewak ha subido una versión parcheada de los scripts de construcción de Xerces a XERCESJ-1454 que permite subir a Maven Central. Vote / observe / contribuya a este problema y solucionemos este problema de una vez por todas.


617
2017-07-26 20:32


origen


Respuestas:


Hay 2.11.0 JAR (y fuente JAR!) de xerces en Maven Central desde el 20 de febrero de 2013! Ver Xerces en Maven Central. Me pregunto por qué no han resuelto https://issues.apache.org/jira/browse/XERCESJ-1454...

He usado:

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

y todas las dependencias han resuelto bien, incluso las adecuadas xml-apis-1.4.01!

Y lo más importante (y lo que no era obvio en el pasado): el JAR en Maven Central es el mismo JAR que en el oficial Xerces-J-bin.2.11.0.zip distribución.

Sin embargo, no pude encontrar xml-schema-1.1-beta versión - no puede ser un Maven classifier-ed versión debido a dependencias adicionales.


94
2018-03-07 07:30



Francamente, casi todo lo que hemos encontrado funciona muy bien con la versión JAXP, por lo que nosotros siempre excluir  xml-apis y xercesImpl.


56
2017-07-26 22:18



Puede usar el complemento maven enforcer con la regla de dependencia prohibida. Esto le permitiría prohibir todos los alias que no desea y permitir solo el que desea. Estas reglas no podrán generar la construcción de su proyecto cuando se viole. Además, si esta regla se aplica a todos los proyectos en una empresa, puede poner la configuración del complemento en un pom padre corporativo.

ver:


40
2017-07-27 16:28



Sé que esto no responde la pregunta exactamente, pero para las personas que vienen de Google que utilizan Gradle para su gestión de la dependencia:

Logré deshacerme de todos los problemas de xerces / Java8 con Gradle de esta manera:

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

24
2018-04-24 06:54



Supongo que hay una pregunta que debes responder:

¿Existe un xerces * .jar con el que pueda vivir todo en su aplicación?

Si no, básicamente estás jodido y tendrías que usar algo como OSGI, que te permite tener diferentes versiones de una biblioteca cargadas al mismo tiempo. Tenga en cuenta que básicamente reemplaza los problemas de la versión jar con los problemas del cargador de clases ...

Si existe tal versión, puede hacer que su repositorio devuelva esa versión para todo tipo de dependencias. Es un hack feo y terminaría con la misma implementación de xerces en tu classpath varias veces, pero mejor que tener múltiples versiones diferentes de xerces.

Puede excluir cada dependencia de xerces y agregar una a la versión que desea usar.

Me pregunto si puedes escribir algún tipo de estrategia de resolución de versión como un complemento para maven. Esta sería probablemente la mejor solución, pero si es factible, necesita alguna investigación y codificación.

Para la versión contenida en su entorno de tiempo de ejecución, deberá asegurarse de que sea eliminada de la ruta de clases de la aplicación o que los archivos jar de la aplicación sean considerados primero para la carga de clases antes de que se considere la carpeta lib del servidor.

Entonces, para concluir: es un desastre y eso no cambiará.


16
2017-07-26 20:49



Hay otra opción que no se ha explorado aquí: declarar dependencias de Xerces en Maven como Opcional:

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

Básicamente, lo que hace esto es obligar a todos los dependientes a declarar su versión de Xerces o su proyecto no se compilará. Si quieren anular esta dependencia, pueden hacerlo, pero entonces serán los dueños del posible problema.

Esto crea un fuerte incentivo para que los proyectos posteriores:

  • Toma una decisión activa. ¿Van con la misma versión de Xerces o usan algo más?
  • En realidad, pruebe su análisis sintáctico (por ejemplo, a través de pruebas unitarias) y la carga de clases, así como también para no saturar su classpath.

No todos los desarrolladores realizan un seguimiento de las dependencias recientemente introducidas (por ejemplo, mvn dependency:tree) Este enfoque inmediatamente traerá el asunto a su atención.

Funciona bastante bien en nuestra organización. Antes de su introducción, vivíamos en el mismo infierno que el OP está describiendo.


5
2017-10-02 09:52



Debes depurar primero, para ayudar a identificar tu nivel de infierno XML. En mi opinión, el primer paso es agregar

-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

a la línea de comando. Si eso funciona, comience a excluir bibliotecas. Si no, agrega

-Djaxp.debug=1

a la línea de comandos.


4
2018-06-14 13:51



Lo que ayudaría, excepto para excluir, son las dependencias modulares.

Con una carga de clases plana (aplicación independiente), o semijerárquico (JBoss AS / EAP 5.x) esto fue un problema

Pero con marcos modulares como OSGi y Módulos JBoss, esto ya no es tanto dolor. Las bibliotecas pueden usar la biblioteca que deseen, de forma independiente.

Por supuesto, es aún más recomendable seguir con una sola implementación y versión, pero si no hay otra manera (usando características adicionales de más libs), modularización podría salvarte.

Un buen ejemplo de JBoss Modules en acción es, naturalmente, JBoss AS 7 / EAP 6 / WildFly 8para el cual fue desarrollado principalmente.

Ejemplo de definición de módulo:

<?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 comparación con OSGi, JBoss Modules es más simple y más rápido. Si bien faltan ciertas características, es suficiente para la mayoría de los proyectos que (en su mayoría) están bajo el control de un proveedor, y permiten un arranque increíblemente rápido (debido a la resolución de dependencias paralelizadas).

Tenga en cuenta que hay un Esfuerzo de modularización en curso para Java 8, pero AFAIK es principalmente modularizar el JRE en sí mismo, no estoy seguro de si será aplicable a las aplicaciones.


2
2018-06-22 02:59



Cada proyecto de maven debería dejar de depender de xerces, probablemente no lo hagan realmente. Las API XML y un Impl han sido parte de Java desde 1.4. No hay necesidad de depender de xerces o XML API, es como decir que dependes de Java o Swing. Esto es implícito

Si yo fuera el jefe de un repositorio maven, escribiría un script para eliminar de forma recursiva las dependencias de xerces y escribir una lectura que diga que este repositorio requiere Java 1.4.

Cualquier cosa que realmente se rompa porque hace referencia a Xerces directamente a través de importaciones de org.apache necesita una corrección de código para llevarlo al nivel Java 1.4 (y lo ha hecho desde 2002) o solución al nivel JVM a través de libs aprobadas, no en maven.


1
2018-02-05 11:58



Aparentemente xerces:xml-apis:1.4.01 ya no está en maven central, que sin embargo es xerces:xercesImpl:2.11.0 referencias.

Esto funciona para mí:

<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



Mi amigo es muy simple, aquí un ejemplo:

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

Y si desea verificar en la terminal (consola de windows para este ejemplo) que su árbol maven no tiene problemas:

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

0
2018-05-18 14:08