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

爱⌒轻易说出口 提交于 2019-11-29 19:57:32

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.

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.

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

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.

Edit 2:

I once had similar strange class loading problem in my application. If I run it as a normal application, everything was OK but when I invoked it as a Windows Service, it started to fail with ClassNotFoundExceptions. The analysis showed that the threads have their classloaders as null somehow. I solved the problem by setting the SystemClassLoader on the threads:

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

Don't know if your container allows this kind of change though.

I've just run into this issue. For me, the solution was to use IBM's JRE instead of Oracle's. Seems like the JAXB implementation is more OSGI-friendly in that one.

I successfully resolved this by adding the package of my generated classes containing ObjectFactory to the <Private-Package> part of my bundle definition, plus org.jvnet.jaxb2_commons.*

There may be another scenario which can give this problem.

When you install and start a bundle which export the package that contains the jaxb.index or objectFactory.java

Then please make sure that the bundles importing the classes are stopped or pointing to the correct package name.

Also check the export and import statements in the pom.xml

Faced similar issue in servicemix(karaf) osgi container

For me the problem was that a unit test which was not related to the module that I have developed did not had a dependency in it pom.xml to my module. The UT still recognized my module due to fetching the packages list from shared configuration file.

When running the UT it didn't compile the new module so it didn't generate the ObjectFactory.java therefore I received the error even though when I compiled the module I was able to see the ObjectFactory.java

added the following dependency:

<dependency>
    <groupId>com.myCompany</groupId>
    <artifactId>my-module-name</artifactId>
    <version>${project.version}</version>
    <scope>test</scope>
</dependency>

My Solution was:

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

OR

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

OR

a full solution:

public static <T> T deserializeFile(Class<T> _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;
        }
    }

Works 100%

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!