Wednesday, November 22, 2023

Transition from Java EE to Jakarta EE

What happened and what you need to know


Java EE is undoubtedly one of the most recognizable frameworks for server-side Java. It essentially kick-started the industry for using Java on the server, and it goes all the way back to the very beginnings of Java in 1996 with Kiva Enterprise Server (GlassFish) and the Tengah application server (the Oracle WebLogic Server ancestor). Note that here, the word Tengah refers to an administrative region in the center of the island of Java in Indonesia.

Java EE, or J2EE (Java 2 Enterprise Edition) as it was known before, is perhaps best known for its Java Servlet specification and for servers implementing that, such as Tomcat and Jetty. These are often called servlet containers. Although there are alternatives, many server applications and third-party frameworks are based on the Java Servlet specification. Besides this specification, Java EE in later years became known for its specifications for persistence (Java Persistence API [JPA], mostly via Hibernate), REST (JAX-RS), WebSocket, and a slew of smaller specifications such as for transactions (Java Transaction API [JTA], mostly used under the covers by JPA), for validation (Bean Validation), and for JSON (JSON-P and JSON-B).

In practice, some applications that might not seem to be classified as Java EE applications might use a variety of Java EE APIs.

Full implementations of Java EE, traditionally used in application servers, have enjoyed considerable success as well: JBoss/WildFly, GlassFish/Payara, and, more recently, Open Liberty (the modern successor of WebSphere) are all well known.

Then there’s a group of products that are neither application servers nor servlet containers, but do support a variety of Java EE APIs out of the box. These include Quarkus (Contexts and Dependency Injection [CDI], JAX-RS, JPA), Helidon (CDI, JAX-RS, JPA, JTA), KumuluzEE (CDI, JAX-RS, JPA, Servlet, JavaServer Faces [JSF], WebSocket, Bean Validation, JSON-P), and Piranha (CDI, JAX-RS, Java EE Security, Expression Language [EL]).

Finally there’s the Java EE offspring platform called MicroProfile, which directly depends on Java EE APIs such as CDI, JAX-RS, and JSON. All together this makes the Java EE APIs quite relevant for a large group of users.

What Has Been Going on with Java EE?


The last release of Java EE proper was Java EE 8 in August 2017. This was a scope-reduced release, although it did contain important key functionality, such as Java EE Security. Oracle decided later that year to transfer Java EE fully to an open source foundation. In coordination with Java EE partners Red Hat and IBM, it was decided to transfer Java EE along with the full reference implementation and the Technology Compatibility Kit (TCK) to the Eclipse Foundation.

Due to the enormous amount of work involved with this transfer, the process was split into three stages.

Stage 1: Transfer API and implementation code and release a verified build. The first stage involved creating a new top-level project at Eclipse called Eclipse Enterprise for Java (EE4J). The EE4J project and its associated GitHub organization, eclipse-ee4jz, are home to both the specification and implementation projects. EE4J should not be confused with the new brand name for Java EE, Jakarta EE, which was selected several months later by the community.

Before the actual transfer of all the existing source code from the Oracle repository at github.com/javaee could be done, all the code had to be cleared legally, which among other things meant potentially controversial portions had to be removed. Weighing in at many million lines of code, this was clearly no small task. Applying this legal clearing to all the historical code as well would have been simply undoable. Therefore, the first thing to note is that only the latest released versions of the code were transferred. For instance, JSF 2.3 was transferred as a snapshot from its master branch. JSF 2.2 and earlier versions remain at their original location and are not maintained or supported by the Eclipse Foundation.

After the transfer of the source code, all the code was built using Eclipse build servers, and the result was staged to a Maven repository. The API JAR files had their Maven group ID changed from javax.* to jakarta.*, indicating that they are the build artifacts produced by Eclipse. From these, a new build of GlassFish was produced, and against this build the original Java EE 8 TCK was run. After the build passed the TCK tests, proving that all the code was transferred successfully, it was released as GlassFish 5.1.

By the way, the initial release of the APIs under the Jakarta group ID are Java EE 8 certified, not Jakarta EE 8 certified. For example, jakarta.faces:jakarta.faces-api:2.3.1 is identical to javax.faces:javax.faces-api:2.3 and both are Java EE 8 certified, but the first is built from github.com/eclipse-ee4j and the latter is from github.com/javaee.

Stage 2: Transfer TCK code, set up a new specification process, define new terms, and release a rebranded build. The second stage involved transferring the TCK and building new binaries from it for Jakarta EE 8 certification. A new certification process was set up: the Jakarta EE Specification Process (JESP). Also, a new specification license was created: the Eclipse Foundation Technology Compatibility Kit license.

