Über unsMediaKontaktImpressum
Dirk Weil 28. März 2023

Von J2EE zu Jakarta EE - zwei Jahrzehnte Enterprise Java

Enterprise Java wird in diesem Jahr 24 - zumindest, wenn man den Standard betrachtet. Dabei gab es verschiedene Lizenzeigentümer, viele Expert Groups, teilweise revolutionäre Entwicklungen, aber auch vermeintlichen Stillstand und Ungewissheit. Werfen wir mal einen Blick auf die bewegte Vergangenheit:

J2EE

Sun veröffentlichte Ende 1999 die Java 2 Platform, Enterprise Edition - kurz J2EE. Bemerkenswert waren daran einige Dinge.

So war die J2EE eine Umbrella Specification, die diverse Einzelstandards umfasste, die zwar unabhängig entwickelt, aber dennoch koordiniert unter einem Dach zusammengeführt wurden. Zudem gab es sowohl für die Bestandteile als auch für das Ganze direkt mehrere Implementierungen - und nicht nur von Sun. Wenngleich die Lizenz und auch die Produkte im Allgemeinen nicht so waren, sehen wir hier schon Ansätze wie in vielen Open-Source-Projekten.

Mit Enterprise JavaBeans war ein Standard enthalten, der die Welt stark polarisierte. Zum einen konnten hiermit kleine Geschäftslogikeinheiten deklarativ miteinander kombiniert und mit verschiedenen Scopes und Transaktionsmodi versehen werden - aus heutiger Sicht für 1999 geradezu revolutionär. Zum anderen platzte der Standard vor technischer Komplexität aus allen Nähten. Die Komponenten bestanden aus mehreren Klassen und Interfaces, die von bestimmten Basistypen abzuleiten waren und deren Methoden sehr technische Signaturen haben mussten. Für die o. a. Konfiguration und Verknüpfung waren recht komplexe XML-Deskriptoren beizusteuern.

Um bspw. zwei simple statuslose Services zu programmieren, die zur Demonstration nur Texte produzieren, wobei der eine den zweiten aufruft, sind sage-und-schreibe je 3 Java-Quellen nötig:

// WorldService.java
public interface WorldService extends EJBLocalObject {
  public String getWorld();
}

// WorldServiceHome.java
public interface WorldServiceHome extends EJBLocalHome {
  public WorldService create() throws RemoteException, CreateException;
}

// WorldServiceBean.java
public class WorldServiceBean implements SessionBean {

  public String getWorld() { return "world"; }

  public void ejbCreate() throws RemoteException, CreateException {}
  public void setSessionContext(SessionContext ctx) throws EJBException, RemoteException {}
  public void ejbRemove() throws EJBException, RemoteException {}
  public void ejbActivate() throws EJBException, RemoteException {}
  public void ejbPassivate() throws EJBException, RemoteException {}
}

// HelloService.java
public interface HelloService extends EJBLocalObject {
  public String getHello();
}

// HelloServiceHome.java
public interface HelloServiceHome extends EJBLocalHome {
  public HelloService create() throws RemoteException, CreateException;
}

// HelloServiceBean.java
public class HelloServiceBean implements SessionBean {

  private WorldService worldService;

  public String getHello() {
    return "Hello, " + this.worldService.getWorld();
  }

  public void ejbCreate() throws RemoteException, CreateException {
    try {
      InitialContext context = new InitialContext();
      WorldServiceHome worldServiceHome = (WorldServiceHome) context.lookup("java:comp/env/WorldService");
      this.worldService = worldServiceHome.create();
    } catch (Exception e) {
      throw new CreationException(e);
    }
  }

  public void ejbCreate() throws RemoteException, CreateException {}
  public void setSessionContext(SessionContext ctx) throws EJBException, RemoteException {}
  public void ejbRemove() throws EJBException, RemoteException {}
  public void ejbActivate() throws EJBException, RemoteException {}
  public void ejbPassivate() throws EJBException, RemoteException {}
}

 

Dazu kommt dann noch ein Deployment Descriptor:

<ejb-jar xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd" version="2.1">
  <enterprise-beans>
    <session>
      <ejb-name>WorldService</ejb-name>
      <local-home>de.gedoplan.showcase.service.WorldServiceHome</local-home>
      <local>de.gedoplan.showcase.service.WorldService</local>
      <ejb-class>de.gedoplan.showcase.service.WorldServiceBean</ejb-class>
      <session-type>Stateless</session-type>
      <transaction-type>Container</transaction-type>
    </session>
    <session>
      <ejb-name>HelloService</ejb-name>
      <local-home>de.gedoplan.showcase.service.HelloServiceHome</local-home>
      <local>de.gedoplan.showcase.service.HelloService</local>
      <ejb-class>de.gedoplan.showcase.service.HelloServiceBean</ejb-class>
      <session-type>Stateless</session-type>
      <transaction-type>Container</transaction-type>
      <ejb-local-ref>
        <ejb-ref-name>WorldService</ejb-ref-name>
        <ejb-ref-type>Session</ejb-ref-type>
        <local-home>de.gedoplan.showcase.service.WorldServiceHome</local-home>
        <local>de.gedoplan.showcase.service.WorldService</local>
      </ejb-local-ref>
    </session>
  </enterprise-beans>
