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

时间:2009-06-25 10:21:51

标签: java jaxb osgi apache-felix

它就在那里,在它应该索引的包中。不过,当我打电话时

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

我得到一个JAXBException,说

  

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

虽然它确实包含两者。

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

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

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

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

ETA

从ContentFactory的源代码判断,我发现here,这可能是代码无法正常工作的代码:

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

从我的previous experience,我猜这与运行的OSGi容器的类加载机制有关。不幸的是,我仍然有点不在我的深度在这里。

10 个答案:

答案 0 :(得分: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);

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

答案 1 :(得分: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

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

如果您熟悉OSGi程序包限制,那么现在必须非常清楚 Bundle B 不导入包 com.c ,即 C类 不可见 B类因此无法实例化C。

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

希望这有用。

答案 2 :(得分:4)

对于同样的问题,我通过手动将包放入导入来解决它。

答案 3 :(得分:1)

如果您在项目中使用maven,那么只需使用此库:

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

它是为Glasfish服务器创建的,但也使用Tomcat(已检查)。 使用此库,您可以轻松地将JAXB与OSGI包一起使用。

答案 4 :(得分:0)

编辑2:

我曾经在我的应用程序中遇到类似奇怪的类加载问题。如果我将它作为普通应用程序运行,一切都很好但是当我将其作为Windows服务调用时,它开始因ClassNotFoundExceptions而失败。分析表明,线程的类加载器以某种方式为null。我通过在线程上设置SystemClassLoader解决了这个问题:

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

不知道你的容器是否允许这种改变。

答案 5 :(得分:0)

我刚遇到这个问题。对我来说,解决方案是使用IBM的JRE而不是Oracle。似乎JAXB实现在那个实现中对OSGI更友好。

答案 6 :(得分:0)

我成功解决了这个问题,方法是将我生成的包含ObjectFactory的类的包添加到我的包定义的<Private-Package>部分,再加上org.jvnet.jaxb2_commons.*

答案 7 :(得分:0)

可能有另一种情况可以解决这个问题。

安装并启动导出包含jaxb.in​​dex或objectFactory.java的包的包时

然后请确保导入类的包已停止或指向正确的包名。

还要检查pom.xml中的export和import语句

面对servicemix(karaf)osgi容器中的类似问题

答案 8 :(得分:0)

对我来说,问题是与我开发的模块无关的单元测试没有依赖于pom.xml到我的模块。 由于从共享配置文件中获取包列表,UT仍然识别我的模块。

当运行UT时,它没有编译新模块,因此它没有生成ObjectFactory.java,因此我收到错误,即使我编译模块时我能够看到ObjectFactory.java

添加了以下依赖项:

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

答案 9 :(得分:-1)

我的解决方案是:

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

OR

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

OR

完整的解决方案:

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;
        }
    }

100%工作