¿Por qué JAXB no puede encontrar mi jaxb.index cuando se ejecuta dentro de Apache Felix?

Está justo ahí, en el paquete que debería estar indexando. Aun así, cuando llamo

JAXBContext jc = JAXBContext.newInstance("my.package.name"); 

Tengo una excepción JAXBException que dice

“my.package.name” no contiene ObjectFactory.class o jaxb.index

aunque contiene ambos.

Lo que funciona, pero no es exactamente lo que quiero, es

 JAXBContext jc = JAXBContext.newInstance(my.package.name.SomeClass.class); 

Esta pregunta de varias otras personas aparece en algunas listas de correo y foros, pero aparentemente no recibe respuestas.

Estoy ejecutando esto en OpenJDK 6, así que obtuve los paquetes fuente y puse mi depurador en la biblioteca. Comienza por buscar jaxb.properties, luego busca las propiedades del sistema y, al no encontrar ninguna, intenta crear el contexto predeterminado usando com.sun.internal.xml.bind.v2.ContextFactory. Allí, se lanza la excepción (dentro de ContextFactor.createContext(String ClassLoader, Map) ), pero no puedo ver lo que sucede porque la fuente no está aquí.

ETA :

A juzgar por el código fuente de ContentFactory, encontré aquí , esta es probablemente la pieza de código que no funciona como se esperaba:

 /** * Look for jaxb.index file in the specified package and load it's contents * * @param pkg package name to search in * @param classLoader ClassLoader to search in * @return a List of Class objects to load, null if there weren't any * @throws IOException if there is an error reading the index file * @throws JAXBException if there are any errors in the index file */ private static List loadIndexedClasses(String pkg, ClassLoader classLoader) throws IOException, JAXBException { final String resource = pkg.replace('.', '/') + "/jaxb.index"; final InputStream resourceAsStream = classLoader.getResourceAsStream(resource); if (resourceAsStream == null) { return null; } 

Por mi experiencia previa , supongo que esto tiene que ver con los mecanismos de carga de clase del contenedor OSGi en el que se está ejecutando. Desafortunadamente, todavía estoy un poco fuera de mi scope aquí.

OK, esto requirió bastante investigación, pero la respuesta no es tan sorprendente ni tan complicada:

JAXB no puede encontrar jaxb.index, porque de forma predeterminada, newInstance(String) usa el cargador de clases del subproceso actual (como lo devuelve Thread.getContextClassLoader() ). Esto no funciona dentro de Felix, porque los paquetes OSGi y los hilos del marco tienen cargadores de clases separados.

La solución es obtener un cargador de clases adecuado de algún lugar y usar newInstance(String, ClassLoader) . jaxb.index un cargador de clases adecuado de una de las clases en el paquete que contiene jaxb.index , una opción sensata por razones de flexibilidad probablemente sea ObjectFactory :

 ClassLoader cl = my.package.name.ObjectFactory.class.getClassLoader(); JAXBContext jc = JAXBContext.newInstance("my.package.name", cl); 

Tal vez también podría obtener el cargador de clases que usa la instancia de Bundle , pero no pude averiguar cómo, y la solución anterior me parece segura.

Me enfrenté a un problema similar con el proyecto en el que estoy trabajando. Después de leer http://jaxb.java.net/faq/index.html#classloader, me di cuenta de que JAXBContext no puede encontrar el paquete que contiene jaxb.index.

Trataré de hacer esto lo más claro posible.

Tenemos

 Bundle A -- com.a A.java aMethod() { B.bMethod("com.cC"); } MANIFEST.MF Import-Package: com.b, com.c Bundle B -- com.b B.java bmethod(String className) { Class clazz = Class.forName(className); } Export-Package: com.b Bundle C -- com.c C.java c() { System.out.println("hello i am C"); } Export-Package: com.c 

Para relacionarse con JAXB . la clase B es JAXBContext y bMethod es newInstance ()

Si está familiarizado con las restricciones del paquete OSGi, debe ser muy claro ahora que el paquete B no está importando el paquete com.c, es decir, la clase C no es visible para la clase B, por lo tanto, no puede crear una instancia de C.

La solución sería pasar un ClassLoader a bMethod. Este ClassLoader debe provenir de un paquete que esté importando com.c. En este caso, podemos pasar A.class.getClassLoader () ya que el paquete A está importando com.c

Espero que esto haya sido útil.

Para el mismo problema, lo resolví poniendo manualmente el paquete en la importación.

Si está utilizando maven en su proyecto, simplemente use esta biblioteca:

  com.sun.xml.bind jaxb-osgi 2.2.7  

Se creó para el servidor Glasfish, pero también funciona con Tomcat (marcado). Con esta biblioteca puede usar JAXB fácilmente con paquetes OSGI.

Edición 2:

Una vez tuve un extraño problema de carga de clases en mi aplicación. Si lo ejecuto como una aplicación normal, todo estaba bien, pero cuando lo invocé como un servicio de Windows, comenzó a fallar con las excepciones ClassNotFoundExceptions. El análisis mostró que los subprocesos tienen sus cargadores de clases como nulos de alguna manera. Resolví el problema configurando SystemClassLoader en los hilos:

 // ... thread.setContextClassLoader(ClassLoader.getSystemClassLoader()); thread.start(); // ... 

No sé si su contenedor permite este tipo de cambio sin embargo.

Acabo de encontrarme con este problema. Para mí, la solución fue utilizar JRE de IBM en lugar de Oracle. Parece que la implementación de JAXB es más amigable con OSGI en eso.

ObjectFactory esto con éxito agregando el paquete de mis clases generadas que contenían ObjectFactory a la parte de la definición de mi paquete, más org.jvnet.jaxb2_commons.*

Puede haber otro escenario que puede dar este problema.

Cuando instala e inicia un paquete que exporta el paquete que contiene el jaxb.index o objectFactory.java

Luego, asegúrese de que los paquetes que importan las clases estén detenidos o que apunten al nombre del paquete correcto.

También revise las declaraciones de exportación e importación en el pom.xml

Se enfrentó a un problema similar en el contenedor osgi servicemix (karaf)

Para mí, el problema era que una prueba de unidad que no estaba relacionada con el módulo que he desarrollado no tenía una dependencia en el pom.xml de mi módulo. La UT aún reconoció mi módulo debido a que recuperó la lista de paquetes del archivo de configuración compartido.

Al ejecutar el UT, no compiló el nuevo módulo, por lo que no generó el ObjectFactory.java, por lo que recibí el error, aunque cuando compilé el módulo pude ver el ObjectFactory.java

Agregó la siguiente dependencia:

  com.myCompany my-module-name ${project.version} test  

Mi solución fue:

JAXBContext context = JAXBContext.newInstance ( new Class [] {“my.package.name”} );

O

JAXBContext context = JAXBContext.newInstance ( new Class [] {class.getName ()} );

O

una solución completa:

 public static  T deserializeFile(Class _class, String _xml) { try { JAXBContext context = JAXBContext.newInstance(new Class[]{_class}); Unmarshaller um = context.createUnmarshaller(); File file = new File(_xml); Object obj = um.unmarshal(file); return _class.cast(obj); } catch (JAXBException exc) { return null; } } 

Trabaja 100%