javagradlejava-9java-platform-module-systemunnamed-module

Dependening on legacy jar in Java 9 Module System library using gradle


Problem

How do you create a java library jar that both:

The dependency is an implementation detail - should not be exported.

Sources

Having the following build.gradle (using gradle-6.8):

plugins {
    id 'java-library'
}

group = 'test'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '15'

repositories {
    mavenCentral()
}
java {
    modularity.inferModulePath = true
}

dependencies {
    implementation 'org.apache.commons:commons-exec:1.3'
}

and the following module-info.java:

module test.module {
    requires commons.exec;
}

Errors

I'm getting the following compile error:

module-info.java:2: error: module not found: commons.exec
    requires commons.exec;
                    ^

If I don't include requires commons.exec then the error becomes:

error: package org.apache.commons.exec is not visible
import org.apache.commons.exec.CommandLine;
                         ^
  (package org.apache.commons.exec is declared in the unnamed module,
   but module test.module does not read it)

commons.exec module name?

Running jar --file=commons-exec-1.3.jar --describe-module does output:

No module descriptor found. Derived automatic module.

commons.exec@1.3 automatic
requires java.base mandated
contains org.apache.commons.exec
contains org.apache.commons.exec.environment
contains org.apache.commons.exec.launcher
contains org.apache.commons.exec.util

So commons.exec looks like a valid module name for commons-exec-1.3.jar. Intelij Idea seem to agree and does auto-complete it in module-info.java. Though it fails at build time.


Solution

  • I managed to overcome the same issue using java-module-info plugin.

    This plugin allows you to add module information to a Java library that does not have any. If you do that, you can give it a proper module name and Gradle can pick it up to put it on the module path during compilation, testing and execution.

    plugins {
       id 'java-library'
       id("de.jjohannes.extra-java-module-info") version "0.6"
    }
    

    Add this section into your build.gradle to add the commons-exec module information

      extraJavaModuleInfo {
        module("commons-exec-1.3.jar", "org.apache.commons.exec", "1.3") {
            exports("org.apache.commons.exec")
        }
    }
    

    Add requires org.apache.commons.exec; to your module-info.java

    EDIT 1:

    Gradle 7.0 comes with full support for the Java module system. Users can now build, test, and run Java modules via Gradle. The mere presence of module-info.java will let Gradle infer that your jar is a module and has to be put on the modulepath instead of the traditional classpath.

    Using libraries that are not modules