Two classes have the same XML type name "objectFactory"
Asked Answered
A

4

9

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.

Asepsis answered 9/9, 2011 at 12:2 Comment(6)
Using Java SE 6 I would recommend using the latest patch release of your JAXB impl that supports JAXB 2.1 unless there is a particular JAXB 2.2 feature you are trying to use.Unwish
Sorry that I didn't mention that. The reason to upgrade to JAXB 2.2.4 is that we are upgrading our JAX-WS version to 2.2.5 and it depends on that version of JAXB (jax-ws.java.net/2.2.5/docs/ReleaseNotes.html). Otherwise we could use JAXB 2.1.Asepsis
It looks like your JAXB impl is mistakenly treating ObjectFactory as a domain class. This is most likely due to the @XmlRegistry annotation not being recognized due to a ClassLoader difference between your domain classes and the JAX-WS implementation. Are you creating the JAXBContext directly or is the JAX-WS implementation doing this?Unwish
No, in this case I'm not using it through JAX-WS. I'm creating directly the JAXBContext passing the context path with all the object factories as a parameter. The code hasn't changed at all, I've just replaced the jars of 2.1.9 by the version 2.2.4.Asepsis
Since you are in an OSGi environment, I believe you are in a case where your domain classes and JAXB impl are referencing different versions of the JAXB binaries. This wasn't happening before since there was only one version of the JAXB binary available. Can you modify your code so that when you create the JAXBContext you pass along the ClassLoader that has your domain classes loaded?Unwish
I've edited the post so you can see the code that we use to instantiate a JAXBContext.Asepsis
A
5

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.

Asepsis answered 20/9, 2011 at 10:5 Comment(0)
D
3
  1. Make sure that there is only one @XmlRegistry annotation in your classpath (Search for XmlRegistry.class file, not usage). Maybe the wrong annotation is picked up.

  2. If that doesn't work, create your own classloader which sees only one factory. That should not be necessary but who knows.

  3. Try to set a breakpoint at JAXBContextImpl.java:436 to see which types it processes and why.

Dwarf answered 9/9, 2011 at 15:7 Comment(3)
Hi, Sorry for the late reply. 1. There is an XmlRegistry.class in jaxb-api-2.2.4.jar (which is inside my bundle "lib.jaxb") and in the Java 1.6 classes.jar. 2. It's difficult to change the implementation, I will try that if I don't succeed with 3. 3. I've been debugging the internals, but let me add my comment in the main post.Asepsis
Thanks for your great effort. Unfortunately, I'm out of my league, now :-( I'm pretty sure it's because of how OSGi/Java does classloading (The rule is to load from the parent CL first, so the Java code has precedence over the classes which OSGi adds to the classpath). As a test, try to specify your JAXB implementation in the bootclasspath (see the docs how to set that: download.oracle.com/javase/1.3/docs/tooldocs/win32/…).Dwarf
Hi Aaron, thanks for your reply. I tried to add the jars to the bootclasspath but it makes no difference, the same exception is thrown.Asepsis
R
2

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>
Rupe answered 21/12, 2012 at 12:44 Comment(0)
C
1

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.

Crow answered 5/9, 2013 at 20:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.