Jakarta EE 10 - final

Von Markus Pauer, GEDOPLAN IT Training.

Was lange währt, wird endlich gut. So oder so ähnlich kann man von der Finalisierung des Releases 10 des Standards für Enterprise Java Anwendungen sprechen. Die Veröffentlichung der aktuellen Version hatte sich in diesem Jahr immer wieder verzögert. Zunächst war das Release bereits für das erste Quartal 2022 angekündigt worden aber dann immer wieder verschoben. Dann endlich, als man schon gar nicht mehr damit gerechnet hatte, dass es wirklich passieren wird, wurde das neueste Release dann doch endlich freigegeben.

Der Weg zum neuen Release

Fünf Jahre sind in der IT-Entwicklung eine lange Zeit. Frameworks kommen und geraten manchmal ebenso schnell wieder in Vergessenheit. Nach nunmehr 5 Jahren ist es also das erste Release, das wirkliche Neuerungen mit sich bringt. Aber was bringt denn nun die neue Version an Verbesserungen mit sich? Die Erwartungen sind da natürlich entsprechend groß. Wir schauen uns in diesem Artikel einmal die aus unserer Sicht wichtigsten Neuerungen an.

Die in den letzten Jahren sicherlich gravierendste Veränderung in der Softwareentwicklung ist der Schritt Richtung Cloud Native und DevOps. Dementsprechend muss natürlich eine moderne Enterprise Platform genau diese Anforderungen unterstützen.

Definition der Cloud Native Computing Foundation (CNCF)

Cloud native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. Containers, service meshes, microservices, immutable infrastructure, and declarative APIs exemplify this approach.

…​

                                                                                                                               — CNCF

Diese Anpassung der Spezifizierung wurde mit der Version 10 nun eingeleitet und wird sicherlich in den nächsten Versionen weiter ausgebaut werden.

Namensänderungen

Zu den wahrscheinlich gravierendsten Änderungen zählt sicherlich die Anpassung des Basis-Packetraums. Der Wechsel von javax auf jakarta wurde zwar schon mit der ersten Jakarta Version 8 im Jahr 2017 begonnen, allerdings in der praktischen Umsetzung häufig immer weiter nach hinten geschoben.

Das lag unter anderem daran, dass diejenigen, die Java EE 8 produktiv einsetzen, nicht die Notwendigkeit gesehen haben eine Migration auf die neue Version vorzunehmen. Das spiegelt sich auch ein wenig in der teilweise zaghaften Umsetzung der neuen Version durch die Anwendungsserver wieder.

Der Wildfly konnte mit den Versionen jenseits von Jakarta EE 9 nur im Preview-Modus eingesetzt werden. Dieser Modus hat natürlich immer etwas von Beta-Version und es war daher verständlich, dass die IT-Verantwortlichen dessen Einsatz nicht unbedingt in Betracht gezogen haben.

Die Änderung der Paketnamen wurde bereits in Jakarta EE 8 initiiert. Dort wurde als Duplikat der Namensraum jakarta statt javaxeingeführt. Dies war notwendig, da Oracle die Namensrechte sowohl an der Bezeichnung Java EE, als auch die Hoheit über javax behalten wollte.

Maven Coordinates

javax:javaee-api:8.0.0 à jakarta.platform:jakarta.jakartaee-api:10.0.0

Package Names

javax.{annotation|ejb|enterprise|faces|inject|persistence|ws} à jakarta.…

Deployment Descriptor Schemas

http://xmlns.jcp.org à https://jakarta.ee

Property Names (persistence.xml, web.xml, …)

javax.… à jakarta.…

Faces Taglib Names

xmlns:…="http://xmlns.jcp.org/jsf/{facelets|core|html|passthrough|composite}" à xmlns:…="jakarta.faces.…"

Anforderungen an das JDK

Neben den oben genannten Änderungen war es notwendig sich an den neuen Release-Zyklus des JDK anzupassen. Die Jakarta Platform definiert als Voraussetzung mindestens Java 11. Allerdings wurden fast alle Anwendungsserver auch für Java 17 freigegeben, so dass man hier zumindest auf das aktuellste Long-Term-Release zurückgreifen kann.

Context and Dependency Injection (CDI)