</ejb-jar>

Der überwiegende Teil davon ist Boiler Plate Code - nur ca. 5% sind "Fachlogik"!

Java EE

Das konnte natürlich so nicht weiter gehen. Die Idee zur Vereinfachung kam vom zeitparallel entwickelten Framework Spring: Dependency Injection. Statt jede Komponente ihr eigenes Setup und die eventuell benötigten Verknüpfungen zu anderen Komponenten selbst erledigen zu lassen, übernimmt dies ein Container in der Server-Runtime. Die technischen Aspekte der Komponenten verschwinden damit aus dem Fokus der Entwicklung. Keine (verpflichtenden) Lifecycle-Methoden mehr, keine Home-Interfaces. Wenn Sie nicht wissen, was das war und wofür man das benötigte - vergessen Sie’s einfach!

In Java EE 5 übernahm man diese Idee aus Spring und ging sogar noch einen Schritt weiter: Statt die Konfiguration weiterhin in XML vorzunehmen, konnten nun Annotationen genutzt werden. Die oben angerissenen Beispiel-Services sehen nun so aus:

// WorldService.java
@Stateless
public class WorldService {
  public String getWorld() {
    return "world";
  }
}

// HelloService.java
@Stateless
public class HelloService {
  @EJB
  WorldService worldService;

  public String getHello() {
    return "Hello, " + this.worldService.getWorld();
  }
}

Java EE 5 wurde in 2006 veröffentlicht. Dass dabei der Name von J2EE in Java EE geändert wurde war eine Folge der entsprechenden Umbenennung der Sprache Java (und die 2 im Namen hatte vermutlich niemand wirklich verstanden). Zudem wurde die Nummerierung von 1.x in x geändert (schließlich war .NET schon bei 6).

Java EE reloaded

Eigentlich hätte es damit nun gut sein können, aber die oft gehörte Meinung über EJB (und damit ganz häufig über Java EE insgesamt) war "Das ist alles zu schwergewichtig. In Spring geht das mit viel weniger Code".

Häh? Ersetzen wir im Code oben @Stateless durch @Service und @EJB durch @Autowired, haben wir Spring-Code. Gleiche Länge, gleiche Komplexität.

Es ist vermutlich wie mit anderen Dingen des menschlichen Lebens: Ist der Ruf einmal versaut, bleibt der Makel auf ewige Zeit haften. EJB ist durch die frühen Versionen verbrannt - keine Rettung mehr nöglich!

Was macht man dann? Richtig: Man gibt dem Kind einen anderen Namen. So wurde in Java EE 6 der neue Standard CDI eingebaut. Damit könnten die beiden Services nun diesen Code haben:

// WorldService.java
@ApplicationScoped
public class WorldService {
  public String getWorld() {
    return "world";
  }
}

// HelloService.java
@ApplicationScoped
public class HelloService {
  @Inject
  WorldService worldService;

  public String getHello() {
    return "Hello, " + this.worldService.getWorld();
  }
}

 

Das fanden die Entwickelnden nun endlich leichtgewichtig. Interessant, wie viel "schwerer" @Stateless und @EJB im Gegensatz zu @ApplicationScoped und @Inject (oder @Service und @Autowired) zu sein scheinen …​

Ich würde CDI allerdings Unrecht tun, wenn ich es nur auf diese Annotationsänderungen reduzieren würde. In den vier Jahren zwischen Java EE 5 und 6 sind vielmehr viele weitere Ideen in CDI eingeflossen, die EJB vorher nicht besaß.

Problematisch ist an der Koexistenz von EJB und CDI im Gesamtstandard, dass nun zwei Bausteine für denselben Zweck existieren. Es sollte jedoch für jede Aufgabe stets nur ein Angebot vorhanden sein. Um dies abzumildern, muss EJB alle CDI-Eigenschaften kompatibel implementieren. Komponenten beider Frameworks können somit in einer Anwendung genutzt und problemlos miteinander kombiniert werden. In der Zukunft wird EJB aber eine zunehmend kleinere Rolle spielen.

Jakarta EE

Die Plattform wurde in der Folge für die Releases 7 und 8 weiterentwickelt, aber in den Jahren 2016 und 2017 entstand eine irritierende Pause, als insbesondere die von Oracle geleiteten Teilspezifikationen nicht voranzukommen schienen. Der sehr hierarchische Standardisierungsprozess JCP (Java Community Process) verlangt die Mitwirkung des (einen) Specification Leads, so dass dieser durch Inaktivität die Weiterentwicklung faktisch stoppen kann.

