Den*_*ian 7 java osgi jax-ws jaxb 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
Run Code Online (Sandbox Code Playgroud)
错误两个类具有相同的XML类型名称"objectFactory",用于在构建过程中生成的每个对象工厂.
我们在SO中看到了几个具有相同错误的帖子,但是应用于生成的类型,而不是对象工厂.我们认为JAXB可能没有将ObjectFactory类标识为对象工厂,而是将其标识为数据类型.
一种可能性是我们在Java 6中使用JAXB的内部版本,因此我们决定使用System Property -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);
Run Code Online (Sandbox Code Playgroud)
其中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;
}
}
Run Code Online (Sandbox Code Playgroud)
(编辑:亚伦之后)
我一直在调试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.问题是:为什么?
而且,除了加载所有com.sun.xml.internal.*类(如JAXBContextImpl)之外,不应该从JAXB jar加载和执行com.sun.xml.bind.v2.runtime.JAXBContextImpl?在调试过程中,我可以看到它正在用反射做一些事情,但我不明白为什么这样做.
我们终于找到了解决这个问题的办法。
来自 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 包。
无论如何,当我们使用自己的类加载器时,我们重写 getResourceAsStream() 方法,该方法是从ContextFinder:343调用的:
@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);
}
Run Code Online (Sandbox Code Playgroud)
这不是最漂亮的解决方案,但它对我们有用。并且该类加载器仅在我们创建 JAXBContext 时使用,应该没问题。