Search code examples
javaservletstomcat9tomcat10jakarta-migration

Create a jar file that supports javax.* and jakarta.*


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.


Solution

  • 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.