Two classes have the same XML type name "objectFactory"
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 fro开发者_运维知识库m 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.
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:
@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);
}
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.
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.
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:
<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>
Working Config:
<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>
I had a similar issue when trying to deploy a spring-based soap web service. I added a package to the contextPaths
of a org.springframework.oxm.jaxb.Jaxb2Marshaller
spring bean. The problem was that the same class was in other packages included in the same contextPaths
. I changed the Ant build script to exclude those other classes from the package I was adding, that that resolved the issue.
精彩评论