Jakarta EE based WildFly have to communicate successfully with previous Java EE based WildFly

In  ee ejb migration

Overview

This feature will ensure bidirectional EJB, JNDI & HTTP remote protocols backward compatibility between Java EE 8- and Jakarta EE 9+ WildFly versions.

Issue Metadata

Issue:

QE Contacts:

TODO!!!

Testing By

  • Engineering

  • QE

Affected Projects or Components:

  • JBoss Marshalling, JBoss EJB Client, WildFly Naming Client, WildFly Http Client

Relevant Installation Types

  • Traditional standalone server (unzipped or provisioned by Galleon)

  • Managed domain

  • OpenShift s2i

  • Bootable jar

Requirements

Hard requirements

  • Ability of Java EE 8- client/server to communicate with Jakarta EE 9+ server/client via EJB, JNDI and HTTP remote protocols.

  • Ability of Jakarta EE 9+ client/server to communicate with Java EE 8- server/client via EJB, JNDI and HTTP remote protocols.

  • Java EE 8- WildFly versions cannot be modified in order to be able to communicate with Jakarta EE9+ WildFly variants.

  • Backward compatibility mode is off by default, and can be enabled by setting the org.wildfly.ee.namespace.interop system property to true.

  • If the property is set to true on a Java EE 8- server/client, it is ignored, as those clients and servers are not responsible for handling interoperability.

None requirements

  • Infinispan and Clustering protocols backward compatibility.

  • ABI compatibility for non existing mapping of classes/methods available in Java EE 8- but not in Jakarta EE 9+ and vice versa. Users will need to migrate their clients/applications for such problematic scenarios manually.

Test Plan

The test will be created for hard requirements scenarios. This test will reside in QE’s testsuite and will become one of the must-execute tests for ensuring backward compatibility between Java EE based WildFly and Jakarta EE based WildFly variants.

Community Documentation

Community documentation will be provided for how the aforementioned compatibility among remote EE clients and servers. It will be added to the WildFly documentation, and it will be based on this analysis document as reference.

Also, the wildfly-http-client project documentation must also be updated describing the changes applied to the WildFly HTTP Client protocol.

Release Note Content

Bullet point: support interoperability between Jakarta EE 9 servers and Java EE 8 or older servers

Design Notes

The backward compatibility between two Java EE 8- and Jakarta EE 9+ remote endpoints has to be enabled at the endpoint that supports Jakarta EE 9+. To accomplish that, the Jakarta EE 9+ endpoint (a client or a server) is started with the system property org.wildfly.ee.namespace.interop set to true. With this property enabled, a Jakarta EE 9+ server or client becomes compatible with other Java EE 8- servers/clients, while still keeping the ability to communicate with other Jakarta EE 9+ endpoints(as long as those endpoints have the same property enabled, see Communication Across Multiple Endpoints). We say that this particular server or client is running on EE namespace interoperable mode.

Only Jakarta EE 9+ endpoints can run on EE namespace interoperable mode. For other endpoints, this property will be ignored if set to true.

To implement that, we propose the following changes to the following libraries:

JBoss Marshalling

  • Introduce new abstraction ClassNameTransformer that will allow to remap one java type to another java type.

  • Provide hooks for that abstraction to allow renaming java types before/after marshalling/unmarshalling them.

  • Provide default Java EE <→ Jakarta EE class name transformer implementation.

JBoss EJB Client

Since JBoss EJB protocol supports 'handshake' kind of messages it is possible to detect other side protocol version before exchanging messages. Because of this we propose to:

  • Introduce new major version 4 of remote EJB protocol to indicate EJB client/server is supporting Jakarta EE 9+.

  • Activate version 4 of EJB protocol if and only if JBoss EJB client/server is used in Jakarta EE9+ environment.

  • Install Java EE <→ Jakarta EE class name transformer if and only if the client/server is running on EE namespace interoperable mode, and the other side is using version 1 or 2 or 3 of the protocol.

WildFly Naming Client

Since WildFly NAMING protocol supports 'handshake' kind of messages it is possible to detect other side protocol version before exchanging messages. Because of this we propose to:

  • Introduce new major version 3 of remote NAMING protocol to indicate NAMING client/server is supporting Jakarta EE 9+.

  • Activate version 3 of NAMING protocol if and only if WildFly NAMING client/server is used in Jakarta EE9+ environment.

  • Install Java EE <→ Jakarta EE class name transformer if and only if the client/server is running on EE namespace interoperable mode, and the other side is using version 1 or 2 of the protocol.

WildFly Http Client

Since WildFly HTTP protocol doesn’t support 'handshake' kind of messages it is not possible to detect other side protocol version in advance. The version number is in the URL of the particular service provided by the server, and the request is sent directly to that particular URL, without prior negotiation. Because of this we propose to:

  • implement a handshake based on a HTTP header

  • Install Java EE <→ Jakarta EE class name transformer if and only if the server is on EE namespace interoperable mode and the handshake indicates the connection requires such transformer

