Introduction
Java 9 introduced the Java Platform Module System a.k.a. JMS, JPMS and Project Jigsaw. With JMS, classloading and visibility are much different than before with modules being the way to import and export classes and resources. There are articles out that that explain the changes. Mark Reinhold made a post about JMS. What are the benefits? Segmentation, smaller build sizes. Let's talk bout Gradle.
Gradle is a step above Maven as a build system. With incremental parallel builds, it is super quick! Gradle has performance analysis features such as build scan and profile reports to help with figuring out what is taking so long. The one thing about Gradle though is that it is a little obtuse sometimes. Take the time to learn Groovy since it is a superset of Java.
So, how do we build a Java module for a single project?
Let's say our project root starts from /Project. In the root is src and build. src contains main and etc. the usual directory structure.
We need to declare our module with the exports and imports.
Module File Path: /Project/src/main/java/module-info.java
module com.newcompany.project {
requires java.sql
exports com.newcompany.project.common;
}
Let's configure gradle: /Project/build.gradle
Gradle, under the hood, will run javac to compile java code, so we have to configure it to run javac with the modules. If an error is encountered where compilation fails due to it not finding the modules, it is because the modules have not been declared in the javac options specially the compileJava task.
ext.moduleName = 'com.newcompany.project'
allprojects {
group 'com.newcompany'
version '1.0.0-SNAPSHOT'
}
apply plugin: 'java'
targetCompatibility = 1.10
sourceCompatibility = 1.10
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
options.incremental = true
}
compileJava {
inputs.property("moduleName", moduleName)
doFirst {
options.compilerArgs.addAll([
'--module-path', classpath.asPath,
'--add-modules', moduleName
])
classpath = files()
}
}
...dependencies, test, sourceSets, etc...
That's all you need to start building Java modules with Gradle.
Multi-Project Builds
For multi module projects, the compileJava step can be copied to the build.gradle of each sub project or you can wrap compileJava in an afterEvaluate wrapped in subprojects. For more information on each of the classes and functions below, look at the Gradle reference.
Root Project build gradle: /Project/build.gradle
subprojects {
afterEvaluate {
compileJava {
inputs.property("moduleName", moduleName)
doFirst {
options.compilerArgs.addAll([
'--module-path', classpath.asPath,
'--add-modules', moduleName
])
classpath = files()
}
}
}
}
Subproject build gradle: /Project/subproject/build.gradle
ext.moduleName = 'com.newcompany.project.subproject'
That's all you need for building multi-project modules.
How about JavaDocs and Sources jars?
Often forgotten about are these two artifacts.
For a single project module build, the code below can be pasted straight into the root build.gradle otherwise wrap with subprojects to run it for each sub-project.
afterEvaluate {
javadoc {
inputs.property("moduleName", moduleName)
doFirst {
options.addStringOption('-module-path', classpath.asPath)
}
}
}
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = 'sources'
from sourceSets.main.java.srcDirs
}
task javadocJar(type: Jar, dependsOn: javadoc) {
from javadoc.destinationDir
classifier = 'javadoc'
}
artifacts {
archives sourcesJar
archives javadocJar
}
The javadoc task will fail if the modules are not included or if javadoc syntax is wrong, luckily it will gladly tell you what is wrong so you can correct it.
Update: If you are using newer versions of gradle then you may need this to fix javadoc module lookup failure: https://www.normansoven.com/post/fixing-gradle-javadoc-task-failure-when-using-java-modules
Some extra javadoc options
To make sure HTML5 is used for the javadoc and the new javadoc tokens are supported, they need to be declared.
javadoc {
options.addBooleanOption('html5', true)
options.tags('apiNote:a:API Note:')
options.tags('implSpec:a:Implementation Requirements:')
options.tags('implNote:a:Implementation Note:')
options.tags('param')
options.tags('return')
options.tags('throws')
options.tags('since')
options.tags('version')
options.tags('serialData')
options.tags('factory')
options.tags('see')
}
Conclusion
Building modules with Gradle isn't hard or tedious at all. Every Java developer should consider using modules for their projects to enforce segmentation and smaller build sizes. I implore you to not create a big project as one module. I don't typically create single project builds anymore since a scripting language can be used for smaller projects.