Hi,
I would like to transition a java 11/gradle 7.1.1 project to use jigsaw modules.
To achive this the code has been prepared by moving the classes into packages that will be the jigsaw modules. Now the goal is to to have the first two modules. Basically this means that one module is extracted and the rest of the code is changed into a module as well. From there it should be easy to extract all the other modules.
While all of this was quite easy to setup in a tiny demo project it proves to be tricky in the details. It was possible to set up the modules and get them to compile and run however it is difficult to get it working in the actual project.
Now I am not so sure what is even configured correct and what is not. I am not even sure gradle supports everything we need (regarding to jigsaw) out of the box or whether plugins are a must for this. One plugin that sounds promising is: GitHub - java9-modularity/gradle-modules-plugin: This Gradle plugin helps working with the Java Platform Module System However if it is somehow possible to achive a running config without using a plugin that would be nice since it makes it harder to be able to upgrade gradle from time to time.
The build.gradle in root contains a lot of helper tasks and is holds configuration for the subprojects:
allprojects {
group 'my.group'
repositories {
maven {
url 'http://server:8081/repository/'
allowInsecureProtocol = true
}
}
}
subprojects {
apply plugin: 'de.jjohannes.extra-java-module-info'
afterEvaluate {
java {
modularity.inferModulePath = true
withSourcesJar()
withJavadocJar()
toolchain {
languageVersion = JavaLanguageVersion.of(11)
vendor = JvmVendorSpec.ADOPTOPENJDK
}
}
compileJava {
modularity.inferModulePath = true
}
compileTestJava {
modularity.inferModulePath = true
}
test {
modularity.inferModulePath = true
useJUnitPlatform()
testLogging {
outputs.upToDateWhen { false }
showStandardStreams = true
}
minHeapSize = "512m"
maxHeapSize = "3g"
// is necessary
forkEvery = 1
maxParallelForks = 3
reports {
junitXml.required = true
junitXml.outputPerTestCase = true
html.required = false
}
// tests that create a file need a dir to store it - it should be deleted with the clean target
def testTempResDir = Paths.get(buildDir.toString(), "tmp", "test", "res").toString()
mkdir testTempResDir
systemProperty "test.temp.res.dir", testTempResDir
systemProperty "test.root.dir", rootDir
}
javadoc {
modularity.inferModulePath = true
excludes = ['**/*.form', '**/*.xml', '**/*.html',
'**/*.ttf', '**/*.png', '**/*.txt',
'**/*.jrxml', '**/*.jasper', '**/*.jpg',
'**/*.csv', '**/*.ftl', '**/*.gpg', '**/*.wsdl',
'**/META-INF/', '**/*.bak']
}
}
}
Can someone point out a bit how the module path is handled in gradle and also how it should be handled regarding jar manifests and run.bat scripts. I have noticed that the startup scripts that are generated have paths so long they just wouldn’t start (assuming lots of dependencies). Can I add the jars to the manifest like we have added them to the classpath? This is how we did that before:
project.configurations.implementation.setCanBeResolved(true)
project.configurations.runtimeOnly.setCanBeResolved(true)
jar {
manifest {
attributes(
'Main-Class': application.mainClass.get(),
'Class-Path': configurations.implementation.collect { "lib/$it.name" }.join(' ') + " " +
configurations.runtimeOnly.collect { "lib/$it.name" }.join(' ')
}
From most of my searches modules are still something that aren’t adopted that well so many dependencies aren’t compatible still. Is it better to wrap them with an automatic module plugin or to read everything from the unnamed module? If so is it applied to the project in a good way with the compilerArgs there or can that be done better?
Does gradle facilitate the testing with modules in any way? I couldn’t find anything useful beside the aformentioned plugin that helps with both whitebox and blackbox testing.
With this setup the project can be compiled and the tests run - however the application just won’t run and fails with an exception mentioning that the module cannot be found:
> Task :suite:run FAILED
Error occurred during initialization of boot layer
java.lang.module.FindException: Module suite not found
The module-info.java from suite starts with open module suite {...}
so the name should be fine.
Also if I create another module and place a main in there the application would start just fine (well at least it would run the main of that module)
Intellij also wouldn’t show any signs of the module-info being wrong or anything else in the project setup that is obviously wrong.
The project is structured like that:
root
├── modules
│ ├── core
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── java
│ │ │ │ │ ├── my.packages
│ │ │ │ │ ├── module-info.java
│ │ │ │ ├── test
│ │ └── build.gradle
│ ├── suite (rest)
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── java
│ │ │ │ │ ├── my.other.packages
│ │ │ │ │ ├── module-info.java
│ │ │ │ ├── test
│ │ └── build.gradle
├── build.gradle
└── settings.gradle
settings.gradle:
rootProject.name = 'myApp'
include ':suite', ':core'
project(':suite').projectDir = new File('modules/suite')
project(':core').projectDir = new File('modules/core')
suite:build.gradle
plugins {
id 'application'
id 'de.jjohannes.extra-java-module-info'
}
extraJavaModuleInfo {
failOnMissingModuleInfo = false
automaticModule("jasperreports-6.16.0.jar", "net.sf.jasperreports")
}
dependencies {
implementation project(':core')
implementation 'net.sf.jasperreports:jasperreports:6.16.0'
}
compileJava {
modularity.inferModulePath = true
doFirst {
options.compilerArgs = [
'--add-reads', 'suite=ALL-UNNAMED'
]
}
}
application {
mainModule = 'suite'
mainClass = 'my.other.packages.Main'
}
core:build.gradle
plugins {
id 'java-library'
id 'de.jjohannes.extra-java-module-info'
}
extraJavaModuleInfo {
failOnMissingModuleInfo = false
automaticModule("commons-cli-1.4.jar", "org.apache.commons.cli")
}
dependencies {
implementation 'commons-cli:commons-cli:1.4'
}
Any help would be much appreciated.
Thanks for reading.