2017-10-03 1 views
0

Ich versuche JAXB 2.2.11 in einer OSgi-Umgebung (Liferay DXP) zu verwenden. Ich habe Probleme beim Erstellen eines JAXBContext. Basierend auf einigen anderen Quellen, die ich während meiner Recherchen wie this und this gefunden habe, habe ich festgestellt, dass ich in einem osgi Container den richtigen Classloader für JAXB bereitstellen muss, um den Kontext zu instanziieren. Also ich habe Code wie folgt:Wie JAXB 2.2.11 in OSgi (Liferay DXP)

ClassLoader cl package.with.jaxb.objects.ObjectFactory.class.getClassLoader(); JAXBContext jc = JAXBContext.newInstance("package.with.jaxb.objects ", cl);

Dieser Code verursacht eine Null-Zeiger-Ausnahme mit dem folgenden Stack-Trace:

Caused by: java.lang.NullPointerException 
    at javax.xml.bind.ContextFinder.handleClassCastException(ContextFinder.java:129) 
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:201) 
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:146) 
    at javax.xml.bind.ContextFinder.find(ContextFinder.java:371) 
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:446) 
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:409) 

an der Quelle der Suche nach ContextFinder kann ich sehen, dass context null sein müssen on line 129:

throw handleClassCastException(context.getClass(), JAXBContext.class);

ich dachte perhap s Das Problem war, dass mein Modul eine Abhängigkeit von jaxb-api 2.2.11 hat, aber die jaxb-impl Klassen zur Laufzeit von rt.jar bereitgestellt werden und wahrscheinlich neuer als 2.2.11 sind, da Liferay DXP auf JDK 1.8 läuft. Um dieses Problem zu umgehen, habe ich versucht, jaxb-impl.jar 2.2.11 als eine Abhängigkeit in meinem osgi-Modul, denken dann die jaxb-api & jaxb-impl-Versionen würden übereinstimmen. Danach wird versucht, einen JAXBContent mit dem gleichen Code wie oben ergibt folgende Fehler zu erstellen:

ClassCastException: attempting to cast jar:file:/C:/Program%20Files/Java/jdk1.8.0_144/jre/lib/rt.jar!/javax/xml/bind/JAXBContext.class to bundleresource://623.fwk616113009:13/javax/xml/bind/JAXBContext.class. Please make sure that you are specifying the proper ClassLoader. 

Durch das Aussehen dieser Nachricht, die JAXBContext, das ist von der Version von JAXBContext instanziiert zu werden, die über geladen wird rt.jar. Das ist sehr verwirrend für mich, da ich erwarten würde, dass die JAXBContext-Version, die vom Classloader meines Moduls geladen wurde, verwendet wird, da ich jaxb-impl.jar in mein Modul eingefügt habe und angegeben habe, dass der Klassenlader meines Moduls derjenige ist, der verwendet wird mein Aufruf an JAXBContext.newInstance. Kann jemand etwas Licht darauf werfen, wie ich jaxb 2.2.11 in einem osgi Container arbeiten kann?

* Bitte beachten Sie, dass ich die von meinem Modul verwendete Version von jaxb-api nicht aktualisieren kann, da der JAXB-Code tatsächlich in einem 3rd-Party-Jar ist, der jaxb 2.2.11 benötigt (ich habe gerade das 3rd-Party-Jar aus dem Programm genommen) die Gleichung für jetzt, indem Sie einen Test-JAXB-Code schreiben).

Antwort

0

Nach umfangreichen Recherchen fand ich die folgende Lösung. Da es so aussah, als ob der Paketklassen-Lader, wie von der akzeptierten Antwort in this post vorgeschlagen, korrekt sein musste, folgte ich dem Pfad der herauszufinden, warum ich eine NullPointerException bekam, als ich das versuchte.Nach sorgfältiger Prüfung für jaxb-api über den Quellcode schauen, um den Stack-Trace des Nullpointer zu folgen, konnte ich sehen, dass der jaxb-api Code Dinge wie

classLoader.loadClass("com.sun.xml.internal.bind.v2.ContextFactory")