In this stage, new simplified and more-consistent names were created for all specifications. The new names all start with Jakarta and are followed by a simple description of the specification, avoiding inconsistent filler words such as architecture, api, and service.

The old and new terms are shown in Table 1.

Transition from Java EE to Jakarta EE
Table 1. Old Java EE 8 terms compared to new Jakarta EE 8 terms

The Javadoc for all APIs was updated in this stage to reflect the new terms, and the resulting API JAR files were relicensed and then tested against GlassFish 5.1 with the rebranded TCK that was built from the transferred TCK source code. All this was done following the new JESP specification process.

The resulting API JAR files were all released with near-empty placeholder specification documents. These combined constitute Jakarta EE 8.

For the individual JAR files, this means that this stage is the third release of technically the same API, the second release using the Maven Jakarta group ID, and the first release that’s Jakarta EE certified. Table 2 shows an example for JSF/Jakarta Faces.

Transition from Java EE to Jakarta EE
Table 2. Example showing the JAR files for JSF and Jakarta Faces

There are two extra things to notice here.

The first is that for Jakarta EE 8, there wasn’t a corresponding GlassFish release, although GlassFish 5.1 was certified for Jakarta EE 8 in addition to the existing Java EE 8 certification.

The second is that, as mentioned above, Jakarta EE 8 was released with essentially empty specification documents. The reason for this is the large amount of time it takes to legally clear and transfer those documents, and this simply was not finished in time for the Jakarta EE 8 release. For now, users (nonimplementors) of the technologies can read the evaluation version of the corresponding Java EE 8 documents.

Updating to the Jakarta EE versions of the APIs is the first small step users can take to prepare themselves for the upcoming larger changes. In a Maven project, doing that is mostly as simple as replacing this:

<dependency>
    <groupid>javax</groupid>
    <artifactid>javaee-api</artifactid>
    <version>8.0</version>
    <scope>provided</scope>
</dependency>

With this:

<dependency>
    <groupid>jakarta.platform</groupid>
    <artifactid>jakarta.jakartaee-api</artifactid>
    <version>8.0.0</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupid>jakarta.platform</groupid>
    <artifactid>jakarta.jakartaee-api</artifactid>
    <version>8.0.0</version>
    <scope>provided</scope>
</dependency>

Or, when individual dependencies are used, replacing this:

<dependency>
    <groupid>javax.faces</groupid>
    <artifactid>javax.faces-api</artifactid>
    <version>2.3</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupid>javax.faces</groupid>
    <artifactid>javax.faces-api</artifactid>
    <version>2.3</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupid>javax.faces</groupid>
    <artifactid>javax.faces-api</artifactid>
    <version>2.3</version>
    <scope>provided</scope>
</dependency>

With this:

<dependency>
    <groupid>jakarta.faces</groupid>
    <artifactid>jakarta.faces-api</artifactid>
    <version>2.3.2</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupid>jakarta.faces</groupid>
    <artifactid>jakarta.faces-api</artifactid>
    <version>2.3.2</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupid>jakarta.faces</groupid>
    <artifactid>jakarta.faces-api</artifactid>
    <version>2.3.2</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupid>jakarta.faces</groupid>
    <artifactid>jakarta.faces-api</artifactid>
    <version>2.3.2</version>
    <scope>provided</scope>
</dependency>

Because the APIs are essentially identical, there should be few issues after this update. Note, though, that Maven does not see the two dependencies as related with one being newer than the other.

For Maven, there are two totally different dependencies, and Maven will happily include both of them. This can happen, for instance, when a top-level dependency transitively brings in a Java EE dependency. Prior to the update to Jakarta, a transitively introduced javax.faces:javax.faces-api:2.2 would be overridden by, for example, a top-level javax.faces:javax.faces-api:2.3.

When that top-level dependency is changed to jakarta.faces:jakarta.faces-api:2.3.2, the 2.2 dependency will no longer be overridden and Maven will use them both, leading to all sorts of problems. If the transitive inclusion can’t be updated, this issue can typically be fixed by using exclusions, for example:

<dependency>
    <groupid>com.example</groupid>
    <artifactid>foo</artifactid>
    <scope>provided</scope>
    <exclusions>
        <exclusion>
            <groupid>javax.faces</groupid>
            <artifactid>javax.faces-api</artifactid>
         </exclusion>
     </exclusions>
 </dependency>


