我们的系统中已经使用 JAXB 2.1 很长时间了。我们有一个使用 Ant 构建的平台,并生成一堆部署在 OSGi 运行时中的包。我们使用 Java SE 6。
我们在构建过程中使用 JAXB 从不同模式生成数据类型。这些类打包在捆绑包中,并在运行时用于序列化/反序列化内容。此外,我们在运行时在我们的平台中使用 JAXB 从用户提供的其他模式(它是一种 MDA 平台)生成数据类型。
在 OSGi 运行时中,我们有一个包,其中包含 JAXB jar 并导出必要的包。我们使用生成的所有对象工厂的上下文路径创建一个 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 并放置三个 jar (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 是一个字符串,其中包含用“:”分隔的所有对象工厂,而 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() 它会返回 OSGi 的 classLoader包含我的 JAXB jar 的包“lib.jaxb”,这是正确的。
因此,我猜想我们同时加载了两个不同版本的 XmlRegistry,一个来自 JDK,另一个来自 JAXB 2.2.4 jar。问题是:为什么?
而且,更重要的是,不应该从 JAXB jar 加载和执行 com.sun.xml.bind.v2.runtime.JAXBContextImpl,而不是加载所有这些 com.sun.xml.internal.* 类(如 JAXBContextImpl)?在调试过程中,我可以看到它正在通过反射执行一些操作,但我不明白为什么要这样做。
We have been using JAXB 2.1 for a long time in our system. We have a platform that is built with Ant and generates a bunch of bundles that are deployed in an OSGi runtime. We use Java SE 6.
We use JAXB during the build process to generate datatypes from different schemas. Those classes are packaged in the bundles and used in runtime to serialize/deserialize content. In addition we use JAXB in our platform in runtime to generate datatypes from other schemas provided by the user (it a sort of MDA platform).
In the OSGi runtime we have a bundle that has the JAXB jars and exports the necessary packages. We create a JAXBContext instance with the context path of all the object factories generated, so we can marshall/unmarshall all our datatypes.
That has been working so far but right now we are trying to upgrade to the latest stable version of JAXB (2.2.4) and we are having problems trying to create the context in runtime. We get the following exception:
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
The error Two classes have the same XML type name "objectFactory" is printed for each of the object factories generated during the build process.
We have seen several posts in SO with the same error but applying to the generated types, not to the object factory. We think that JAXB may not be identifying the ObjectFactory class as an object factory but as a datatype.
One possibility was that we were using the internal version of JAXB in Java 6, so we decided to use the System Property -Djava.endorsed.dirs and put the three jars (jaxb-api-2.2.4.jar, jaxb-impl-2.2.4.jar and jaxb-xjc-2.2.4.jar) in that path, but still not working.
We think that the problem might be that we are using a different version of JAXB in the OSGi runtime and in the build process, so the generated code is not compatible. But maybe we are wrong and there is another problem.
Do you have any ideas?
Thanks in advance.
(Edit: more details on this)
We create the JAXBContext in this way:
ClassLoader classLoader = new JAXBServiceClassLoader(getParentClassLoader(),
Collections.unmodifiableMap(objectFactories));
context = JAXBContext.newInstance(contextPath.toString(), classLoader);
where contextPath is a String that contains all our object factories separated by ':', and the JAXBServiceClassLoader is:
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;
}
}
(Edit: after Aaron's post)
I've been debugging all the internals of JAXBContextImpl and the thing is that the JAXBContextImpl is trying to get the type info from our ObjectFactory classes, which is wrong. In fact, in com.sun.xml.internal.bind.v2.model.impl.ModelBuilder:314, the getClassAnnotation() call returns null but when I see the instance I can see the annotation XmlRegistry.
The thing is that, at that point XmlRegistry.class.getClassLoader() returns null, but if I run ((Class)c).getAnnotations()[0].annotationType().getClassLoader() it returns the classLoader of the OSGi bundle "lib.jaxb" that contains my JAXB jars, which is correct.
So, I guess that we are loading at the same time two different versions of XmlRegistry, one from the JDK and the other one from JAXB 2.2.4 jars. The question is: why?
And, even more, instead of loading all those com.sun.xml.internal.* classes (like JAXBContextImpl), shouldn't be loading and executing com.sun.xml.bind.v2.runtime.JAXBContextImpl from JAXB jars? During the debug process I can see that it's doing some stuff with reflection but I don't understand why is doing that.
发布评论
评论(4)
我们终于找到了解决这个问题的办法。
来自 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() 方法:
这不是最漂亮的解决方案,但它对我们有用。并且该类加载器仅在我们创建 JAXBContext 时使用,应该没问题。
We finally found a solution for this.
From the JAXB documentation (Discovery of JAXB implementation section):
http://jaxb.java.net/nonav/2.2.4-1/docs/api/javax/xml/bind/JAXBContext.html
We tried to add a resource in META-INF/services/javax.xml.bind.JAXBContext in order to enforce the use of com.sun.xml.bind.v2.ContextFactory instead of Sun's internal factory. That didn't work, probably because we are using an OSGi bundle.
Anyway, as we are using our own classloader, we override the getResourceAsStream() method, that is called from ContextFinder:343:
This is not the prettiest solution but it works for us. And that classloader is used only when we create the JAXBContext, it should be fine.
确保类路径中只有一个
@XmlRegistry
注释(搜索XmlRegistry.class
文件,而不是用法)。也许选择了错误的注释。如果这不起作用,请创建您自己的类加载器,它只能看到一个工厂。这应该不是必需的,但谁知道呢。
尝试在 JAXBContextImpl.java:436 处设置断点以查看它处理哪些类型以及原因。
Make sure that there is only one
@XmlRegistry
annotation in your classpath (Search forXmlRegistry.class
file, not usage). Maybe the wrong annotation is picked up.If that doesn't work, create your own classloader which sees only one factory. That should not be necessary but who knows.
Try to set a breakpoint at JAXBContextImpl.java:436 to see which types it processes and why.
我有同样的问题,但原因不同。即使它可能无法解决作者的问题,我也会为所有这些问题发布这个答案,稍后阅读这篇文章并遇到与我相同的问题。
我使用了这个编译器插件配置,它从编译中排除了 package-info.java 文件。删除排除后,一切都像魅力一样!看来 JAXB 在这些文件中包含了一些重要的定义!
损坏的配置:
工作配置:
I had the same problem, but a different cause. Even if it might not fix the authors problems I post this answer for all those, reading this post later on and having the same issue as me.
I used this compiler-plugin configuration, which excluded package-info.java files from compilation. After removing the excludes everything worked like a charm! It seems that JAXB include some important definitions in these files!
Broken Config:
Working Config:
在尝试部署基于 spring 的肥皂网络服务时,我遇到了类似的问题。我向
org.springframework.oxm.jaxb.Jaxb2Marshaller
spring bean 的contextPaths
添加了一个包。问题是同一类位于同一contextPaths
中包含的其他包中。我更改了 Ant 构建脚本,从我添加的包中排除其他类,从而解决了问题。I had a similar issue when trying to deploy a spring-based soap web service. I added a package to the
contextPaths
of aorg.springframework.oxm.jaxb.Jaxb2Marshaller
spring bean. The problem was that the same class was in other packages included in the samecontextPaths
. I changed the Ant build script to exclude those other classes from the package I was adding, that that resolved the issue.