Das Release 10 der Spezifikation rückt nun, konsequenter Weise, CDI als führendes Komponentenmodell in den Fokus. Dazu wurden die fehlenden oder in der Vergangenheit oft stiefmütterlich behandelten Aspekte der CDI Integration nun konsequenter definiert.
Mit CDI 2.0 wurde die Spezifikation bereits in die Bereiche Core, SE und EE unterteilt. Das sollte es ermöglichen auf die Besonderheiten der Ausführungsplattformen einzugehen. Im Laufe der Zeit wurden die Jakarta Runtime-Frameworks immer beliebter, daher wurde mit CDI 4.0 die Spezifikation dahingehend angepasst. CDI Core wurde nun aufgeteilt in CDI Full und CDI Lite.

CDI Full ist für den Einsatz auf den klassischen Anwendungsservern gedacht und enthält alle bekannten Techniken des Komponentenmodells. CDI Lite ist nun insbesondere auf die Besonderheiten der Runtime-Frameworks (Quarkus, Micronaut, …) ausgelegt. Passivierbare Scopes und Dekoratoren sind dort nicht enthalten.
Ebenfalls nicht in CDI Lite sind die Portable Extensions. Über die Portable Extensions ist es möglich CDI einfach um Funktionalitäten zu erweitern. Darüber können beispielsweise Beans, Interceptoren oder sogar neue Scopes bereitgestellt werden. Ein populärer Vertreter solcher Extensions ist das Projekt Deltaspike, welches über Module für unterschiedliche Bereiche (JPA, JSF, …) weitere Funktionalitäten anbietet.
Die Portable Extensions werden zur Startzeit der Anwendung initialisiert und von Runtime-Frameworks wie Quarkus nicht unterstützt. Eine Alternative hierzu wurde nun auch in CDI Lite geschaffen. Die Build Compatible Extensions bieten die Möglichkeit zur Compile-Zeit Erweiterungen bereitzustellen.
Die einzelnen Phasen bei der Ausführung …

    @Discovery

    @Enhancement

    @Registration

    @Synthesis

    @Validation

ähneln denen der Portable Extensions. Es ist davon auszugehen, dass es solche Build Compatible Extensions auch für die beliebten Deltaspike-Module geben wird um diese mit CDI Lite verwenden zu können.

Asynchrone Verarbeitung

Eine sehr häufig vermisste Möglichkeit in CDI war die Nebenläufigkeit von Methodenaufrufen. Diese war nur in der EJB-Spezifikation definiert und verwendete noch das etwas in die Jahre gekommene java.util.concurrent.Future.

Mit der Concurrency API 3.0 wurde nun die Annotation @jakarta.enterprise.concurrent.Asynchronous eingeführt, mit der es nun auch für CDI-Beans eine Möglichkeit zur asynchronen Ausführung von Methoden gibt. Als Rückgabewerte sind hier java.util.concurrent.CompletableFuture, java.util.concurrent.CompletionStage oder void möglich.

@Asynchronous
public CompletableFuture<String> getAnswerToQuestionAboutLifeUniverseAndEverything() {
    ...
    return Asynchronous.Result.complete("Zweiundvierzig");
}

Core Profile

Mit Java EE 6 wurde das Web Profile eingeführt. Es sollte den Application Server Herstellern die Möglichkeit geben mit einem reduzierten Funktionsumfang dennoch den Enterprise Standard zu unterstützen. Die Entwicklungen in Richtung Runtime-Frameworks und Microservices führten zur dazu, dass sich eine "eigenständige" Spezifikation das Microprofile entwickelte.
Jakarta EE 10 definiert nun bewusst ein sogenanntes Core Profile um dieser Entwicklung Rechnung zu tragen. In diesem Profil sind lediglich die für eine Realisierung eines Microservices notwendigen Spezifikationen enthalten.

Wenn man sich die aktuelle Version 5 der Microprofile-Spezifikation anschaut, erkennt man viele Gemeinsamkeiten mit dem Core Profile. Bis auf die Interzeptoren sind alle Teilspezifikationen auch dort enthalten. Sowohl Microprofile als auch Jakarta EE befinden sich unter dem Dach der Eclipse Foundation. Um zukünftig eine gemeinsame Linie der beiden Spezifikationen zu erreichen wurde eine Angleichung beider Profile vereinbart.

Jakarta RESTful Webservices

Eine Möglichkeit zur Nutzung von RESTfull Webservices in Java SE Anwendungen fehlte bisher. Diese ist aber gerade in einer Cloud-nativen Umgebung interessant, wenn eine Laufzeitumgebung nicht unbedingt benötigt wird. Das Bootstraping der Webservices in Java SE kann wie folgt durchgeführt werden.

Bootstraping Webservices in Java SE

SeBootstrap
    .start(RestApplication.class)
    .thenAccept(instance -> {
        System.out.printf("REST server running at %s%n", instance.configuration().baseUri());
    });

