Eclipse no puede encontrar clases relacionadas con XML después de cambiar la ruta de compilación a JDK 10
Estoy desarrollando un proyecto Maven (plataforma sucursal-bom_brussels-sr7) en Eclipse. Cuando recientemente intenté cambiar la ruta de compilación de Java para el proyecto a JDK 10, la compilación de Eclipse ya no puede encontrar clases como javax.xml.xpath.XPath
, org.w3c.dom.Document
o org.xml.sax.SAXException
. Parece que sólo las clases relacionadas con XML se ven afectadas, principalmente por la dependencia de Maven xml-apis-1.4.01
.
Probar una compilación de Maven desde Eclipse funciona sin errores. Ctrl-Clic izquierdo en una de las clases supuestamente faltantes encuentra la clase y la abre en el editor de Eclipse. Parece que sólo la compilación de Eclipse se ve afectada.
Intenté varias cosas, pero ninguna ayudó. Lo intenté:
- Proyecto limpio
- Diferentes versiones de Eclipse: Oxígeno y Fotón.
- Ejecutando el propio Eclipse con JDK 8 y JDK 10.
- Cambiar el nivel de cumplimiento del compilador para el proyecto. Se compila con los niveles de cumplimiento 8 y 10 en la ruta de compilación JDK 8 y falla en ambos con JDK 10 en la ruta de compilación.
Supongo que el proyecto que se está migrando desde Java 1.8 todavía no tiene ningún archivo module-info.java
. Esto implica que está compilando código en el "módulo sin nombre".
El código en el módulo sin nombre "lee" todos los módulos observables con y sin nombre, en particular lee el módulo "java.xml" de la biblioteca del sistema JRE. Este módulo exporta paquetes como java.xml.xpath
.
Además, tiene xml-apis.java
en el classpath, que aporta otro conjunto de paquetes con los mismos nombres ( java.xml.xpath
y amigos). Se dice que están asociados al módulo sin nombre, como su propio código.
Esta situación viola el requisito de "visibilidad única" tal como se define en JLS §7.4.3 (último párrafo). En particular, cada nombre de tipo calificado Q.Id ( JSL §6.5.5.2 ) requiere que su prefijo Q sea un paquete visible de forma única (estoy ignorando el caso de tipos anidados por simplicidad). Ergo: el programa es ilegal y los compiladores deben rechazarlo.
Esto nos deja con una pregunta y dos soluciones:
(1) Pregunta: ¿Por qué javac acepta el programa?
(2) Solución: si agrega module-info.java
a su proyecto, puede controlar mediante requisitos qué módulo lee su proyecto, ya sea requires java.xml;
o requires xml.apis;
(donde "xml.apis" es el nombre del módulo automático de "xml-apis-1.4.01.jar).
(3) Solución: a menos que convierta su proyecto en un módulo, aún puede evitar el conflicto excluyéndolo java.xml
del conjunto de módulos observables. En la línea de comando esto se haría usando --limit-modules
. El equivalente en Eclipse es el cuadro de diálogo "Detalles de modularidad" , consulte también JDT 4.8 New&Noteworthy (busque la pestaña Contenido ). Dado que java.xml
se requiere implícitamente a través de muchos otros módulos observables predeterminados, puede ser una buena idea mover todo excepto de java.base
derecha ("Módulos incluidos explícitamente") a izquierda ("Módulos disponibles") (y volver a agregar selectivamente esos módulos que tu proyecto necesita).
PD: Eclipse todavía no proporciona un mensaje de error ideal; en lugar de "no se puede resolver", debería decir: "Se puede acceder al paquete javax.xml.xpath desde más de un módulo: javax.xml, <sin nombre>.
PPS: También es extraño: ¿cómo es que cambiar el orden entre JRE y un jar en el classpath (dicho orden no es un concepto admitido por javac ni por JEP 261) cambia el comportamiento del compilador?
EDICIONES:
- Alex Buckley confirmó que la situación dada es ilegal, a pesar de lo que dice javac. Se generó un error contra javac como JDK-8215739 . Este error se reconoció meses antes del lanzamiento de Java 12. A partir de 2019-06, se decidió que también Java 13 se enviará sin solución. Lo mismo ocurre con Java 14. El error se programó temporalmente para Java 15, pero este plan se abandonó el 20 de abril de 2020.
- El mensaje de error de Eclipse se ha mejorado para mencionar el problema real.
- En Eclipse 2019-06, se renovó la interfaz de usuario utilizada para la Solución (3) . Puede encontrar documentación actualizada en la ayuda en línea .
- A partir de 2022-12, hay otra perspectiva sobre este tema, como se describe en mi otra respuesta . Esto no invalida lo que se dice aquí, pero veamos las cosas bajo una luz diferente.
Si bien la respuesta aceptada (por mí misma) sigue siendo correcta , recientemente me llamó la atención sobre un giro adicional de la historia :
La intención original puede haber sido realmente apoyar la situación actual.
Vea esta cita en el documento de diseño original " El estado del sistema modular " (SotMS):
Si un paquete está definido tanto en un módulo con nombre como en el módulo sin nombre, entonces se ignora el paquete en el módulo sin nombre.
Ese documento tiene fecha del 8/3/2016 a las 08:18 y ya en ese momento estaba marcado " Este documento está ligeramente desactualizado ". Además, no es jurídicamente vinculante para ninguna implementación. Aún así, ese documento tiene cierta relevancia ya que lo citado anteriormente es precisamente lo que javac
parece implementarse (y aún se implementa muchos años después de que se presentó JDK-8215739 ).
OIA, el conflicto no es tanto un conflicto entre la primera y la segunda implementación, sino un conflicto incluso dentro de Oracle, al menos eso parece. 2 votos a favor de apoyar la situación (SotMS y javac) y solo un voto a favor de rechazar (JLS).
Dado que los responsables de Eclipse no están dispuestos a resolver este conflicto dentro de Oracle, la reciente versión 2022-12 de Eclipse tiene una nueva opción de compilación: al agregar la siguiente línea al archivo .settings/org.eclipse.jdt.core.prefs
, un usuario puede optar por ignorar JLS en este sentido:
org.eclipse.jdt.core.compiler.ignoreUnnamedModuleForSplitPackage=enabled
Esta opción pone la decisión en manos del usuario: ¿quiere semántica JLS o semántica SotMS/javac (en este número en particular)? Aún así, no estábamos preparados para proporcionar una opción de interfaz de usuario para evitar que los usuarios hicieran esta elección sin pensar, sin la información general que se proporciona aquí.
Personalmente, no estoy muy contento con esta situación, ya que agrava el hecho de que Java no es uno, sino varios lenguajes .
En mi caso, el problema fue que xercesImpl : 2.10.0
se trataba de una dependencia (transitoria). Este frasco se agrupa org.w3c.dom.html.HTMLDOMImplementation
.
Hasta donde tengo entendido, el org.w3c.dom
paquete pasa a estar disponible en dos módulos, lo que provoca que la compilación falle. En caso de que una de las dependencias (directa o transitoria) tenga clases en uno de los 25 paquetes exportados por el módulo java.xml, su compilación fallará.
Excluir xercesImpl (y también los infractores que se enumeran a continuación) en Maven resolvió el problema por mí:
<dependency>
<groupId>xyz</groupId>
<artifactId>xyz</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
</exclusion>
<exclusion>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
</exclusion>
<exclusion>
...
</exclusion>
</exclusions>
</dependency>
Gracias a Rune Flobakk por darnos la pista aquí: https://bugs.eclipse.org/bugs/show_bug.cgi?id=536928#c73
Otros delincuentes:
batik-ext : 1.9
(paquetes org.w3c.dom.Window)xom : 1.2.5
(paquetes org.w3c.dom.UserDataHandler)stax-api : 1.0.2
(incluye javax.xml.stream.EventFilter)xml-apis : 1.4.01
(paquetes org.w3c.dom.Document)xml-beans : 2.3.0
(paquetes org.w3c.dom.TypeInfo)
Esto parece haber sido reportado como error 536928 de Eclipse . Tal vez si todos votaran al respecto, aumentarían la prioridad.
Lo que sucede aquí es que tiene un comodín de importación como import org.w3c.dom.*
, que indica que desea importar todas las clases del paquete org.w3c.dom
. Ahora, si hay al menos una clase proporcionada org.w3c.dom
por una segunda fuente, Java no debe iniciarse (como se señala aquí ).
(Por cierto, el mensaje " ... no se puede resolver " se reemplaza por un mensaje de error más preciso " Se puede acceder al paquete org.w3c.dom desde más de un módulo: <sin nombre>, java.xml " en versiones más recientes Versiones de Eclipse, consulte esta solicitud de cambio fusionada de Stephan Herrmann).
Para resolver este problema
- Abra el cuadro de diálogo "Abrir tipo" ( Ctrl+ Shift+ T).
- Ingrese la importación completa , so
org.w3c.dom.*
oorg.w3c.dom.
. - Consulte la lista completa para obtener múltiples fuentes. Todas las entradas aquí deben contener sólo algo como "jdk-11-...".
- Reúna todos los JAR que contengan clases para las que tenga múltiples fuentes.
- Abra la pestaña "Jerarquía de dependencias" desde
pom.xml
. - Busque el archivo JAR.
- Agregue una exclusión (haga clic derecho o edítela
pom.xml
manualmente).
Ejemplo
Tenía esta dependencia de findbugs en mi pom.xml
:
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>findbugs</artifactId>
<version>${findbugs.version}</version>
</dependency>
Findbugs tiene dos dependencias que deben excluirse:
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>findbugs</artifactId>
<version>${findbugs.version}</version>
<exclusion>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
</exclusion>
<exclusion>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
</exclusion>
</dependency>