Die Java-Community war sich nicht sicher, was da passiert - oder eben auch nicht passiert. Würde Java EE eventuell sogar aufgegeben, "eingestampft"? Die Erlösung folgte 2017, als einerseits Java EE 8 veröffentlicht wurde und andererseits Oracle verkündete, dass die Plattform als Open-Source-Projekt an die Eclipse Foundation gehen würde.

Dieser guten Nachricht folgte leider zweimal "Aber": Oracle behielt zum einen die Rechte am Namen Java EE. Im Open-Source-Projekt entschied man sich mit Hilfe eines Community Votings für den neuen Namen Jakarta EE. Du liebe Güte: Der dritte Name innerhalb überschaubarer Zeit. Das verwirrt schon mal den einen oder anderen, der Fachkräfte für Projekte sucht. Immerhin kann man immer noch die Abkürzung JEE verwenden.

Dramatischer noch ist die Tatsache, dass sich Oracle und Eclipse Foundation nicht auf eine weitere Nutzung des im Enterprise Java häufig auftretenden Paketnamen-Präfixes javax einigen konnten. Die bisherigen Klassen und Interfaces dürfen zwar in Jakarta EE 8 verwendet werden, aber jede Weiterentwicklung in diesem Namensraum ist untersagt. Die Folge: In einem Big Bang werden sämtliche JEE-Pakete von javax.XYZ in jakarta.XYZ umbenannt. Was nach einem einfachen Textersatz aussieht, bedeutet in der Praxis, dass ältere Anwendungen und Bibliotheken auf neueren Servern oder in neueren Runtime-Frameworks nicht mehr ohne Weiteres lauffähig sein werden.

In den Umbenennungs-Reigen gehören noch die XML-Namespaces (aus http://xmlns.jcp.org wird https://jakarta.ee) und Konfigurations-Properties (z. B. javax.persistence.jdbc.driverjavax.persistence.jdbc.driver).

Da der Paket-Präfix nicht nur im JEE-Bereich genutzt wird, kann man nicht etwa in seinen Programmquellen javax. durch jakarta. ersetzen. Glücklicherweise unterstützen die gängigen IDEs recht gut bei der Migration. Ob Oracle bewusst war, welchen immensen Aufwand das Festhalten an Java EE und javax weltweit erzeugen wird?

Durch die beschriebenen Legal Issues ergibt sich die folgende Release-Situation:

  • Jakarta EE 8 wurde 2019 code-gleich zu Java EE 8 veröffentlicht. Geändert haben sich ausschließlich die Spezifikationsdokumente, in denen die dem Urheberrecht von Oracle unterliegenden Namen durch neue ersetzt wurden.
  • Jakarta EE 9 wurde 2020 mit unveränderter Funktionalität released, wobei die zuvor beschriebenen Umbenennungen von Paketen, Namespaces und Properties durchgeführt wurde.
  • Jakarta EE 10 kam 2022 heraus. Dies ist das erste Release seit Java EE 8, das Änderungen der Funktionalität einführt. Die Details sind noch nicht finalisiert, aber relativ klar ist, dass CDI um nahezu alle Features ergänzt wird, die bislang nur in EJB zu finden sind. Zudem wird vermutlich Config aus MicroProfile nach Jakarta EE übernommen. Gute Chancen hat zudem die Integration von NoSQL.

Der Übergang zu Jakarta EE hat organisatorisch einige Vorteile erzeugt. Der Standardisierungsprozess ist öffentlich und community-driven mit den üblichen Werkzeugen wie Git, Pull Requests, AsciiDoc etc. Wer mag und sich berufen fühlt, ist eingeladen mitzumachen - ohne große Hürden. Die TCKs (Technology Compatibility Kits aka Tests), die früher Closed Source waren, sind nun frei verfügbar, sodass viel leichter als zuvor funktionierende Implementierungen der Standards entstehen können. Es gibt auch nicht mehr die eine Reference Implementation, sondern Compatible Products.

In der nächsten Ausgabe werde ich einen Blick auf eben diese Produkte werfen, auf klassische (und dennoch leichtgewichtige (!)) Application Server, aber auch auf JAR Deployments und Micro(profile) Runtimes.

Autor

Dirk Weil

Dirk Weil ist seit 1998 als Berater im Bereich Java tätig. Als Geschäftsführer der GEDOPLAN GmbH ist er für die Konzeption und Realisierung von Informationssystemen auf Basis von Java EE verantwortlich.
>> Weiterlesen
Das könnte Sie auch interessieren
Kommentare (0)

Neuen Kommentar schreiben