为什么JAXB在Apache Felix中运行时找不到我的jaxb.in​​dex?

Han*_*etz 52 java osgi jaxb apache-felix

它就在那里,在它应该索引的包中.我还是打电话的时候

JAXBContext jc = JAXBContext.newInstance("my.package.name");
Run Code Online (Sandbox Code Playgroud)

我得到一个JAXBException说

"my.package.name"不包含ObjectFactory.class或jaxb.in​​dex

虽然它确实包含两者.

什么工作,但不是我想要的,是

JAXBContext jc = JAXBContext.newInstance(my.package.name.SomeClass.class);
Run Code Online (Sandbox Code Playgroud)

来自其他人的这个问题出现在一些邮件列表和论坛上,但似乎没有得到答案.

我在OpenJDK 6上运行它,所以我获得了源包并将调试器放入库中.它首先查找jaxb.properties,然后查找系统属性并且无法找到,它尝试使用com.sun.internal.xml.bind.v2.ContextFactory创建默认上下文.在那里,异常被抛出(内部ContextFactor.createContext(String ClassLoader, Map)),但我看不到发生了什么,因为源不在这里.

ETA:

从ContentFactory的源代码判断,我在这里发现,这可能是那些无法按预期工作的代码:

/**
 * 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;
    }
Run Code Online (Sandbox Code Playgroud)

根据我以前的 经验,我猜这与运行的OSGi容器的类加载机制有关.不幸的是,我仍然有点超出我的深度.

Han*_*etz 60

好的,这需要花费一些时间,但答案并不令人惊讶,甚至不是那么复杂:

JAXB找不到jaxb.in​​dex,因为默认情况下,newInstance(String)使用当前线程的类加载器(由返回Thread.getContextClassLoader()).这在Felix中不起作用,因为OSGi包和框架的线程具有单独的类加载器.

解决方案是从某个地方获得合适的类加载器并使用newInstance(String, ClassLoader).我从包含的jaxb.index一个类中得到了一个合适的类加载器,灵活性的一个明智选择可能是ObjectFactory:

ClassLoader cl = my.package.name.ObjectFactory.class.getClassLoader();
JAXBContext jc = JAXBContext.newInstance("my.package.name", cl);
Run Code Online (Sandbox Code Playgroud)

也许你也可以得到Bundle实例正在使用的类加载器,但我无法弄清楚如何,以上解决方案对我来说似乎是安全的.


小智 6

我遇到了与我正在进行的项目类似的问题.在阅读http://jaxb.java.net/faq/index.html#classloader后,我意识到JAXBContext无法找到包含jaxb.in​​dex的包.

我会尽量让这个尽可能清楚.

我们有

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
Run Code Online (Sandbox Code Playgroud)

JAXB相关.B类是JAXBContext,bMethod是newInstance()

如果您熟悉的OSGi包的限制,那么它必须很清楚,现在捆B未导入包com.cC类不可见的B类,因此它无法实例C.

解决方案是将ClassLoader传递给bMethod.此ClassLoader应来自导入com.c.在这种情况下,我们可以传递A.class.getClassLoader(),因为bundle A正在导入com.c

希望这有用.