The handshaking between two Jakarta 9+ client and servers, both running on EE namespace interoperable mode, works as follows:

  • whenever the client side opens a new connection to a server, the first request it sends via that connection contains the x-wf-ee-ns: interop HTTP header, and that first request is marshalled with the Java EE 8- <→ Jakarta EE 9+ class transformer, transforming the EE api classes in the request to javax EE namespace

  • the server receives the request, verifies it has the x-wf-ee-ns: interop header, and enables the class name transformer to transform the request back to jakarta namespace. The server though sends the response without the transformer, with the Jakarta EE 9+ classes intact, and adds the x-wf-ee-ns: jakarta header to the response

  • the client receives such response and reads the header. It indicates that this connection is a Jakarta EE 9+ connection at both ends, and the client is not supposed to transform the namespace of the EE classes contained in the response data.

  • from that point on, whenever the client uses the same connection, no transformation is done on its side. Furhtermore, all requests sent by the client through this connection contain the x-wf-ee-ns: jakarta header

  • whenever the server receives a request with the x-wf-ee-ns: jakarta header, it knows that the client is a Jakarta EE 9+ client running on EE namespace interoperable mode. So, it does not use any class file transformer for reading the request and writing the response.

Here is how the handshaking works when an EE namespace interoperable client sends a request to a Java EE 8- server:

  • as in the previous case, the client side opens a new connection to a server, and the first request it sends via that connection contains the x-wf-ee-ns: interop HTTP header. Also, that first request is marshalled with the Java EE <→ Jakarta EE class transformer, porting the request from javakarta to javax EE namespace

  • the Java EE 8- server receives the request in Java EE format and handles the request normally, ignoring the x-wf-ee-ns: interop header.

  • the client receives the server response and checks it does not contain the x-wf-ee-ns header. So, it enables the Java EE <→ Jakarta EE class name transformer for that connection’s lifetime.

  • from that point on, whenever the client uses the same connection, no extra header is added, and the class name transformer is always enabled, guaranteeing that the Jakarta EE 9+ classes are ported to Java EE 8- namespace on every request, and transformed back on every response

The final handshake scenario is a Java EE 8- client sending a request to a EE namespace interoperable server:

  • client sends the request to the server in the standard way, and the request naturally can contain javax EE namespace classes

  • the server receives the request and verifies it does not contain the x-wf-ee-ns header. The server interprets this as an indication that the client is Java EE 8-, and it enables the class name transformer for both reading the request and writing the response back to the client.

If a Jakarta EE9+ server receives a request from an EE namespace interoperable client, even if the server is not on interoperable mode it will be able to respond, because it will check the presence of the x-wf-ee-ns: interop header. In this case, it will apply the class name transformation to the request on read, and it will add the x-wf-ee-ns: jakarta header to the response on write. This allows the non-interoperable server to be able to serve requests from interoperable clients.

Communication Across Multiple Endpoints

The following table summarizes the possible scenarios where a client can communicate with a server remotely:

Java EE 8- client

Jakarta EE 9+ client

Jakarta EE 9+ namespace interoperable client

Java EE 8- server

Yes

No

Yes

Jakarta EE 9+ server

No

Yes

Yes

Jakarta EE 9+ namespace interoperable server

Yes

No

Yes

Notice that, in order for a Jakarta EE 9+ client to be able to communicate with a Jakarta EE 9+ interoperable server, the client must be also running on EE namespace interoperable mode.

Regarding handling cases where a server is non interoperable and receives a Javax EE namespace request, the code could, in the future, be smarter and detect potential mismatches between the endpoints that will cause the communication to fail. Such as current endpoint is not EE namespace interoperable while the other end is otherwise, or even if the other end is a Java EE 8- instance. If we do so, we could print a warning indicating that the EE namespace interoperability must be enabled at this server/client, so it can establish proper communication with the remote endpoint. However, there are two options to print this warning:

  • verifying if the message indicates the remote endpoint is running on EE namespace interoperability mode

  • or catching a ClassCastException when marshalling/unmarshalling and checking if the exception message contains "javax"

While the former option sounds more elegant. The client would not have a way of detecting if it is invoking an incompatible server, unless we change the protocol to apply a x-wf-ee-ns:javax header to Java 8- clients requests. Both extra actions at the two ends will result in a performance penalty. The solution we propose at this moment was designed in a way to prevent by all means any kind of overhead in the standard communication between two Jakarta EE 9+ endpoints, leaving the extra overhead to the special case of an Java EE 9+ endpoint needing to communicate with Java EE8- endpoints.

All servers and clients are interoperable if the communication does not involve any EE class. This might lead a user to mistakenly believe that no such configuration was needed "before, when my code was doing one thing, but now it has changed and it no longer works". However, full interoperability among the clients and servers with mixed different EE libraries (i.e, javax vs jakarta namespace) is only guaranteed when all Jakarta EE 9+ elements of that communication are running on EE namespace interoperable mode.