Perché JAXB non riesce a trovare il mio jaxb.index durante l'esecuzione all'interno di Apache Felix?

StackOverflow https://stackoverflow.com/questions/1043109

  •  20-08-2019
  •  | 
  •  

Domanda

È proprio lì, nel pacchetto che dovrebbe essere indicizzato. Tuttavia, quando chiamo

JAXBContext jc = JAXBContext.newInstance("my.package.name");

Ricevo una JAXBException che lo diceva

  

quot &; & My.package.name quot; non contiene ObjectFactory.class o jaxb.index

sebbene contenga entrambi.

Ciò che funziona, ma non è esattamente quello che voglio, è

JAXBContext jc = JAXBContext.newInstance(my.package.name.SomeClass.class);

Questa domanda di varie altre persone appare su alcune mailing list e forum ma sembra che non ottenga risposte.

Sto eseguendo questo su OpenJDK 6, quindi ho preso i pacchetti sorgente e ho inserito il mio debugger nella libreria. Inizia cercando jaxb.properties, quindi cerca le proprietà di sistema e non riesce a trovarle, tenta di creare il contesto predefinito utilizzando com.sun.internal.xml.bind.v2.ContextFactory. Lì, viene generata l'eccezione (all'interno di ContextFactor.createContext(String ClassLoader, Map)), ma non riesco a vedere cosa sta succedendo perché la fonte non è qui.

ETA :

A giudicare dal codice sorgente per ContentFactory, ho trovato qui , questo è probabilmente il pezzo di codice che non funziona come previsto:

/**
 * Look for jaxb.index file in the specified package and load it's contents
 *
 * @param pkg package name to search in
 * @param classLoader ClassLoader to search in
 * @return a List of Class objects to load, null if there weren't any
 * @throws IOException if there is an error reading the index file
 * @throws JAXBException if there are any errors in the index file
 */