tut, wo classLoader Class Loader mein Bundle ist (seit das ist, was ich übergeben habe) und ContextFactory ist eigentlich eine Klasse in Jaxb-Impl, die vom Bootstrap-Klassenlader geladen wird. Dies ist, wo das Problem liegt, weil mein Classloader nicht in der Lage ist, Klassen zu sehen, die vom Bootstrap-Klassenlader geladen werden. Das hat mich für eine Weile gestört, weil ich nicht daran gewöhnt bin, wie Klassenlader in osgi arbeiten. Ich dachte fälschlicherweise, dass die vom Bootstrap-Klassenladeprogramm geladenen Klassen sichtbar sein würden, da ich daran gewöhnt bin, dass die App-Klassen geladen werden, wo Delegationen stattfinden. In osgi Klasse Loader sind vollständig voneinander isoliert, Dinge sind nur sichtbar, wenn sie exportiert werden. Um das Problem zu umgehen, fand ich somehelpfulposts über ähnliche Probleme sprechen. Es stellt sich heraus, dass es ein Konzept boot delegation in osgi gibt, wo Sie eine Liste von Klassen/Paketen angeben können, die immer über den Bootstrap Classloader geladen werden. So ist das Endergebnis in zwei Schritten:

1) Schalten Sie den Klassenlader Thread zu Ihrem Bündel Class Loader, bevor Sie den Code Aufruf der JAXBContext zu bekommen:

ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader(); 

try {   
    // ObjectFactory here is in the same package as my classes to be marshalled 
    ClassLoader objectFactoryClassLoader = ObjectFactory.class.getClassLoader();    
    Thread.currentThread().setContextClassLoader(objectFactoryClassLoader); 
    // JAXB code goes here 
} finally { 
    Thread.currentThread().setContextClassLoader(currentClassLoader); 
} 

2) Pakete angeben, um das Boot geladen mit Delegationsmechanismus. Diese Liste muss die transitiven Abhängigkeiten der geladenen Klassen enthalten. In meinem Fall benutze ich Liferay, also ist die Liste Liferay spezifisch und geht in die Konfigurationsdatei portal-ext.properties. Zum Glück fand ich this post, wo jemand die meiste Arbeit für mich getan hatte:

module.framework.properties.org.osgi.framework.bootdelegation=\ 
    __redirected,\ 
    com.liferay.aspectj,\ 
    com.liferay.aspectj.*,\ 
    com.liferay.portal.servlet.delegate,\ 
    com.liferay.portal.servlet.delegate*,\ 
    com.sun.ccpp,\ 
    com.sun.ccpp.*,\ 
    com.sun.crypto.*,\ 
    com.sun.image.*,\ 
    com.sun.jmx.*,\ 
    com.sun.jna,\ 
    com.sun.jndi.*,\ 
    com.sun.mail.*,\ 
    com.sun.management.*,\ 
    com.sun.media.*,\ 
    com.sun.msv.*,\ 
    com.sun.org.*,\ 
    com.sun.syndication,\ 
    com.sun.tools.*,\ 
    com.sun.xml.*,\ 
    com.yourkit.*,\ 
    org.eclipse.persistence.internal.jaxb,\ 
    org.eclipse.persistence.internal.jaxb.*,\ 
    javax.xml.*,\ 
    sun.* 

Hilfreiche Links:

Why can't JAXB find my jaxb.index when running inside Apache Felix?

What is the difference between bootdelegation and DynamicImport-Package in osgi

https://web.liferay.com/web/user.26526/blog/-/blogs/liferay-dxp-and-weblogic-

https://dev.liferay.com/develop/tutorials/-/knowledge_base/7-0/bundle-classloading-flow

http://apache-felix.18485.x6.nabble.com/Classloading-for-JAXB-td4834670.html

0

Der beste Ort, um zu sehen, wie es gemacht werden sollte, ist Apache Karaf. Es wird kein JAXB-API-Paket installiert, sondern org.apache.servicemix.specs.jaxb-api-2.2-2.7.0.jar innerhalb des Verzeichnisses lib/endorsed.

Auf diese Weise verwenden Sie nicht die JAXB-API, die von bereitgestellt wird.

Für die Umsetzung - es ist am besten ServiceMix Version von JAXB-Bundles zu verwenden:

  • org.apache.servicemix.bundles: org.apache.servicemix.bundles.jaxb-impl: 2.2.11_1
  • org .apache.servicemix.bundles: org.apache.servicemix.bundles.jaxb-xjc: 2.2.11_1
+0

Vielen Dank für die Antwort Grzegorz. Leider habe ich, wie ich am Ende meines Posts erwähnt habe, nicht wirklich die Kontrolle darüber, wie der JAXB-Code geschrieben wurde, weil er in einer 3rd-Party-Bibliothek namens STIX (https://github.com/STIXProject/java -stix) und die STIX-Lib benutzt jaxb-api. – clav

Verwandte Themen