In diesem Jahr steht mit Java 17 das nächste LTS-Release nach Java 11 vor der Tür. Damit ist ein Ende der Unterstützung von älteren Java Versionen absehbar. Bereits ein Update auf Java 11 kann problematisch werden: Mit Java 11 sind die beliebten Module javax.xml.bind (JAXB) und javax.xml.ws (JAX-WS) durch JEP320 endgültig entfallen. Dieser Blogpost zeigt die großen Herausforderungen bei einem Update auf Java 11 als Voraussetzung für eine Java 17-Migration.
In einem vorherigen Blogpost hatten wir aktuelle Probleme mit dem Update auf Java 11 und JAX-B betrachtet: Die beliebten Module javax.xml.bind (JAXB) und javax.xml.ws (JAX-WS) waren mit JEP320 endgültig entfallen.
Seit diesem Blogpost sind etwas mehr als zwei Jahre vergangen. Zusätzlich steht in diesem Jahr mit Java 17 das nächste LTS-Release nach Java 11 vor der Tür. Zeit also, die bisherigen Lösungen für altbekannte Probleme zu hinterfragen und gewonnene Erkenntnisse für eine Java-Migration zu betrachten.
Im Folgenden wird dieser Blogpost den aktuellen Stand der Probleme mit der entfallenen JAX-B API und dem damit verbundenen Tooling betrachten. Darüber hinaus stellen sich für ein Java 11 Update aber auch weitere Fragen: Muss überhaupt ein Update erfolgen? Woher bekomme ich das JDK oder die Runtime und ist die noch frei? Sollte das Update auf Java 11 oder ein neueres Release durchgeführt werden? Und wie geht es danach weiter?
Ist ein Java-Update (jetzt schon) notwendig?
Der Premier Support für Java 8 läuft entsprechend der offiziellen Support Roadmap von Oracle im Jahr 2022 ab. Für Java 7 ist nur noch der Extended Support bis 2022 verfügbar. Neben den Support-Kosten werden auch die aktuellen Versionen eingesetzter Bibliotheken ihre Mindest-Anforderung an die Java Version immer weiter erhöhen.
Der Punkt, an dem Bugfixes oder auch kritische Sicherheitsupdates nicht mehr ohne Probleme in der aktuellen Landschaft vorgenommen werden können, wird daher unausweichlich kommen.
Welches Java?
Bleibt also die Frage: Woher beziehe ich das JDK oder die Runtime?
Die naheliegende Quelle ist Oracle. Dort können die Runtime sowie auch das SDK für Java 11 bezogen werden. Seit dem Wechsel des Lizenzmodells bei Oracle im Jahr 2019 steht Java 11 aber nur unter einer proprietären Lizenz zur Verfügung. Diese gestattet die kostenlose Nutzung nur für bestimmte Szenarien.
Daneben steht das OpenJDK - die Basis aller Java-Distributionen - unter der GPL v2 mit Classpath Exception (GPLv2 + CPE) zur Verfügung. Jedoch nur als Quellcode in Github. Für eine binäre Distribution muss dies zunächst gebaut werden. Hier stehen mit AdoptOpenJDK / Eclipse Adoptium und Amazon Coretto zwei Distributionen zur Verfügung.
Bisher stand hinter AdoptOpenJDK die London Java Community CIC. Mitte 2020 wurde angekündigt der Eclipse Foundation beizutreten. In diesem Zuge ändert sich der Name in Eclipse Adoptium. Hinter Coretto steht Amazon, welche die Distribution auch in ihren eigenen Rechenzentren einsetzen. Beide Distributionen sind ebenfalls unter der GPL v2 mit Classpath Exception verfügbar.
Beide Distributionen basieren auf dem gleichen Quellcode, wodurch sich in der Praxis nur geringe Unterschiede zeigen dürften. Da Coretto jedoch auch direkt bei Amazon eingesetzt wird, liegt die Vermutung nahe, dass Probleme bei einem Release schneller erkannt werden könnten.
LTS vs. non-LTS
Die Release-Strategie von Java bietet Long-Term-Support-Releases (LTS-Releases) und Non-LTS-Releases. Java 11 ist - wie auch Java 17 - ein LTS-Release. Als Release-Date wird September 2021 genannt.
Ein Non-LTS-Release wird offiziell nur bis zum Erscheinen des Folge-Releases supported. Ein LTS-Release dagegen länger. Wie lange genau kann unter anderem vom Distributor abhängen. Amazon Coretto 11 wird zum aktuellen Zeitpunkt bis September 2027 supported.
Ein LTS-Release stellt die Sammlung aller neuen Features der vorangegangenen Non-LTS-Releases dar. Dabei sind in einem Non-LTS-Release durchaus auch Previews als experimentelle Features enthalten. Dies bietet zwar zwischen zwei LTS-Releases eine gute Option frühzeitig die Features zu testen, jedoch auch die Gefahr einer Veränderung dieser Features bis zum finalen Erscheinen. Ein Beispiel für ein Feature, welches über mehrere Versionen hinweg entwickelt wurde, sind die neuen Switch-Case-Expressions:
Neben dem Support der jeweiligen Java Version sind jedoch auch die eingesetzten Frameworks zu beachten: Aufgrund des relativ kurzen Release-Zyklus für Non-LTS-Releases und deren experimentelle Features kann es durchaus sein, dass nicht jede Non-LTS-Version von jedem Framework unterstützt wird.
Bezüglich des Entfallens von Features muss noch gesagt werden, dass der API-Lifecycle mit Java 9 (JEP 227) geschärft wurde: Bisher wurden Methoden in der Runtime-Library zwar als deprecated markiert, aber faktisch nicht entfernt. Seit Java 9 gibt es bei der bisherigen @deprecated-Annotation das neue Feld forRemoval. Ist dieses gesetzt, wird ist die Methode oder Klasse für zukünftige Releases zum Entfernen vorgesehen. Bei einem Sprung von einem zum nächsten LTS-Release kann es somit vorkommen, dass die bisher problemlos verwendete Methode direkt entfallen ist. Die Deprecation-Information war dann in den dazwischenliegenden Non-LTS-Releases sichtbar.
Für produktionskritischen Code sollte daher besser auf LTS-Releases gesetzt werden. Es kann auf dem Weg zum nächsten LTS-Release jedoch nicht schaden, auch einen Compile gegen ein Non-LTS-Release durchzuführen.
Warten auf Java 17?
Wenn Java 17 bereits im September dieses Jahres erscheint, stellt sich die Frage: Jetzt ein Update auf Java 11 durchführen oder auf Java 17 warten?
Zunächst ist dies nur bedingt ein "oder": Ein Update auf Java 17 schließt die gleichen Probleme ein, die mit einem Update auf Java 11 entstehen. Die im Folgenden betrachteten Probleme bei einem Update auf Java 11 ergeben sich auch am Ende dieses Jahres für ein Update auf Java 17.
Dazu kommt jedoch noch der Support von Java 17 durch Bibliotheken, Frameworks und Laufzeitumgebungen. Für Frameworks und Bibliotheken ist mit einer schnellen Anpassung zu rechnen. So wird nur die 5.3er-Reihe von Spring einen Support für Java 17 erhalten. Werden noch klassische Application-Server eingesetzt, können sich diese jedoch für eine schnelle Adaption als ein Hindernis herausstellen.
Soweit nicht explizit Features aus den Versionen 12 bis 17 benötigt werden, bietet sich ein zweistufiges Update an: Zunächst werden die Probleme für ein Java 11-Update gelöst. Nachdem ein breiter Support für Java 17 im verwendeten Technologie-Stack gegeben und erprobt ist, wird der nächste Sprung angegangen. Im Fall von Amazon Coretto 11 bietet Amazon einen kostenlosen Support bis September 2027 an. Das bietet eine breiten zeitlichen Handlungsspielraum für den zweiten Update-Sprung.
Läuft die bestehende Landschaft bereits auf Java 11, sollte dem eingesetzten Technologie-Stack und Java selber für Version 17 dennoch etwas Zeit zum Reifen eingeräumt werden. Damit bietet sich ein Update der Java-Runtime erst gegen Mitte 2022 an.
Welche APIs / Tools sind betroffen?
JAXB
Wie auch im vorherigen Blogpost beschrieben, betrifft der JEP320 neben Corba insbesondere JAXB und das damit verbundene Tooling.
Zu den bisherigen zwei Optionen ist mit dem Wechsel von JEE zu Jakarta noch eine weitere Option für die API im Maven-Central hinzugekommen:
- API-1 com.sun.xml.bind:jaxb-core
- API-2 javax.xml.bind:jaxb-api
- API-3 jakarta.xml.bind:jakarta.xml.bind-api
Bei der ersten Option handelt es sich um das alte JAXB Kern-Modul. Die zweite Option ist mit dem Wechsel von JEE zu Jakarta EE in die dritte Option übergegangen. Neue Versionen wird es somit unter der zweiten Option nicht mehr geben. Somit bleibt für die API aktuell die dritte Option. Hierbei muss jedoch beachtet werden, dass sich mit dem Wechsel auch das Root-Package von javax.xml.* zu jakarta.xml.* geändert hat. Damit ist die neue Jakarta API (API-3) nicht als Drop-In-Replacement für die bisherige JEE API (API-2) verwendbar.
Für die Implementierung stehen zwei Optionen im Maven-Central zur Auswahl:
- Impl-1 com.sun.xml.bind:jaxb-impl
- Impl-2 org.glassfish.jaxb:jaxb-runtime
Erstere ist die alte Implementierung zur erstgenannten API-Option. Bei der zweiten Option handelt es sich um die Referenzimplementierung des ursprünglichen JSR 222 zu JAX-B 2.0. Für beide sind aktuelle Updates erschienen.
Welche Implementierung eingesetzt wird, kann unter anderem von den jeweiligen eingesetzten Bibliotheken abhängen:
In der Dokumentation zur Jakarta XML API (API-3) wird die initiale Implementierung (Impl-1) als Implementierung verwendet. Das aktuellen Hibernate 5.4.27 (org.hibernate:hibernate-core) beinhaltet eine Abhängigkeit auf die Glassfish-Implementierung (Impl-2) sowie die initiale API (API-1). Version 3.7.1 von Camel (org.apache.camel:camel-jaxb) verwendet ebenfalls die initiale API (API-1) jedoch mit der initialen Implementierung (Impl-1).
Werden mehrere Bibliotheken eingesetzt, müssen die jeweiligen Abhängigkeit genauer auf Kompatibilität geprüft werden. Andernfalls drohen zwei Implementierungen unterschiedlicher Versionen im Projekt zu landen.
Generierung von JAXB-Annotierten Klassen (XJC)
Auch die bisherigen Tools, um zur Entwicklungszeit aus einer XSD entsprechende Klassen zu erstellen, sind mit Java 11 vom fehlenden JAXB betroffen.
Das hierfür benötigte Tool XJC steht im Maven Central unter com.sun.xml.bind:jaxb-xjc zur Verfügung und muss lediglich mit einer entsprechenden JAXB-Runtime kombiniert werden. Alternativ steht mit org.glassfish.jaxb:jaxb-xjc eine Variante bereit, welche notwendige Runtime-Dependencies bereits referenziert. An diesen beiden Optionen hat sich seit dem letzten Blogpost zu diesem Thema nicht viel geändert.
Erfolgt die Generierung im Rahmen eines Maven-Builds, kommt hierfür häufig das Maven-Plugin jaxb2-maven-plugin zum Einsatz. Hier muss jedoch berücksichtigt werden, dass dieses aufgrund des entfallenen JAX-B erst ab Version 2.5.0 mit Java 11 kompatibel ist. Wenn die aktuelle Version nicht eingesetzt werden kann, besteht weiterhin die Option den im letzten Blogpost beschriebenen Workaround zu verwenden.
Java 9 Modulsystem
Mit Java 9 wurde im Rahmen des Projekt Jigsaw ein Modulsystem (JSR 376) eingeführt. Dabei ist das Modulsystem hinreichend kompatibel, dass es bei einem Update von einer älteren Java-Version wenig bis keine Probleme bereitet.
Parallele Kompatibilität?
Insbesondere in komplexen Systemen ist es möglich, dass eine Komponente in mehreren Installationen eingesetzt wird. Dies kann zum Beispiel auf den Application-Servern als EAR sein oder ein JAR wird als Bibliothek mit mehreren anderen Komponenten geteilt. Ein Update als Big-Bang ist bei komplexen Systemen in der Regel aus der Risiko-Perspektive nicht sinnvoll.
Für diese geteilten Komponenten kann es dann aber notwendig sein, dass sie weiterhin unter der alten Java-Version und neuen Java-Version lauffähig sein müssen. Das bedeutet, dass die gleiche Code-Basis einmal für die alte Java-Version sowie zusätzlich für die neue Java-Version mit den ebenfalls neu notwendigen Abhängigkeiten übersetzt werden muss. Bei einem Update ist diese zusätzliche Build-Komplexität ebenfalls mit zu beachten.
Christian Nockemann beschreibt in einem Blog-Beitrag, wie eine solche Migration bei komplexen Systemen erfolgen kann.
Das ganze Bild
Neben dem Modulsystem in Java 9 und den mit Java 11 entfallenen APIs stellen sich für ein Update je nach System weitere Herausforderungen.
Wird noch ein Application-Server eingesetzt, muss auch dieser für Java 11 kompatibel sein. Hier kann zum Beispiel ein altes, proprietäres RMI-Protokoll des JBoss-Application-Servers ein Update-Problem darstellen. Auch der Wechsel von JEE zu Jakarta EE kann mit dem Update des Application-Servers ein größeres Refactoring notwendig machen. Dabei hat sich der bisherige Namensraum der javax-Packages geändert: So wurde beispielweise aus javax.servlet nun jakarta.servlet.
JEE hatte eine lange Geschichte und war eine zukunftssichere Plattform. Wie sich die Zukunft mit Jakarta EE neben einem weit etablierten Spring Ökosystem entwickelt ist offen. Der API-Bruch durch den abweichenden Namensraum ist vor diesem Hintergrund keine vertrauensbildende Maßnahme. Neben dem Bewahren der Stabilität der bestehenden Plattform sind aber auch Neuerungen wie Cloud- und Container-Technologien als relevante Faktoren zu betrachten. Eine Gegenüberstellung von JEE / Jakarta EE mit Spring nimmt Tobias Voß in seinem Blogpost vor. Um zu sehen, wie eine Technologie-Landschaft ohne Jakarta EE und Application-Server aussehen kann, habe ich in einem anderen Blogpost für die gängigen Jakarta EE APIs mögliche Alternativen dargestellt.
zurück zur Blogübersicht