2011-09-09 55 views
5

Hemos estado utilizando JAXB 2.1 durante mucho tiempo en nuestro sistema. Tenemos una plataforma que se construye con Ant y genera un conjunto de paquetes que se implementan en un tiempo de ejecución OSGi. Usamos Java SE 6.Dos clases tienen el mismo nombre de tipo XML "objectFactory"

Utilizamos JAXB durante el proceso de compilación para generar tipos de datos de diferentes esquemas. Esas clases se empaquetan en los paquetes y se usan en tiempo de ejecución para serializar/deserializar el contenido. Además, utilizamos JAXB en nuestra plataforma en tiempo de ejecución para generar tipos de datos de otros esquemas proporcionados por el usuario (es una especie de plataforma MDA).

En el tiempo de ejecución de OSGi tenemos un paquete que tiene las jarras JAXB y exporta los paquetes necesarios. Creamos una instancia de JAXBContext con la ruta de contexto de todas las fábricas de objetos generadas, por lo que podemos ordenar/deshacer todos nuestros tipos de datos.

Eso ha estado funcionando hasta ahora, pero ahora estamos intentando actualizar a la última versión estable de JAXB (2.2.4) y estamos teniendo problemas al intentar crear el contexto en tiempo de ejecución. Obtenemos la siguiente excepción:

Two classes have the same XML type name "objectFactory". Use @XmlType.name and @XmlType.namespace to assign different names to them. 
    this problem is related to the following location: 
     at some.package.ObjectFactory 
    this problem is related to the following location: 
     at some.other.package.ObjectFactory 

    at com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:91) 
    at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:436) 
    at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:277) 
    at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1100) 
    at com.sun.xml.internal.bind.v2.ContextFactory.createContext(ContextFactory.java:143) 
    at com.sun.xml.internal.bind.v2.ContextFactory.createContext(ContextFactory.java:110) 
    at com.sun.xml.internal.bind.v2.ContextFactory.createContext(ContextFactory.java:191) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
    at java.lang.reflect.Method.invoke(Method.java:597) 
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:187) 
    ... 76 more 

El error dos clases tienen el mismo nombre de tipo XML "objectFactory" se imprime para cada una de las factorías de objetos generados durante el proceso de construcción.

Hemos visto varias publicaciones en SO con el mismo error pero aplicando a los tipos generados, no a la fábrica de objetos. Creemos que JAXB puede no estar identificando la clase ObjectFactory como una fábrica de objetos sino como un tipo de datos.

Una posibilidad era que estábamos usando la versión interna de JAXB en Java 6, por lo que decidió utilizar la propiedad Sistema -Djava.endorsed.dirs y poner los tres frascos (jaxb-api-2.2.4 .jar, jaxb-impl-2.2.4.jar y jaxb-xjc-2.2.4.jar) en esa ruta, pero todavía no funciona.

Creemos que el problema podría ser que estamos utilizando una versión diferente de JAXB en el tiempo de ejecución de OSGi y en el proceso de compilación, por lo que el código generado no es compatible. Pero tal vez estamos equivocados y hay otro problema.

¿Tiene alguna idea?

Gracias de antemano.

(Edit: más detalles sobre esto)

creamos el JAXBContext de esta manera:

ClassLoader classLoader = new JAXBServiceClassLoader(getParentClassLoader(), 
                 Collections.unmodifiableMap(objectFactories)); 
    context = JAXBContext.newInstance(contextPath.toString(), classLoader); 

donde contextPath es una cadena que contiene todas nuestras factorías de objetos separados por ':', y la JAXBServiceClassLoader es:

private static final class JAXBServiceClassLoader extends ClassLoader 
    { 
    @NotNull 
    private final Map<String, Object> objectFactories; 

    private JAXBServiceClassLoader(@NotNull ClassLoader parent, @NotNull Map<String, Object> objectFactories) 
    { 
     super(parent); 
     this.objectFactories = objectFactories; 
    } 

    @Override 
    public Class<?> loadClass(String name) throws ClassNotFoundException 
    { 
     Class<?> ret; 
     try 
     { 
     ret = super.loadClass(name); 
     } 
     catch (ClassNotFoundException e) 
     { 
     Object objectFactory = objectFactories.get(name); 
     if (objectFactory != null) 
     { 
      ret = objectFactory.getClass(); 
     } 
     else 
     { 
      throw new ClassNotFoundException(name + " class not found"); 
     } 
     } 
     return ret; 
    } 
    } 

(Editar: después del post de Aaron)

He estado depurando todas las partes internas de JAXBContextImpl y el hecho es que JAXBContextImpl está tratando de obtener la información de tipo de nuestras clases de ObjectFactory, lo cual es incorrecto. De hecho, en com.sun.xml.internal.bind.v2.model.impl.ModelBuilder: 314, la llamada getClassAnnotation() devuelve null pero cuando veo la instancia puedo ver la anotación XmlRegistry.

El caso es que, en ese punto XmlRegistry.class.getClassLoader() devuelve un valor nulo, pero si run() C (Clase) .getAnnotations() [0] .annotationType(). GetClassLoader() devuelve el classLoader del paquete de OSGi "lib.jaxb" que contiene mis jar de JAXB, que es correcto.

Por lo tanto, supongo que estamos cargando al mismo tiempo dos versiones diferentes de XmlRegistry, una del JDK y la otra de JAXB 2.2.4 jar. La pregunta es: ¿por qué?

Y, aún más, en lugar de cargar todas las clases com.sun.xml.internal. * (Como JAXBContextImpl), no debería cargar y ejecutar com.sun.xml.bind.v2.runtime.JAXBContextImpl desde Jars JAXB? Durante el proceso de depuración puedo ver que está haciendo algunas cosas con la reflexión, pero no entiendo por qué está haciendo eso.

