两个类具有相同的XML类型名称“objectFactory”

时间:2011-09-09 12:02:10

标签: java jaxb jax-ws osgi jaxb2

我们在系统中长期使用JAXB 2.1。我们有一个使用Ant构建的平台,并生成一组部署在OSGi运行时中的bundle。我们使用Java SE 6。

我们在构建过程中使用JAXB从不同的模式生成数据类型。这些类打包在bundle中,并在运行时用于序列化/反序列化内容。此外,我们在运行时在我们的平台中使用JAXB来从用户提供的其他模式生成数据类型(它是一种MDA平台)。

在OSGi运行时,我们有一个包含JAXB jar的bundle并导出必要的包。我们使用生成的所有对象工厂的上下文路径创建一个JAXBContext实例,因此我们可以编组/取消编组所有数据类型。

到目前为止一直有效,但是现在我们正在尝试升级到JAXB (2.2.4)的最新稳定版本,我们在尝试在运行时创建上下文时遇到问题。我们得到以下例外:

Two classes have the same XML type name "objectFactory". Use @XmlType.name and @XmlType.namespace to assign different names to them.
    this problem is related to the following location:
        at some.package.ObjectFactory
    this problem is related to the following location:
        at some.other.package.ObjectFactory

    at com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:91)
    at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:436)
    at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:277)
    at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1100)
    at com.sun.xml.internal.bind.v2.ContextFactory.createContext(ContextFactory.java:143)
    at com.sun.xml.internal.bind.v2.ContextFactory.createContext(ContextFactory.java:110)
    at com.sun.xml.internal.bind.v2.ContextFactory.createContext(ContextFactory.java:191)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:187)
    ... 76 more

错误两个类具有相同的XML类型名称“objectFactory”是为构建过程中生成的每个对象工厂打印的。

我们在SO中看到了几个具有相同错误的帖子,但是应用于生成的类型,而不是对象工厂。我们认为JAXB可能没有将ObjectFactory类标识为对象工厂,而是将其标识为数据类型。

一种可能性是我们在Java 6中使用了JAXB的内部版本,因​​此我们决定使用系统属性 -Djava.endorsed.dirs 并放置三个罐子( jaxb -api-2.2.4.jar,jaxb-impl-2.2.4.jar和jaxb-xjc-2.2.4.jar )在该路径中,但仍无效。

我们认为问题可能是我们在OSGi运行时和构建过程中使用了不同版本的JAXB,因此生成的代码不兼容。但也许我们错了,还有另一个问题。

你有什么想法吗?

提前致谢。

(编辑:有关此内容的更多详情)

我们以这种方式创建JAXBContext:

    ClassLoader classLoader = new JAXBServiceClassLoader(getParentClassLoader(),
                                                         Collections.unmodifiableMap(objectFactories));
    context = JAXBContext.newInstance(contextPath.toString(), classLoader);

