Sorry for posting again. I really need to proof-read better. Please ignore the 
prior email.

I have recently had quite an adventure modifying several of Log4j’s Maven 
modules to 
implement JPMS on our master branch. It was an adventure due to a few issues:

1. https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8265826 
<https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8265826>. 
   This bug has been fixed in Java 17 but Log4j uses Java 11 to build.
2. Log4j-plugins builds an annotation processor, packages it with the 
annotations 
   and classes necessary to build and run plugins, creates and test jar and 
runs unit tests.
3. It is not possible to compile an annotation processor with a 
module-info.java present. 
   The compile will fail because it can’t find the annotation processor 
“service” when 
   compiling module-info.java.
4. It is very difficult to compile an annotation processor and then use it in 
the same Maven
   module. JPMS expects the annotation processor to either be on the classpath 
or specified 
   with the processorpath option. When a module-info.java is present, Maven 
automatically 
   moves everything to the module path. Maven only supports using coordinates 
to specify the 
   processor path, which don’t exist when the processor is in the same module. 
The only way 
   to solve this is to compile all the classes with proc=only without a 
module-info.java present.
5. Although number 4 might seem bad, it really doesn’t matter because javac 
will fail if a 
   module-info.java is present because module-info.java will have a reference 
to the service 
   class being generated by the annotation processor and for some reason 
module-info.java 
   is resolved before the annotation processor runs.
6. If the main set of classes are compiled with a module-info.java every other 
compile 
   in the Maven module must also be modularized. Likewise, if the main module 
does 
   not contain a module-info.java no other compile can either.
7. JPMS requires that every module have its own package space with no overlap
    with any other JPMS module.

So while generating the log4j-plugins module is quite painful, generating 
log4j-core isn’t 
much better. That is actually the primary focus for this list.

Log4j-core consists of the main classes packaged in the jar, a test jar that is 
used by 
downstream Maven modules, and the unit tests. Prior to JPMS one would just 
create 
the main jar and then package all the test classes and unit tests in a test 
jar. This can 
no longer be done with JPMS.

When a project publishes a test jar along with the main jar, just like any 
other JPMS 
module. the test jar cannot use the package space of the main classes. Log4j 
core 
uses org.apache.logging.log4j.core so I placed all the test utility classes 
under 
org.apache.logging.log4j.core.test. However, the unit tests all need to be 
packaged 
in the main package space since its module-info.java “extends” the main module 
and 
several unit tests are required to be in the same main package so that they can 
access 
package private methods specifically provided for testing.

In order to get this to work I had to perform the following steps:

        • Compile all the main classes except module-info.java with the Plugin 
annotation processor.
        • Compile the main module-info.java.
        • Compile the test classes used by other modules with module-info.java 
and 
          using the plugin preprocessor.
        • Package these test classes in a test jar.
        • Delete the module-info and generated source for the test classes.
        • Move the main module-info to a temp location.
        • Compile the unit test classes without module-info.java.
        • Move the main module-info back to the classes directory.
        • Compile module-info.java for unit tests.
        • Run the unit tests.
        • Create the main jar if the unit tests pass.

Were it not for JDK-8265826 I believe this could have been simplified somewhat 
to:

        • Compile all the main classes except module-info.java with the Plugin 
annotation processor.
        • Compile the main module-info.java.
        • Compile the test classes used by other modules with its 
module-info.java and 
         using the plugin preprocessor.
        • Package these test classes in a test jar.
        • Delete the module-info and generated source for the test classes.
        • Compile the unit test classes with its module-info.java.
        • Compile module-info.java for unit tests.
        • Run the unit tests.
        • Create the main jar if the unit tests pass.

So the gist of this entire email is pretty simple. Is there a way Maven could 
be modified 
to better support creating test jars with JPMS?

Ralph

Reply via email to