2011-09-09 5 views
5

Nous utilisons JAXB 2.1 depuis longtemps dans notre système. Nous avons une plate-forme qui est construite avec Ant et génère un tas de paquets qui sont déployés dans un environnement d'exécution OSGi. Nous utilisons Java SE 6.Deux classes ont le même nom de type XML "objectFactory"

Nous utilisons JAXB pendant le processus de construction pour générer des types de données à partir de différents schémas. Ces classes sont empaquetées dans les bundles et utilisées à l'exécution pour sérialiser/désérialiser le contenu. En outre, nous utilisons JAXB dans notre plate-forme en cours d'exécution pour générer des types de données à partir d'autres schémas fournis par l'utilisateur (c'est une sorte de plate-forme MDA).

Dans l'environnement d'exécution OSGi, nous avons un ensemble qui contient les fichiers JAXB et exporte les packages nécessaires. Nous créons une instance JAXBContext avec le chemin de contexte de toutes les usines d'objets générées, ainsi nous pouvons marshall/unmarshall tous nos types de données. Cela a fonctionné jusqu'ici mais maintenant nous essayons de mettre à niveau vers la dernière version stable de JAXB (2.2.4) et nous avons des problèmes essayant de créer le contexte dans l'exécution. Nous obtenons l'exception suivante:

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 

L'erreur Deux classes ont le même nom de type XML « ObjectFactory » est imprimé pour chacune des usines d'objets générés au cours du processus de construction.

Nous avons vu plusieurs messages dans SO avec la même erreur mais s'appliquant aux types générés, pas à la fabrique d'objets. Nous pensons que JAXB peut ne pas identifier la classe ObjectFactory en tant que fabrique d'objets mais en tant que type de données.

Une possibilité est que nous utilisions la version interne de JAXB en Java 6, nous avons donc décidé d'utiliser la propriété système -Djava.endorsed.dirs et mettre les trois pots (JAXB-api-2.2.4 .jar, jaxb-impl-2.2.4.jar et jaxb-xjc-2.2.4.jar) dans ce chemin, mais ne fonctionne toujours pas.

Nous pensons que le problème pourrait être que nous utilisons une version différente de JAXB dans le runtime OSGi et dans le processus de construction, donc le code généré n'est pas compatible. Mais peut-être avons-nous tort et il y a un autre problème.

Avez-vous des idées?

Merci d'avance.

(Modifier: plus de détails sur ce sujet)

Nous créons la JAXBContext de cette façon:

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

où contextPath est une chaîne qui contient toutes nos usines d'objets séparés par ':', et le JAXBServiceClassLoader est:

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: après le post Aaron)

J'ai débogué tous les internes de JAXBContextImpl et le fait est que JAXBContextImpl essaie d'obtenir les informations de type de nos classes ObjectFactory, ce qui est faux. En fait, dans com.sun.xml.internal.bind.v2.model.impl.ModelBuilder: 314, l'appel getClassAnnotation() renvoie null mais lorsque je vois l'instance, je peux voir l'annotation XmlRegistry.Le fait est que, à ce stade, XmlRegistry.class.getClassLoader() renvoie null, mais si j'exécute ((Class) c) .getAnnotations() [0] .annotationType(). GetClassLoader() il retourne le classLoader du bundle OSGi "lib.jaxb" qui contient mes jarres JAXB, ce qui est correct. Donc, je suppose que nous chargeons en même temps deux versions différentes de XmlRegistry, l'une du JDK et l'autre des JARB JAVA 2.2.4. La question est: pourquoi? Et, encore plus, au lieu de charger toutes ces classes com.sun.xml.internal. * (Comme JAXBContextImpl), ne pas charger et exécuter com.sun.xml.bind.v2.runtime.JAXBContextImpl à partir de JAXB pots? Au cours du processus de débogage, je peux voir qu'il fait des choses avec réflexion, mais je ne comprends pas pourquoi cela le fait.

+0

Utilisation de Java SE 6 Je vous recommande d'utiliser la dernière version de correctif de votre JAXB impl prenant en charge JAXB 2.1 sauf si vous utilisez une fonctionnalité JAXB 2.2 particulière. –

+0