<dependency>
    <groupid>com.example</groupid>
    <artifactid>foo</artifactid>
    <scope>provided</scope>
    <exclusions>
        <exclusion>
            <groupid>javax.faces</groupid>
            <artifactid>javax.faces-api</artifactid>
         </exclusion>
     </exclusions>
 </dependency>


<dependency>
    <groupid>com.example</groupid>
    <artifactid>foo</artifactid>
    <scope>provided</scope>
    <exclusions>
        <exclusion>
            <groupid>javax.faces</groupid>
            <artifactid>javax.faces-api</artifactid>
         </exclusion>
     </exclusions>
 </dependency>


<dependency>
    <groupid>com.example</groupid>
    <artifactid>foo</artifactid>
    <scope>provided</scope>
    <exclusions>
        <exclusion>
            <groupid>javax.faces</groupid>
            <artifactid>javax.faces-api</artifactid>
         </exclusion>
     </exclusions>
 </dependency>

<dependency>
    <groupid>com.example</groupid>
    <artifactid>foo</artifactid>
    <scope>provided</scope>
    <exclusions>
        <exclusion>
            <groupid>javax.faces</groupid>
            <artifactid>javax.faces-api</artifactid>
         </exclusion>
     </exclusions>
 </dependency>

That brings me to the final step in the process.

Stage 3: Transfer and update the specification documents, prune the specifications, change the API package name, and release JDK 11. The final step of the transfer, which is currently in process and set to complete later this year, includes transferring the specification document source code (mostly in AsciiDoc). After the API code, implementation code, and TCK code, this is the final artifact to be transferred. Just like the Javadoc for the APIs, the specification documents will be updated to use the new terminology.

The highest impact item in this stage, however, is changing the package name in all the Java APIs from javax.* to jakarta.*. For instance javax.faces.context.FacesContext will become jakarta.faces.context.FacesContext. The consequence of this package name change is that code of existing applications will have to be updated, making this a nontrivial update.

Given the large amount of time that has passed since the Java EE 8 release, Jakarta EE 9 will officially require support for JDK 11. However, since JDK 8 is still so important, the APIs remain at JDK 8. Practically, this means the APIs have to be compiled with the JDK 8 source code level, but the implementations must pass the TCK running on JDK 11.

Because JDK 11 removed several specifications that had earlier been moved from Java EE into Java SE, these will now be moved back again. Jakarta Activation enters Jakarta EE as a required specification (specifically because it’s a required dependency of Jakarta Mail), while Jakarta XML Binding, Jakarta XML Web Services, Web Services Metadata, and SOAP with Attachments are added as optional specifications.

Jakarta Enterprise Beans (formerly EJB) will be reduced in size again. After entity beans (including EJB Query Language) and Java API for XML-based RPC (JAX-RPC) endpoints were pruned in EJB 3.1, it’s now time to prune the EJB 2.1 API group (for example, javax.ejb.EJBHome) and the so-called distributed interoperability.

Furthermore, the Deployment specification (JSR 88) and the Management specification (JSR 77) will be pruned as well. JSR 88 was already optional in Java EE 8, although JSR 77 was once slated for a major update; however, that failed to materialize. JAX-RPC, which was long ago superseded by JAX-WS and already optional in Java EE 8, will now finally be pruned as well, together with XML Registries, which also was already optional in Java EE 8.

Table 3 shows the JSF/Jakarta Faces example again updated for Jakarta EE 9, and changes related to Java EE 8 are in bold. The final row is tentative and subject to change still (though unlikely).

Transition from Java EE to Jakarta EE
Table 3. JSF/Jakarta example updated for Jakarta EE 9 (view larger image)

Conclusion

After Jakarta EE 9 has been released, and presumably all specification documents have been transferred and updated, the transfer of Java EE 8 will be considered done. At that point, everything related to Java EE will have been moved to the Eclipse Foundation and updated to the new branding.

Functionally speaking, Jakarta EE 9 is still essentially the same as Java EE 8, so from a purely functional perspective, neither Jakarta EE 8 nor Jakarta EE 9 are particularly enticing for users to update to. The purpose of those releases is, however, to give the community and the ecosystem (for example, tooling and library vendors) the opportunity to prepare their applications and products. Jakarta EE 10 will be the first version in which new functionality will appear. Table 4 shows the releases and release dates (tentative dates are denoted by *).

Transition from Java EE to Jakarta EE
Table 4. Releases and release dates

Related Posts

0 comments:

Post a Comment