Mit Java 11 wurde es ernst: Die mit Java 9 als deprecated markierten Module sind in Java 11 mit JEP320 nun entfallen. Dazu gehören unter anderem auch die beliebten Module javax.xml.bind
(JAXB) und javax.xml.ws
(JAX-WS). Versucht man nun also, unter einem aktuellen Java 11 die Module JAXB oder JAX-WS zu verwenden, führt dies erst einmal zu einem Fehler. Auch im Hinblick auf die Build-Zeit ergibt sich in Java 11 ein Problem: Die bisher mit dem JDK verfügbaren Tools xjc, schemagen, wsgen
und wsimport
sind entfallen.
Zwar sind im JEP320 entsprechende Maven-Artefakte genannt, welche für die entfallenen Module eine Referenzimplementierung bereithalten, jedoch werden diese insbesondere von älteren Frameworks nicht als Dependency referenziert. Im Fall von Spring Boot werden die notwendigen Bibliotheken mit der aktuellen Version 2.1 mit Java 11-Support eingebunden.
Reicht das aus? Es ist an der Zeit, sich die verfügbaren Dependencies mal etwas genauer anzuschauen:
JAXB
Im Fall von JAXB wird im JEP320 als Maven-Artefaktcom.sun.xml.bind:jaxb-ri
als Referenz-Implementierung genannt. Leider handelt es sich dabei um ein ZIP-Archiv. Schaut man sich das Archiv genauer an, enthält es entsprechende JARs sowie weitere Dokumentation und auch die ebenfalls im JDK entfallenen Tools xjc
und schemagen
. Als direkte Dependency im nächsten Java 11-Projekt ist das aber nicht wirklich brauchbar.Helfen kann die oben genannte Dokumentation zur Referenz-Implementierung: Hier werden
javax.xml.bind:jaxb-api
als API und org.glassfish.jaxb:jaxb-runtime
als Implementierung gelistet.Bei einer Suche im Maven-Central nach Artefakten der Group-Id
com.sun.xml.bind
wird man mit com.sun.xml.bind:jaxb-impl
und com.sun.xml.bind:jaxb-core
ebenso fündig. Das Dependency-Paar wird zum Beispiel auch vom JAXB-Support in Apache Camel (Version 2.22.1) referenziert. Es handelt sich dabei jedoch um eine alte JAXB Runtime-Implementierung, was auch die Dokumentation der entsprechenden POMs besagt.Ein Blick in die Starter-Konfiguration des neuen Spring Boot 2.1, zum Beispiel den
spring-boot-starter-data-jpa
, zeigt das dort zwar die JAXB API javax.xml.bind:jaxb-api
referenziert wird, nicht jedoch eine Implementierung. Ein Test bestätigt, dass die entsprechende Runtime-Dependency zusätzlich eingefügt werden muss. Hier besteht durchaus die Auswahl zwischen der Glassfish-Dependency und dem Pärchen der alten Implementierung – beides funktioniert bei einem Test unter Java 11. Das ist vor dem Hintergrund, dass zum Beispiel Apache Camel in der oben genannten Erweiterung die alte Implementierung nutzt, ganz sinnvoll. Andernfalls würden sich zwei unterschiedliche Implementierungen gleichzeitig im Projekt finden.JAX-WS (Hier SOAP)
Für JAX-WS, bzw. in diesem Beispiel konkret SOAP im Kontext einer Spring Boot-Anwendung, wird im JEP320 als Maven-Artefaktcom.sun.xml.ws:jaxws-ri
genannt. Auch hier handelt es sich wieder um ein ZIP-Archiv aus JAR-Files sowie Dokumentation und den ebenfalls entfallenen Tools wsgen
und wsimport
.Als JAR ist im Maven-Central mit
com.sun.xml.ws:jaxws-rt
direkt ein Artefakt für die Verwendung in Maven verfügbar um ein Spring Boot-Projekt zu erweitern. Diese besitzt jedoch eine ganze Reihe transitiver Abhängigkeiten. Darunter auch die JAXB-Implementierung der Glassfish-Group-Id von oben. Werden im Rahmen eines einfachen Spring Boot-Projektes lediglich SOAP-Services verwendet, so reicht in einem ersten Test für Spring Boot 2.0 neben einer der JAXB-Implementierungen des vorherigen Abschnittes, die Dependency auf die JAAS-Implementierung aus. Diese ist unter com.sun.xml.messaging.saaj:saaj-impl
zu finden.Im Fall von Spring Boot 2.1 wurde die genannte SAAJ-Implementierung direkt dem Starter
spring-boot-starter-web-services
zusammen mit der JAXWS-API hinzugefügt, so dass die zusätzliche Dependency im Projekt transitiv vorhanden ist und damit als direkte Dependency entfällt. Eine JAXB-Implementierung wird jedoch weiterhin benötigt.Generierung von JAXB-annotierten Klassen
Sich zur Entwicklungszeit aus z.B. einer gegebenen XSD die JAXB annotierten Klassen generieren zu lassen, ist nach dem Entfallen der Tools und damit insbesondere des Tools xjc
problematisch.
Im eingangs genannten ZIP-Archiv der JAXB-Implementierung ist das xjc-Tool zwar enthalten, das setzt in der Entwicklung dann aber natürlich voraus, dass dieses entsprechend installiert bzw. vorgehalten werden muss. Es wäre zwar möglich, die Dependency z.B. mit Maven zu beziehen, zu entpacken und dann zu verwenden – elegant und wartbar ist das aber erstmal auch nicht.
Das Maven-Plugin jaxb2-maven-plugin
funktioniert aktuell leider noch nicht mit Java 11. Dazu gibt es noch einen Issue auf Github. Ähnlich sieht es mit dem Maven-Plugin von Apache CXF (org.apache.cxf:cxf-codegen-plugin
) aus: Das soll mit Version 3.3.0 unter Java 11 zwar funktionieren, leider ist die Version jedoch noch nicht veröffentlicht.
In der Zwischenzeit gibt es aber dennoch keinen Grund zum Verzweifeln: Die alte Implementierung des xjc-Tools steht in der Maven-Central unter com.sun.xml.bind:jaxb-xjc
auch als JAR-Artefakt zur Verfügung. Erste Tests zeigen jedoch, dass das Maven-Artefakt offenbar nicht ohne weitere Dependencies funktioniert – es benötigt unter anderem eine JAXB-Implementierung. Davon gibt es, wie wir gesehen haben, eine entsprechende Auswahl. Alternativ steht aber im Kontext des Glassfish auch hier die aktuelle Implementierung zur Verfügung. Mit org.glassfish.jaxb:jaxb-xjc
gibt es eine JAR-Version, welche alle benötigten Dependencies transitiv referenziert. Damit lässt sich das xjc-Tooling recht einfach weiter in Maven verwenden.
Die dabei aufkeimende Euphorie wird beim ersten Anlauf jedoch gleich wieder etwas gedämpft: Versucht man nämlich, das Tool via exec-maven-plugin
zu starten, zeigt sich ein kleiner Schönheitsfehler: Das XJC-Tool scheint seine Arbeit auch im Erfolgsfall mit einem System.exit(0)
zu beenden. Damit ist dann auch der Maven-Prozess erstmal beendet und der Build läuft nicht weiter. Als Ausführung im Maven-Build zur Generierung von Sourcen vor dem eigentlichen Compile ist das eher unschön.
Leider unterstützt das Plugin für eine Java-Ausführung keine Fork-Prozesse, um die Ausführung in einer gesonderten JVM-Instanz zu starten. Dies kann zwar manuell über das Goal exec
erfolgen, indem der Java-Prozess via java.exe
direkt gestartet wird, leider fällt dann ein zweites Problem auf: Das Ausgabe-Verzeichnis der zu erzeugenden Java-Sourcen wird bei der Ausführung nicht automatisch angelegt. Fehlt es, bricht das Tool ab. Das ist im Maven-Kontext eher unschön, da nach einem mvn clean
das Ausgabe-Verzeichnis gelöscht wird. Die Sourcen davon auszunehmen würde den Convention-Gedanken des Maven-Ansatzes verletzen.
Da es für das Anlegen des Ausgabe-Verzeichnisses ohnehin auf ein maven-antrun-plugin
hinausläuft, lässt sich der Java-Aufruf auch direkt damit erledigen:
Da die Dependency auf das Tool nur zur Build-Zeit und nur für diese eine Plugin-Ausführung benötigt wird, wurde die Dependency direkt für das Plugin definiert. Damit kann sich die Tool-Ausführung dann auch auf den Plugin-Classpath beschränken.
Im Gegensatz zum exec-maven-plugin
gibt es bei dem maven-antrun-plugin
keine Option, den Pfad der erzeugten Source-Files (target/generated-sources/ws
in unserem Beispiel) zum Source-Path für den späteren Compile hinzuzufügen.
Dazu muss das build-helper-maven-plugin
verwendet werden:
Fazit
Für die mit Java 11 entfallenen Module gibt es teils mehrere Alternativen, welche direkt über die Maven-Central bezogen werden können. Spring Boot 2.1 hat die entsprechenden API-Dependencies mit aufgenommen, die eigentlichen Implementierungen müssen jedoch weiterhin als Abhängigkeit manuell hinzugenommen werden.
Andere Bibliotheken wie Apache Camel referenzieren in der aktuellen Version weiterhin die alten Implementierungs-Artefakte. Es bleibt also abzuwarten, wie viele Implementierungen sich mit der Zeit ergeben werden und wie viele dieser Implementierungen sich später als transitive Abhängigkeiten in einem realen Projekt gleichzeitig wiederfinden werden.
Update 03.2019
Zum leichteren Kopieren wurden die Bilder der Code-Listings ersetzt. Darüber hinaus ist das hier gezeigte Beispiel zum Aufruf des XJC via maven-antrun-plugin
auch auf GitHub verfügbar.
Das oben genannte jaxb2-maven-plugin
ist aktuell noch nicht für Java 11 verfügbar. Es gibt jedoch bereits eine alternative Implementierung für Java 8 bis 11, welche unter com.github.davidmoten:jax-maven-plugin
auch im Maven-Central verfügbar ist. Erste Tests mit dem oben genannten Beispiel waren erfolgreich.
Update 05.2019
Das Code-Beispiel zur Ausführung des XJC via maven-antrun-plugin
wurde um die aktuelle Maven-Dependency von Apache ANT ergänzt. Wie auch in den Kommentaren erwähnt, kann diese bei der Ausführung unter Java 11 notwendig werden.
UPDATE 04.2021 - Java hat sich weiterentwickelt
Java hat sich weiterentwickelt. Mit Java 17 steht das nächste LTS-Release nach Java 11 vor der Tür. Was das bedeutet und wie es mit Java weitergeht behandeln wir in unserem neuen Blogbeitrag.
zurück zur Blogübersicht