Thread.currentThread().join();

Um in RESTfull Webservices Mutlipart Form Data verwenden zu können, war es bisher nötig die Erweiterungen der jeweils eingesetzen Implementierung zu nutzen. Im Standard war dieser Content-Type bisher nicht definiert.

@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response postMultipart(List<EntityPart> parts) {
    ...
}

Wenn man den Zugriff auf die einzelnen Teile nicht über eine Collection erhalten möchte, besteht die Möglichkeit sich diese auch einzeln in die Methode geben zu lassen.

@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response postMultipart(@FormParam("content") String content, @FormParam("file") InputStream file) {
    ...
}


Die Verwendung von @Context wurde als deprecated markiert und fällt somit in einer der nächsten Versionen weg.

Verwendung der Klasse UriInfo über @Context

@POST
public Response createCustomer(Customer customer, @Context UriInfo uriInfo) {
    ...
}

Stattdessen lässt sich jetzt per @Inject diese innerhalb der CDI-Bean nutzen.

Alternative Verwendung von @Inject

@Path("customer")
public class CustomerResource {
    @Inject
    UriInfo uriInfo;
    ...
}


Das ist zwar nur eine kleine Veränderung, sie verhält sich aber konsequenter in Bezug auf die Verwendung von CDI als zentrales Komponenten-Framework.

Jakarta Persistence API

Die wahrscheinlich geringsten Änderungen hat wohl Jakarta Persistence erfahren.
Verwendung von UUID als Identity für die Entities
Zwar boten die Persistence Frameworks wie Hibernate auch außerhalb des Standards Unterstützung für den Einsatz von UUIDs an, eine einheitliche Spezifizierung fehlte aber bislang. Das wurde nun in der aktuellen Version nachgeholt, womit es nunmehr ebenso möglich ist neben dem Mapping eine Generierung der Id zu bekommen.

@Entity
public class City {
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID id;
    ...
}

Erweiterung der Query-Language

Sowohl JPQL als auch die Criteria-API wurden durch unterschiedliche Funktionen ergänzt. So lassen sich bspw. Teile des Datums heraus extrahieren. Auch die Verwendung von LocalDate und LocalTimeinnerhalb einer Abfrage lassen sich durch die neuen Datumsfunktionen besser nutzen. Darüber hinaus wurden noch einige mathematische Funktionen mit in den Standard aufgenommen.

Jakarta Faces

Auch wenn JSF aka Jakarta Faces mittlerweile immer stärker durch die Javascript-Frameworks verdrengt wird, ist es für Geschäftsanwendungen immer noch eine gute Wahl. Daher verwundert es auch nicht, dass hier statt starker Kosmetik erst einmal alte Zöpfe abgeschnitten werden. Der Support für die Java Server Pages (JSP) und die ManagedBeans wurde nun entfernt. An die Stelle der ManagedBeans treten nun konsequenter Weise CDI-Beans.

Nutzung von CDI

Die folgenden Objekte können nun in CDI-Beans injiziert werden:

•    jakarta.faces.application.ResourceHandler
•    jakarta.faces.context.ExternalContext
•    jakarta.faces.context.FacesContext
•    jakarta.faces.context.Flash
•    jakarta.servlet.http.HttpSession

Damit stehen diese Beans auch endlich für Injektionen in allen Faces-Klassen zur Verfügung. Somit ist es möglich statt FacesContext.getCurrentInstance() diesen Context einfach mit @Inject FacesContext zu injizieren.

Darüber hinaus lassen sich jetzt auch andere CDI-Beans in Faces-Klassen verwenden. Das folgende Codebeispiel zeigt die Verwendung in einem Faces-Converter:

@Dependent
@FacesConverter(forClass = Currency.class, managed = true)
public class CurrencyConverter implements Converter<Currency> {
  @Inject
  private CurrencyRepository currencyRepository;
  ...
}

API zum Aufbau von Views

Die Erstellung von Seiten in reinem Java wird sehr häufig in Frameworks (Vaadin, Apache Wicket, …) für die Erstellung von Webanwendungen genutzt. In Jakarta Faces bestand bisher noch nicht die Möglichkeit Seiten auch ohne Kenntnis von bspw. Facelets zu erstellen. Über die Facelet-Klasse ist es nunmehr auch in JSF möglich Seiten über das API zu generieren.

Einfache Seite mit einem Eingabefeld

