Skip to content

Commit

Permalink
Merge pull request #526 from ropalka/WFLY-16296
Browse files Browse the repository at this point in the history
[WFLY-16296] EAP6 and EAP7 applications have to communicate with EAP8 apps successfully
  • Loading branch information
darranl authored Dec 6, 2023
2 parents 8702919 + 1cef2d4 commit cc716ec
Showing 1 changed file with 214 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
= Jakarta EE based WildFly have to communicate successfully with previous Java EE based WildFly
:author: Richard Opalka; Flavia Rainone
:email: [email protected]; frainone@@redhat.com
:toc: left
:icons: font
:idprefix:
:idseparator: -

== 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:

* https://issues.redhat.com/browse/WFLY-16296[WFLY-16296]

=== Dev Contacts:
* mailto:[email protected][Richard Opalka]
* mailto:[email protected][Flavia Rainone]

=== QE Contacts:
TODO!!!

=== Testing By
* [ ] Engineering

* [x] QE

=== Affected Projects or Components:

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

=== Relevant Installation Types
* [x] Traditional standalone server (unzipped or provisioned by Galleon)

* [x] Managed domain

* [x] OpenShift s2i

* [x] 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.

[IMPORTANT]
====
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_*.
====

0 comments on commit cc716ec

Please sign in to comment.