Pregunta

Al desarrollar una aplicación Java muy basada en XML, recientemente encontré un problema interesante en Ubuntu Linux.

Mi aplicación, utilizando el Java Plugin Framework , no puede convertir un dom4j -creado un documento XML a Batik implementación de la especificación SVG.

En la consola, me entero de que se produce un error:

Exception in thread "AWT-EventQueue-0" java.lang.LinkageError: loader constraint violation in interface itable initialization: when resolving method "org.apache.batik.dom.svg.SVGOMDocument.createAttribute(Ljava/lang/String;)Lorg/w3c/dom/Attr;" the class loader (instance of org/java/plugin/standard/StandardPluginClassLoader) of the current class, org/apache/batik/dom/svg/SVGOMDocument, and the class loader (instance of <bootloader>) for interface org/w3c/dom/Document have different Class objects for the type org/w3c/dom/Attr used in the signature
    at org.apache.batik.dom.svg.SVGDOMImplementation.createDocument(SVGDOMImplementation.java:149)
    at org.dom4j.io.DOMWriter.createDomDocument(DOMWriter.java:361)
    at org.dom4j.io.DOMWriter.write(DOMWriter.java:138)

Me imagino que el problema se debe a un conflicto entre el cargador de clases original de la JVM y el cargador de clases implementado por el marco del complemento.

Que yo sepa, no es posible especificar un cargador de clases para que lo use el framework. Podría ser posible piratearlo, pero preferiría un enfoque menos agresivo para resolver este problema, ya que (por cualquier razón) solo ocurre en sistemas Linux.

¿Alguno de ustedes ha encontrado un problema así y tiene alguna idea de cómo solucionarlo o al menos llegar al núcleo del problema?

¿Fue útil?

Solución

LinkageError es lo que obtendrás en un caso clásico en el que tienes una clase C cargada por más de un cargador de clases y esas clases se usan juntas en el mismo código (comparadas, emitidas, etc.). No importa si es el mismo nombre de Clase o incluso si se carga desde un contenedor idéntico: una Clase de un cargador de clases siempre se trata como una Clase diferente si se carga desde otro cargador de clases.

El mensaje (que ha mejorado mucho con los años) dice:

Exception in thread "AWT-EventQueue-0" java.lang.LinkageError: 
loader constraint violation in interface itable initialization: 
when resolving method "org.apache.batik.dom.svg.SVGOMDocument.createAttribute(Ljava/lang/String;)Lorg/w3c/dom/Attr;" 
the class loader (instance of org/java/plugin/standard/StandardPluginClassLoader) 
of the current class, org/apache/batik/dom/svg/SVGOMDocument, 
and the class loader (instance of ) for interface org/w3c/dom/Document 
have different Class objects for the type org/w3c/dom/Attr used in the signature

Entonces, aquí el problema está en resolver el método SVGOMDocument.createAttribute (), que usa org.w3c.dom.Attr (parte de la biblioteca estándar de DOM). Pero, la versión de Attr cargada con Batik se cargó desde un cargador de clases diferente al de la instancia de Attr que está pasando al método.

Verás que la versión de Batik parece estar cargada desde el complemento de Java. Y el tuyo se está cargando desde " " ;, que probablemente sea uno de los cargadores JVM incorporados (boot classpath, ESOM o classpath).

Los tres modelos destacados del cargador de clases son:

  • delegación (el valor predeterminado en el JDK: preguntar al padre y luego a mí)
  • posterior a la delegación (común en complementos, servlets y lugares donde desea aislamiento: pregúnteme, luego a los padres)
  • hermanos (comunes en modelos de dependencia como OSGi, Eclipse, etc.)

No sé qué estrategia de delegación utiliza el cargador de clases JPF, pero la clave es que desea que se cargue una versión de la biblioteca dom y que todos puedan obtener esa clase desde la misma ubicación. Eso puede significar eliminarlo del classpath y cargarlo como un complemento, o evitar que Batik lo cargue, o algo más.

Otros consejos

Suena como un problema de jerarquía del cargador de clases. No puedo decir en qué tipo de entorno se implementa su aplicación, pero a veces este problema puede ocurrir en un entorno web, donde el servidor de aplicaciones crea una jerarquía de cargadores de clases, que se asemeja a algo como:

javahome / lib - como root
appserver / lib - como hijo de root
webapp / WEB-INF / lib - como hijo de hijo de root
etc

Por lo general, los cargadores de clase delegan la carga en su cargador de clase principal (esto se conoce como " parent-first "), y si ese cargador de clase no puede encontrar la clase, entonces el cargador de clase secundario intenta. Por ejemplo, si una clase implementada como JAR en webapp / WEB-INF / lib intenta cargar una clase, primero le pide al cargador de clases correspondiente a appserver / lib que cargue la clase (que a su vez le pide al cargador de clases correspondiente a javahome / lib para cargar la clase), y si esta búsqueda falla, entonces se busca una coincidencia con esta clase en WEB-INF / lib.