private static List<Class> loadIndexedClasses(String pkg, ClassLoader classLoader) throws IOException, JAXBException {
    final String resource = pkg.replace('.', '/') + "/jaxb.index";
    final InputStream resourceAsStream = classLoader.getResourceAsStream(resource);

    if (resourceAsStream == null) {
        return null;
    }

Dal mio precedente experience , suppongo che questo debba fare con i meccanismi di caricamento di classe del contenitore OSGi in cui questo è in esecuzione. Sfortunatamente, sono ancora un po 'fuori dalla mia profondità qui.

È stato utile?

Soluzione

OK, ci sono voluti parecchi scavi, ma la risposta non è così sorprendente e nemmeno così complicata:

JAXB non riesce a trovare jaxb.index, perché per impostazione predefinita newInstance(String) utilizza il programma di caricamento classi del thread corrente (come restituito da Thread.getContextClassLoader()). Questo non funziona all'interno di Felix, perché i bundle OSGi e i thread del framework hanno caricatori di classi separati.

La soluzione è quella di ottenere un caricatore di classe adatto da qualche parte e utilizzare newInstance(String, ClassLoader). Ho ottenuto un caricatore di classi adatto da una delle classi nel pacchetto che contiene jaxb.index, una scelta ragionevole per motivi di flessibilità probabilmente è ObjectFactory:

ClassLoader cl = my.package.name.ObjectFactory.class.getClassLoader();
JAXBContext jc = JAXBContext.newInstance("my.package.name", cl);

Forse potresti anche ottenere dal caricatore di classi che l'istanza Bundle sta usando, ma non sono riuscito a capire come, e la soluzione sopra mi sembra sicura.

Altri suggerimenti

Ho riscontrato problemi simili con il progetto a cui sto lavorando. Dopo aver letto http://jaxb.java.net/faq/index.html#classloader ho capito che JAXBContext non è in grado di trovare il pacchetto contenente jaxb.index.

Proverò a renderlo il più chiaro possibile.

Abbiamo

Bundle A
   -- com.a
      A.java
        aMethod()
        {
            B.bMethod("com.c.C");
        }
MANIFEST.MF
Import-Package: com.b, com.c         

Bundle B
   -- com.b
      B.java
        bmethod(String className)
        {
            Class clazz = Class.forName(className);
        }

Export-Package: com.b

Bundle C
   -- com.c
      C.java
        c()
        {
            System.out.println("hello i am C");
        }

Export-Package: com.c

Per fare riferimento a JAXB . la classe B è JAXBContext e bMethod è newInstance ()

Se hai familiarità con le restrizioni del pacchetto OSGi, ora deve essere molto chiaro che Bundle B non sta importando il pacchetto com.c cioè classe C è non visibile in classe B quindi non può creare un'istanza C.

La soluzione sarebbe quella di passare un ClassLoader a bMethod. Questo ClassLoader dovrebbe provenire da un pacchetto che sta importando com.c . In questo caso possiamo passare A.class.getClassLoader () poiché il pacchetto A sta importando com.c

Spero che sia stato utile.

Per lo stesso problema, l'ho risolto inserendo manualmente il pacchetto nell'importazione.

Se stai usando Maven nel tuo progetto, usa questa libreria:

<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-osgi</artifactId>
    <version>2.2.7</version>
</dependency>

È stato creato per il server Glasfish ma funziona anche con Tomcat (selezionato). Con questa libreria è possibile utilizzare facilmente JAXB con i bundle OSGI.

Modifica 2:

Una volta ho avuto un problema di caricamento di classe simile nella mia applicazione. Se lo eseguo come una normale applicazione, tutto andava bene ma quando l'ho invocato come un servizio Windows, ha iniziato a fallire con ClassNotFoundExceptions. L'analisi ha mostrato che i thread hanno i loro classloader come nulli in qualche modo. Ho risolto il problema impostando SystemClassLoader sui thread:

// ...
thread.setContextClassLoader(ClassLoader.getSystemClassLoader());
thread.start();
// ...

Non so se il tuo contenitore consente questo tipo di modifica però.

Ho appena riscontrato questo problema. Per me, la soluzione era usare JRE di IBM anziché Oracle. Sembra che l'implementazione di JAXB sia più compatibile con OSGI in questo.

L'ho risolto con successo aggiungendo il pacchetto delle mie classi generate contenente ObjectFactory alla <Private-Package> parte della mia definizione di bundle, più org.jvnet.jaxb2_commons.*

Potrebbe esserci un altro scenario che può dare questo problema.

Quando installi e avvii un bundle che esporta il pacchetto che contiene jaxb.index o objectFactory.java

Quindi assicurati che i bundle che importano le classi siano fermati o puntino al nome del pacchetto corretto.

Controlla anche le dichiarazioni di esportazione e importazione in pom.xml

Si è verificato un problema simile nel contenitore osgi di servicemix (karaf)

Per me il problema era che un test unitario che non era correlato al modulo che avevo sviluppato non aveva una dipendenza in esso pom.xml con il mio modulo. L'UT ha ancora riconosciuto il mio modulo a causa del recupero dell'elenco dei pacchetti dal file di configurazione condiviso.

Durante l'esecuzione dell'UT non ha compilato il nuovo modulo, quindi non ha generato l'ObjectFactory.java, quindi ho ricevuto l'errore anche se quando ho compilato il modulo sono stato in grado di vedere l'ObjectFactory.java

ha aggiunto la seguente dipendenza:

<dependency>
    <groupId>com.myCompany</groupId>
    <artifactId>my-module-name</artifactId>
    <version>${project.version}</version>
    <scope>test</scope>
</dependency>

La mia soluzione era:

JAXBContext context = JAXBContext.newInstance ( nuova classe [] {" my.package.name "} );

o

JAXBContext context = JAXBContext.newInstance ( nuova classe [] {class.getName ()} );

OPPURE

una soluzione completa:

public static <T> T deserializeFile(Class<T> _class, String _xml) {

        try {

            JAXBContext context = JAXBContext.newInstance(new Class[]{_class});
            Unmarshaller um = context.createUnmarshaller();

            File file = new File(_xml);
            Object obj = um.unmarshal(file);

            return _class.cast(obj);

        } catch (JAXBException exc) {
            return null;
        }
    }

Funziona al 100%

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top