其中contextPath是一个String,它包含以':'分隔的所有对象工厂,而JAXBServiceClassLoader是:

  private static final class JAXBServiceClassLoader extends ClassLoader
  {
    @NotNull
    private final Map<String, Object> objectFactories;

    private JAXBServiceClassLoader(@NotNull ClassLoader parent, @NotNull Map<String, Object> objectFactories)
    {
      super(parent);
      this.objectFactories = objectFactories;
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException
    {
      Class<?> ret;
      try
      {
        ret = super.loadClass(name);
      }
      catch (ClassNotFoundException e)
      {
        Object objectFactory = objectFactories.get(name);
        if (objectFactory != null)
        {
          ret = objectFactory.getClass();
        }
        else
        {
          throw new ClassNotFoundException(name + " class not found");
        }
      }
      return ret;
    }
  }

(编辑:在Aaron的帖子之后)

我一直在调试JAXBContextImpl的所有内部,事实是JAXBContextImpl试图从我们的ObjectFactory类中获取类型信息,这是错误的。实际上,在com.sun.xml.internal.bind.v2.model.impl.ModelBuilder:314中,getClassAnnotation()调用返回null,但是当我看到实例时,我可以看到注释XmlRegistry。

问题在于,此时XmlRegistry.class.getClassLoader()返回null,但是如果我运行((Class)c).getAnnotations()[0] .annotationType()。getClassLoader()它返回classLoader OSGi包“lib.jaxb”包含我的JAXB jar,这是正确的。

所以,我猜我们正在同时加载两个不同版本的XmlRegistry,一个来自JDK,另一个来自JAXB 2.2.4 jar。问题是:为什么?

而且,除了加载所有com.sun.xml.internal。*类(如JAXBContextImpl)之外,不应该从JAXB加载和执行com.sun.xml.bind.v2.runtime.JAXBContextImpl。罐子?在调试过程中,我可以看到它正在用反射做一些事情,但我不明白为什么这样做。

4 个答案:

答案 0 :(得分:3)

我们终于找到了解决方案。

从JAXB文档( JAXB实现的发现部分):

http://jaxb.java.net/nonav/2.2.4-1/docs/api/javax/xml/bind/JAXBContext.html

我们尝试在 META-INF / services / javax.xml.bind.JAXBContext 中添加资源,以强制使用 com.sun.xml.bind.v2。 ContextFactory 而不是Sun的内部工厂。这不起作用,可能是因为我们使用的是OSGi包。

无论如何,当我们使用自己的类加载器时,我们覆盖从 ContextFinder:343 调用的getResourceAsStream()方法:

@Override
public InputStream getResourceAsStream(String name)
{
  if (name!=null && name.equals("META-INF/services/javax.xml.bind.JAXBContext"))
  {
    return new ByteArrayInputStream("com.sun.xml.bind.v2.ContextFactory".getBytes());
  }
  return super.getResourceAsStream(name);
}

这不是最漂亮的解决方案,但它对我们有用。而且只有在我们创建JAXBContext时才使用该类加载器,它应该没问题。

答案 1 :(得分:2)

  1. 确保类路径中只有一个@XmlRegistry注释(搜索XmlRegistry.class文件,而不是用法)。也许拿起了错误的注释。

  2. 如果这不起作用,请创建自己的类加载器,只能看到一个工厂。这不应该是必要的,但谁知道。

  3. 尝试在JAXBContextImpl.java:436设置断点,以查看它处理的类型和原因。

答案 2 :(得分:0)

我有同样的问题,但原因不同。即使它可能无法解决作者的问题,我也会为所有人发布这个答案,稍后阅读这篇文章并遇到与我相同的问题。

我使用了这个编译器插件配置,它从编译中排除了package-info.java文件。删除后排除所有工作就像一个魅力!似乎JAXB在这些文件中包含了一些重要的定义!

破碎的配置:

<plugin>
    <artifactId>maven-compiler-plugin</artifactId>
        <version>3.0</version>
        <configuration>
            <source>1.6</source>
            <target>1.6</target>
            <encoding>UTF-8</encoding>
        <excludes>
            <exclude>**/package-info.java</exclude>
        </excludes>
        <showDeprecation>true</showDeprecation>
        <showWarnings>true</showWarnings>
        <fork>false</fork>
    </configuration>
</plugin>

工作配置:

 <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.0</version>
        <configuration>
            <source>1.6</source>
            <target>1.6</target>
            <encoding>UTF-8</encoding>
            <showDeprecation>true</showDeprecation>
            <showWarnings>true</showWarnings>
            <fork>false</fork>
        </configuration>
    </plugin>

答案 3 :(得分:0)

在尝试部署基于Spring的soap Web服务时,我遇到了类似的问题。我在contextPaths spring bean的org.springframework.oxm.jaxb.Jaxb2Marshaller添加了一个包。问题是同一个类在同一contextPaths中包含的其他包中。我更改了Ant构建脚本,以从我添加的包中排除那些其他类,这解决了这个问题。