-
- Notifications
You must be signed in to change notification settings - Fork 57
Description
The issue
The Jar files of the framework are not Jigsaw modules. Therefore, they are considered as "automatic" modules if put in the module path, or "unnamed" ones if put in the classpath instead.
When relying on Gradle, the Jar files are considered as "unnamed" ones, thus leading to troubles when trying to define our own modules depending on the Cloud framework (i.e. by describing the module through a module-info.java file). Here is a quote from the Gradle's documentation:
You probably want to use external libraries, like OSS libraries from Maven Central, in your modular Java project. Some libraries, in their newer versions, are already full modules with a module descriptor. For example, com.google.code.gson:gson:2.8.9 that has the module name com.google.gson.
Others, like org.apache.commons:commons-lang3:3.10, may not offer a full module descriptor but will at least contain an Automatic-Module-Name entry in their manifest file to define the module’s name (org.apache.commons.lang3 in the example). Such modules, that only have a name as module description, are called automatic module that export all their packages and can read all modules on the module path.
A third case are traditional libraries that provide no module information at all — for example commons-cli:commons-cli:1.4. Gradle puts such libraries on the classpath instead of the module path. The classpath is then treated as one module (the so called unnamed module) by Java.
It is worth to mention the fact an "application" module (i.e. a Jar file containing a module descriptor) can't depend on "unnamed" ones. This means that when defining a dependency to the Cloud framework in a module-info.java like that:
module my.module.name { requires cloud.annotations; }We end up with the following Java error when trying to build the project with Gradle:
module-info.java:2: error: module not found: cloud.annotations
requires cloud.annotations;
Even if an IDE like IntelliJ would not comply on its side since handling things differently. Thus, this proves that the framework's Jar files have not been included in the module path by Gradle.
Alternatives considered
One possibility is to adjust the Gradle's behavior at compilation time by adding the classpath in the module path, but that's not something that trivial to do nor convenient.
Relying on a Gradle plugin like extra-java-module-info may be the way to go even if not ideal. That's even more true in my case where I only have the issue with the Cloud framework.
Proposed solutions
Defining an automatic module name
One way to easily solve the issue with Gradle may be by simply defining the Automatic-Module-Name Jar manifest attribute:
jar { manifest { attributes("Automatic-Module-Name" to "cloud.annotations") } }Which should lead to the following content in the MANIFEST.MF Jar's file:
Manifest-Version: 1.0 Automatic-Module-Name: cloud.annotations Instead of the following content as we have for the version 2.0.0:
Manifest-Version: 1.0 However, it would be important to understand why the Gradle team decided to not automatically convert unnamed modules to automatic ones (reference). Especially:
but at most such that declare an automatic module name in the manifest which should be a sign that the library author at least thought about it and at least made it JPMS compatible.
Even if it seems we don't have risks regarding split-package issues:
loicd@DESKTOP-781OBR8 MINGW64 ~/Downloads $ jar --file=cloud-core-2.0.0.jar --describe-module No module descriptor found. Derived automatic module. cloud.core@2.0.0 automatic requires java.base mandated contains org.incendo.cloud contains org.incendo.cloud.annotation.specifier contains org.incendo.cloud.bean contains org.incendo.cloud.caption contains org.incendo.cloud.component contains org.incendo.cloud.component.preprocessor contains org.incendo.cloud.context contains org.incendo.cloud.description contains org.incendo.cloud.exception contains org.incendo.cloud.exception.handling contains org.incendo.cloud.exception.parsing contains org.incendo.cloud.execution contains org.incendo.cloud.execution.postprocessor contains org.incendo.cloud.execution.preprocessor contains org.incendo.cloud.help contains org.incendo.cloud.help.result contains org.incendo.cloud.injection contains org.incendo.cloud.internal contains org.incendo.cloud.key contains org.incendo.cloud.meta contains org.incendo.cloud.parser contains org.incendo.cloud.parser.aggregate contains org.incendo.cloud.parser.flag contains org.incendo.cloud.parser.standard contains org.incendo.cloud.permission contains org.incendo.cloud.setting contains org.incendo.cloud.state contains org.incendo.cloud.suggestion contains org.incendo.cloud.syntax contains org.incendo.cloud.type contains org.incendo.cloud.type.range contains org.incendo.cloud.type.tuple contains org.incendo.cloud.util contains org.incendo.cloud.util.annotation loicd@DESKTOP-781OBR8 MINGW64 ~/Downloads $ jar --file=cloud-annotations-2.0.0.jar --describe-module No module descriptor found. Derived automatic module. cloud.annotations@2.0.0 automatic requires java.base mandated provides javax.annotation.processing.Processor with org.incendo.cloud.annotations.processing.CommandContainerProcessor org.incendo.cloud.annotations.processing.CommandMethodProcessor contains org.incendo.cloud.annotations contains org.incendo.cloud.annotations.assembler contains org.incendo.cloud.annotations.descriptor contains org.incendo.cloud.annotations.exception contains org.incendo.cloud.annotations.extractor contains org.incendo.cloud.annotations.injection contains org.incendo.cloud.annotations.method contains org.incendo.cloud.annotations.parser contains org.incendo.cloud.annotations.processing contains org.incendo.cloud.annotations.string contains org.incendo.cloud.annotations.suggestion It may still preferable to define module descriptors (module-info.java files).
Defining module descriptors
This task may be less trivial and require more efforts.
Learning more about JPMS is required. A great guide on this front may be the Baeldung one.