3

As you probably know, javax had to change it's name to jakarta due to a trademark issue. Right now my company has gives our customers two .jar files, one for those using Tomcat 9 or earlier (javax) and one for Tomcat 10 (jakarta).

Is it possible to create one jar file that can see which Tomcat is used and for every import choose either javax or jakarta? This project does not use Spring.

I already found how to tell which Tomcat version is used.

I see JAVA does not have preprocessor directives like C++. Is there something similar that will allow me to swap the imports?

I also see that I don't have to use imports and just call the class using the whole path. This seems like a lot of work, but a possible solution. I just want to know if there is a quicker easier solution.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Brian
  • 307
  • 2
  • 15
  • 1
    This might be answerable here, but I _think_ it would be more suited to [SuperUser](https://superuser.com/). – Abion47 May 18 '21 at 18:54
  • 1
    Do you provide a library or an application? You can provide both `javax.*` and `jakarta.*` classes and use a `ServletContainerInitializer` to start up your application (actually two :-) ). How many of your classes use `javax.servlet.*` classes? – Piotr P. Karwasz May 18 '21 at 20:00
  • Piotr it is a library. That is my plan if no one can give me a better answer. I got 6 classes. – Brian May 18 '21 at 20:04

1 Answers1

3

The behavior you describe can be achieved with a ClassFileTransformer like the one provided by the Tomcat Migration Tool. Every copy of Tomcat 10 is bundled with the shaded version of the migration tool, so you don't need to distribute it independently.

You can create a jakarta.servlet.ServletContainerInitializer which will inject the ClassFileTransformer upon the web application startup:

import java.util.Set;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import org.apache.tomcat.InstrumentableClassLoader;
import org.apache.tomcat.jakartaee.ClassConverter;

public class JavaEEContainerInitializer implements javax.servlet.ServletContainerInitializer {

    @Override
    public void onStartup(Set<Class< ? >> c, ServletContext ctx) throws ServletException {
        final ClassLoader cl = Thread.currentThread().getContextClassLoader();
        if (cl instanceof InstrumentableClassLoader) {
            final InstrumentableClassLoader instrumentableCl = (InstrumentableClassLoader) cl;
            instrumentableCl.addTransformer(new ClassConverter());
        }
    }
}

However in the case of a library I would rather distribute two versions of it, so the library does not have to mess with the class loader of the application. E.g. openwebbeans provides two versions of the same artifact: a Java EE version and a Jakarta EE version with the jakarta classifier.

The aforementioned Tomcat Migration Tool can automatically generate the Jakarta EE version of the jar.

Piotr P. Karwasz
  • 12,857
  • 3
  • 20
  • 43