Désolé que je n'ai pas mentionné cela. La raison de la mise à niveau vers JAXB 2.2.4 est que nous sommes en train de mettre à jour notre version de JAX-WS vers la version 2.2.5 et cela dépend de cette version de JAXB (http://jax-ws.java.net/2.2.5/docs/ ReleaseNotes.html). Sinon, nous pourrions utiliser JAXB 2.1. – Denian

+0

Il semble que votre impl JAXB traite par erreur 'ObjectFactory' en tant que classe de domaine. Cela est probablement dû au fait que l'annotation '@ XmlRegistry' n'est pas reconnue en raison d'une différence de' ClassLoader' entre vos classes de domaine et l'implémentation JAX-WS. Créez-vous le 'JAXBContext' directement ou l'implémentation de JAX-WS le fait-elle? –

Répondre

2

Nous avons finalement trouvé une solution pour cela.

De la documentation JAXB (Découverte de la mise en œuvre JAXB section):

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

Nous avons essayé d'ajouter une ressource dans META-INF/services/javax.xml.bind.JAXBContext dans Afin d'imposer l'utilisation de com.sun.xml.bind.v2.ContextFactory au lieu de l'usine interne de Sun. Cela n'a pas fonctionné, probablement parce que nous utilisons un bundle OSGi.

Quoi qu'il en soit, comme nous utilisons notre propre classloader, nous redéfinissons la méthode getResourceAsStream(), qui est appelé à partir 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); 
} 

Ce n'est pas la plus jolie solution mais ça fonctionne pour nous . Et ce classloader n'est utilisé que lorsque nous créons le JAXBContext, ça devrait aller.

2
  1. Assurez-vous qu'il n'y a qu'une seule annotation @XmlRegistry dans votre classpath (Rechercher les fichiers XmlRegistry.class, non utilisation). Peut-être que la mauvaise annotation est ramassée.

  2. Si cela ne fonctionne pas, créez votre propre classloader qui ne voit qu'une seule usine. Cela ne devrait pas être nécessaire mais qui sait. Essayez de définir un point d'arrêt sur JAXBContextImpl.java:436 pour voir quels types il traite et pourquoi.

+0

Bonjour, Désolé pour la réponse tardive. 1. Il y a un XmlRegistry.class dans jaxb-api-2.2.4.jar (qui se trouve dans mon paquet "lib.jaxb") et dans le Java classes.jar. 2. Il est difficile de changer l'implémentation, je vais essayer si je ne réussis pas avec 3. 3. J'ai débogué les internes, mais laissez-moi ajouter mon commentaire dans le message principal. – Denian

+0

Merci pour votre grand effort. Malheureusement, je suis hors de ma ligue, maintenant :-(Je suis sûr que c'est à cause de la façon dont OSGi/Java fait la classe (La règle est de charger d'abord le CL parent, donc le code Java a préséance sur les classes OSGi ajoute au chemin de classe.) En tant que test, essayez de spécifier votre implémentation JAXB dans le chemin de classe ** boot ** (voir les docs comment définir cela: http://download.oracle.com/javase/1.3/docs/ tooldocs/win32/migration.html # bootcp) –

+0

Salut Aaron, merci pour votre réponse J'ai essayé d'ajouter les jars au bootclasspath mais cela ne fait aucune différence, la même exception est levée. – Denian

0

J'ai eu le même problème, mais une cause différente. Même si cela ne résout pas les problèmes des auteurs, je publie cette réponse pour tous ceux-là, en lisant ce post plus tard et en ayant le même problème que moi. J'ai utilisé cette configuration du compilateur-plugin, qui excluait les fichiers package-info.java de la compilation. Après avoir enlevé les exclus, tout a fonctionné comme un charme! Il semble que JAXB inclut des définitions importantes dans ces fichiers!

brisé 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> 

de travail 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> 
0

J'ai eu un problème similaire en essayant de déployer un savon à base de ressorts service web. J'ai ajouté un paquet au contextPaths d'un haricot de printemps org.springframework.oxm.jaxb.Jaxb2Marshaller. Le problème était que la même classe était dans d'autres paquets inclus dans le même contextPaths. J'ai changé le script de construction Ant pour exclure ces autres classes du paquet que j'ajoutais, cela a résolu le problème.

Questions connexes