En un entorno web, puede tener problemas con esta jerarquía. Por ejemplo, un error / problema que encontré antes fue cuando una clase en WEB-INF / lib dependía de una clase implementada en appserver / lib, que a su vez dependía de una clase implementada en WEB-INF / lib. Esto causó fallos porque mientras los cargadores de clases pueden delegar en el cargador de clases principal, no pueden delegar de nuevo en el árbol. Por lo tanto, el cargador de clases WEB-INF / lib le pedirá a appserver / lib classloader una clase, el servidor de aplicaciones / lib classloader cargará esa clase e intentará cargar la clase dependiente, y fallará porque no pudo encontrar esa clase en appserver / lib o javahome / lib.

Entonces, aunque no esté implementando su aplicación en un entorno de servidor de aplicaciones / web, mi explicación demasiado larga podría aplicarse a usted si su entorno tiene una jerarquía de cargadores de clases configurados. ¿Lo hace? ¿JPF está haciendo algún tipo de magia de cargador de clases para poder implementar sus características de complemento?

Puede ser que esto ayude a alguien porque funciona bastante bien para mí. El problema se puede resolver integrando sus propias dependencias. Sigue estos sencillos pasos

Primero verifique el error que debería ser así:

  • Error en la ejecución del método:
  • java.lang.LinkageError: infracción de restricción del cargador:
  • al resolver el método "org.slf4j.impl. StaticLoggerBinder .getLoggerFactory () Lorg / slf4j / ILoggerFactory; "
  • el cargador de clases (instancia de org / openmrs / module / ModuleClassLoader) de la clase actual, org / slf4j / LoggerFactory ,
  • y el cargador de clases (instancia de org / apache / catalina / loader / WebappClassLoader) para la clase resuelta, org / slf4j / impl / StaticLoggerBinder ,
  • tienen diferentes objetos de clase para el tipo taticLoggerBinder.getLoggerFactory () Lorg / slf4j / ILoggerFactory; utilizado en la firma

  1. Ver las dos clases resaltadas. Google los busca como " StaticLoggerBinder.class jar download " &erio; " LoggeraFactory.class jar descargar " ;. Esto le mostrará primero o, en algún caso, un segundo enlace (el sitio es http://www.java2s.com ) que es una de las versiones jar que has incluido en tu proyecto. Puede identificarlo con inteligencia, pero somos adictos a Google;)

  2. Después de eso sabrás el nombre del archivo jar, en mi caso es como slf4j-log4j12-1.5.6.jar & amp; slf4j-api-1.5.8

  3. Ahora la versión más reciente de este archivo está disponible aquí http://mvnrepository.com/ (en realidad todas las versiones Hasta la fecha, este es el sitio desde donde Maven obtiene sus dependencias).
  4. Ahora agregue ambos archivos como dependencias con la última versión (o mantenga ambas versiones iguales, ya sea que la versión elegida sea antigua). A continuación se muestra la dependencia que debe incluir en pom.xml

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.7</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.7</version>
</dependency>

Cómo obtener la definición de dependencia del sitio Maven

¿Puede especificar un cargador de clases? Si no es así, intente especificar el cargador de clases de contexto así:

Thread thread = Thread.currentThread();
ClassLoader contextClassLoader = thread.getContextClassLoader();
try {
    thread.setContextClassLoader(yourClassLoader);
    callDom4j();
} finally {
    thread.setContextClassLoader(contextClassLoader);
}

No estoy familiarizado con Java Plugin Framework, pero escribo código para Eclipse, y me encuentro con problemas similares de vez en cuando. No garantizo que lo solucione, pero probablemente valga la pena probarlo.

Las respuestas de Alex y Matt son muy útiles. Yo también podría beneficiarme de su análisis.

Tuve el mismo problema al usar la biblioteca de Batik en un marco de RCP de Netbeans, la biblioteca de Batik se incluyó como un "Módulo de contenedor de biblioteca". Si algún otro módulo hace uso de apis XML, y no se necesita y no se depende la dependencia de Batik para ese módulo, el problema de violación de la restricción del cargador de clase surge con mensajes de error similares.

En Netbeans, los módulos individuales usan cargadores de clases dedicados, y la relación de dependencia entre los módulos implica un enrutamiento adecuado de la delegación del cargador de clases.

Podría resolver el problema simplemente omitiendo el archivo jar xml-apis del paquete de la biblioteca Batik.

Como se especifica en esta pregunta , habilitando el -verbose: class hará que la información de registro de JVM sobre todas las clases que se cargan, lo que puede ser increíblemente útil para comprender de dónde provienen las clases en escenarios más complejos & amp; aplicaciones.

La salida que obtienes se parece aproximadamente a esto (copiada de esa pregunta):

[Opened /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/sunrsasign.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/jsse.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/jce.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/charsets.jar]
[Loaded java.lang.Object from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.io.Serializable from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.Comparable from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.CharSequence from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.String from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top