@View("/simple.xhtml")
@ApplicationScoped
public class SimpleView extends Facelet {
  @Override
  public void apply(FacesContext facesContext, UIComponent parent) throws IOException {
    if (!facesContext.getAttributes().containsKey(StateManager.IS_BUILDING_INITIAL_STATE)) {
      return;
    }
    UIOutput output = new UIOutput();
    output.setValue("<!DOCTYPE html>\n<html>");
    parent.getChildren().add(output);

    HtmlBody body = (HtmlBody) facesContext.getApplication().createComponent(HtmlBody.COMPONENT_TYPE);

    HtmlInputText input = (HtmlInputText) facesContext.getApplication().createComponent(HtmlInputText.COMPONENT_TYPE);
    body.getChildren().add(input);

    parent.getChildren().add(body);

    output = new UIOutput();
    output.setValue("</html>");
    parent.getChildren().add(output);
  }
}

Aktueller Stand

Und nun? Wie kann ich denn mit der neuen Version starten? Kann ich schon eine Migration auf Jakarta EE 10 planen?

Wildfly und Payara haben bereits die finalen Versionen ihrer Application Server fertiggestellt (Stand 01.12.2022). Man kann davon ausgehen, dass auch Glassfish (aktuell Milestone 9) in den nächsten Tagen den finalen Stand erreicht.

Die gezeigten Beispiele haben wir mit dem Wildfly 27 Final erstellt und ausprobiert. Die Quellen dazu sind über github.com/GEDOPLAN/jakarta-ee-demo verfügbar.
Auch Quarkus hat mit der Entwicklung der nächsten Version begonnen. In der Version 3.0 wird dann die Umstellung auf den jakarta-Namesraum vollzogen. Die neuen Funktionen lassen sich aktuell auch schon ausprobieren.

Quarkus Version 3.0 ausprobieren

$ quarkus create app --stream=3.0

Fazit

Als ich mir zum ersten Mal die Neuerungen der Version 10 angeschaut habe, war ich etwas enttäuscht. Ich hatte mir, wie wahrscheinlich jeder mehr an Neuerungen erwartet. Nachdem ich mich dann doch etwas intensiver mit Jakarta EE 10 beschäftigt hatte, sehe ich diese Neuerungen etwas optimistischer.
Der Fokus auf die Jakarta Context and Dependency Injection als zentrales Komponenten-Framework stellt sicherlich den größten Schritt der Spezifikation dar. Aber auch die vielen "kleinen" Dinge, die sich in Bezug auf die Cloud-Readiness getan haben, sind wichtig für die Zukunft von Jakarta EE.
Und wie geht es weiter?

Es gibt sicherlich auch Verlierer, die es nicht mehr in die neue Version geschafft haben. Beispielsweise die Anbindung nicht relationaler Datenbanken über Jakarta NoSQL. Neben der Definition einer einheitlichen Konfiguration mit Jakara Config ist es auch das aktionsgetriebene Jakarta MVC, welches sicherlich den Weg in das nächste Release finden wird.

Links

Stand: 01.12.2022
•    Jakarta EE Spezifikationen, jakarta.ee/specifications
•    Demo-Sourcen, github.com/GEDOPLAN/jakarta-ee-demo
•    Wildfly 27 Final, github.com/wildfly/wildfly/releases/download/27.0.0.Final/wildfly-27.0.0.Final.zip
•    Glassfish 7 Milestone 9, download.eclipse.org/ee4j/glassfish/glassfish-7.0.0-M9.zip
•    Payara Server 6.2022.1 Community, www.payara.fish/downloads/payara-platform-community-edition
•    Quarkus News zu Quarkus 3, quarkus.io/blog/road-to-quarkus-3


Link zur Eclipse Foundation

Aktuelle Jakarta EE Schulungen zum Thema

Jakarta EE 10 Intensivkurs
Grundlagen der Entwicklung von Enterprise-Anwendungen mit Jakarta EE 10

Microservices mit Quarkus - kompakt
Jakarta EE mit MicroProfile kombinieren, um Microservices und verteilte Systeme für Quarkus zu entwickeln

Workshop: Migration von JEE-Anwendungen zu Quarkus-(Micro-)Services
Umbau bestehender Java-EE/Jakarta-EE-Anwendungen in Microservices auf Basis von Quarkus

Expertenkreis Java - kostenlose IT-Vortragsreihe

Im Expertenkreis Java dreht sich alles um das Thema Java: Architekturen, Entwicklungstools, Erfahrungsberichte, Standards und einiges mehr. Die Experten treffen sich regelmäßig.

-> Expertenkreis Java

Unser Jakarta EE Blog

Hier finden Sie alle Neuigkeiten, Ideen und Kommentare rund um Jakarta EE.

-> Jakarta EE BLOG