+0

Uso de Java SE 6 Recomendaría utilizar el último parche de su JAXB impl que sea compatible con JAXB 2.1 a menos que exista una característica particular de JAXB 2.2 que esté tratando de usar. –

+0

Perdón por no haberlo mencionado. La razón para actualizar a JAXB 2.2.4 es que estamos actualizando nuestra versión JAX-WS a 2.2.5 y depende de esa versión de JAXB (http://jax-ws.java.net/2.2.5/docs/ ReleaseNotes.html). De lo contrario, podríamos usar JAXB 2.1. – Denian

+0

Parece que su JAXB impl está tratando erróneamente a 'ObjectFactory' como una clase de dominio. Esto probablemente se debe a que la anotación '@ XmlRegistry' no se reconoce debido a la diferencia' ClassLoader' entre las clases de dominio y la implementación de JAX-WS. ¿Estás creando el 'JAXBContext' directamente o la implementación de JAX-WS está haciendo esto? –

Respuesta

2

Finalmente encontramos una solución para esto.

De la documentación JAXB (Descubrimiento de aplicación JAXB sección):

http://jaxb.java.net/nonav/2.2.4-1/docs/api/javax/xml/bind/JAXBContext.html

Hemos intentado añadir un recurso en META-INF/services/javax.xml.bind.JAXBContext en Para imponer el uso de com.sun.xml.bind.v2.ContextFactory en lugar de la fábrica interna de Sun. Eso no funcionó, probablemente porque estamos usando un paquete OSGi.

De todos modos, como estamos usando nuestro propio cargador de clases, que sobreescribir el método getResourceAsStream(), que se llama desde ContextFinder: 343:

@Override 
public InputStream getResourceAsStream(String name) 
{ 
    if (name!=null && name.equals("META-INF/services/javax.xml.bind.JAXBContext")) 
    { 
    return new ByteArrayInputStream("com.sun.xml.bind.v2.ContextFactory".getBytes()); 
    } 
    return super.getResourceAsStream(name); 
} 

Ésta no es la solución más bonita pero funciona para nosotros . Y ese cargador de clases se usa solo cuando creamos el JAXBContext, debería estar bien.

2
  1. Asegúrese de que sólo hay una @XmlRegistry anotación en su ruta de clase (búsqueda de XmlRegistry.class archivo, no uso). Tal vez la anotación equivocada se recoja.

  2. Si eso no funciona, cree su propio cargador de clases que solo vea una fábrica. Eso no debería ser necesario, pero quién sabe.

  3. Intente establecer un punto de interrupción en JAXBContextImpl.java:436 para ver qué tipos procesa y por qué.

+0

Hola, Perdón por la respuesta tardía. 1. Hay un XmlRegistry.class en jaxb-api-2.2.4.jar (que está dentro de mi paquete "lib.jaxb") y en el Java 1.6 classes.jar. 2. Es difícil cambiar la implementación, lo intentaré si no tengo éxito con 3. 3. He estado depurando las partes internas, pero permítanme agregar mi comentario en la publicación principal. – Denian

+0

Gracias por su gran esfuerzo. Desafortunadamente, estoy fuera de mi alcance, ahora :-(Estoy bastante seguro de que es por la forma en que OSGi/Java realiza la carga de clases (la regla es cargar primero desde la CL padre, por lo que el código de Java tiene prioridad sobre las clases que OSGi agrega a la ruta de clase). Como prueba, trate de especificar su implementación de JAXB en la ruta de clase ** boot ** (consulte los documentos sobre cómo configurar eso: http://download.oracle.com/javase/1.3/docs/ tooldocs/win32/migration.html # bootcp). –

+0

Hola Aaron, gracias por su respuesta. Intenté agregar los archivos jar a bootclasspath, pero no importa, se lanza la misma excepción. – Denian

0

Tuve el mismo problema, pero una causa diferente. Incluso si no soluciona los problemas de los autores, publico esta respuesta para todos, leyendo esta publicación más adelante y teniendo el mismo problema que yo.

Utilicé esta configuración de compilación-complemento, que excluía los archivos package-info.java de la compilación. ¡Después de eliminar las exclusiones, todo funcionó como un amuleto! ¡Parece que JAXB incluye algunas definiciones importantes en estos archivos!

roto Config:

<plugin> 
    <artifactId>maven-compiler-plugin</artifactId> 
     <version>3.0</version> 
     <configuration> 
      <source>1.6</source> 
      <target>1.6</target> 
      <encoding>UTF-8</encoding> 
     <excludes> 
      <exclude>**/package-info.java</exclude> 
     </excludes> 
     <showDeprecation>true</showDeprecation> 
     <showWarnings>true</showWarnings> 
     <fork>false</fork> 
    </configuration> 
</plugin> 

Config de Trabajo:

<plugin> 
     <artifactId>maven-compiler-plugin</artifactId> 
     <version>3.0</version> 
     <configuration> 
      <source>1.6</source> 
      <target>1.6</target> 
      <encoding>UTF-8</encoding> 
      <showDeprecation>true</showDeprecation> 
      <showWarnings>true</showWarnings> 
      <fork>false</fork> 
     </configuration> 
    </plugin> 
0

que tenía un problema similar cuando se trata de implementar un servicio web basado en jabón primavera. Agregué un paquete al contextPaths de un grano de primavera org.springframework.oxm.jaxb.Jaxb2Marshaller. El problema era que la misma clase estaba en otros paquetes incluidos en el mismo contextPaths. Cambié la secuencia de comandos Ant build para excluir esas otras clases del paquete que estaba agregando, eso resolvió el problema.

Cuestiones relacionadas