Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't JAXB find my jaxb.index when running inside Apache Felix?

It's right there, in the package that it should be indexing. Still, when I call

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

I get a JAXBException saying that

"my.package.name" doesnt contain ObjectFactory.class or jaxb.index

although it does contain both.

What does work, but isn't quite what I want, is

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

This question from various other people appears on quite some mailing lists and forums but seemingly doesn't get answers.

I'm running this on OpenJDK 6, so I got the source packages and stepped my debugger into the library. It starts by looking for jaxb.properties, then looks for system properties and failing to find either, it tries to create the default context using com.sun.internal.xml.bind.v2.ContextFactory. In there, the Exception gets thrown (inside ContextFactor.createContext(String ClassLoader, Map)), but I can't see what's going on because the source isn't here.

ETA:

Judging from the source code for ContentFactory, I found here, this is probably the piece of code that fails to work as intended:

/**
 * 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<Class> 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;
    }

From my previous experience, I'm guessing that this has to do with the class loading mechanisms of the OSGi container that this is running in. Unfortunately, I am still a little out of my depth here.

like image 391
Hanno Fietz Avatar asked Jun 25 '09 10:06

Hanno Fietz


4 Answers

OK, this took quite some digging, but the answer is not that surprising and not even that complicated:

JAXB can't find jaxb.index, because by default, newInstance(String) uses the current thread's class loader (as returned by Thread.getContextClassLoader()). This doesn't work inside Felix, because the OSGi bundles and the framework's threads have separate class loaders.

The solution is to get a suitable class loader from somewhere and use newInstance(String, ClassLoader). I got a suitable class loader from one of the classes in the package that contains jaxb.index, a sensible choice for flexibility reasons probably is ObjectFactory:

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

Maybe you could also get at the class loader that the Bundle instance is using, but I couldn't figure out how, and the above solution seems safe to me.

like image 94
Hanno Fietz Avatar answered Nov 20 '22 12:11

Hanno Fietz


I faced similar issue with the project I am working on. After reading http://jaxb.java.net/faq/index.html#classloader I realized that JAXBContext is not able to find the package containing jaxb.index.

I will try to make this as clear as possible.

We have

Bundle A
   -- com.a
      A.java
        aMethod()
        {
            B.bMethod("com.c.C");
        }
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

To relate to JAXB. class B is JAXBContext and bMethod is newInstance()

If you are familiar with OSGi package restrictions then it must be very clear now that Bundle B is not Importing package com.c i.e class C is not visible to class B hence it cannot instantiate C.

The solution would be to pass a ClassLoader to bMethod. This ClassLoader should come from a bundle that is importing com.c. In this case we can pass A.class.getClassLoader() since bundle A is importing com.c

Hope this was helpful.

like image 30
Chaits Avatar answered Nov 20 '22 10:11

Chaits


For the same problem, I resolved it by manually putting the package into the import.

like image 4
Hart Avatar answered Nov 20 '22 10:11

Hart


If you are using maven in your project, then just use this library:

<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-osgi</artifactId>
    <version>2.2.7</version>
</dependency>

It's created for Glasfish server but also working with Tomcat (checked). With this library you can easly use JAXB with OSGI bundles.

like image 1
wierzbiks Avatar answered Nov 20 '22 10:11

wierzbiks