↗️ This README only covers one section of the demo. The master branch contains more information.
Now it's time to really get to know Jigsaw and split that monolith up into separate modules.
The "surprise API", i.e. Surprise
and SurpriseFactory
, is a great success and we want to separate it from the monolith.
Then there are the factories that create the surprises, which turn out to be very dynamic. A lot of work is being done here, they change frequently and which factories are used changes from release to release. So we want to isolate them.
At the same time we plan to create a large Christmas application of which the calendar is only one part. So we'd like to have a separate module for that as well.
We end up with these modules:
- surprise -
Surprise
andSurpriseFactory
- calendar - the calendar, which uses the surprise API
- factories - the
SurpriseFactory
implementations - main - the original application, now hollowed out to the
Main
class
Looking at their dependencies we see that surprise depends on no other module of ours. Both calendar and factories make use of its types so they must depend on it. Finally, main uses the factories to create the calendar so it depends on both.
The first step is to reorganize the source code:
src
- org.codefx.demo.advent.calendar: the "calendar" module
- org ...
module-info.java
- org.codefx.demo.advent.factories: the "factories" module
- org ...
module-info.java
- org.codefx.demo.advent.surprise: the "surprise" module
- org ...
module-info.java
- org.codefx.demo.advent: the "main" module
- org ...
module-info.java
.gitignore
compileAndRun.sh
LICENSE
README
This is the same directory structure as used by the official quick start guide. The depiction is not complete, though, and does not contain the folders below org
, which are the individual packages and eventually the source files.
Let's start with surprise.
There are no required
clauses as it has no dependencies.
(Except for java.base
, which is always implicitly required.)
It exports the package org.codefx.demo.advent.surprise
because that contains the two classes Surprise
and SurpriseFactory
.
So the module-info.java
looks as follows:
module org.codefx.demo.advent.surprise {
// requires no other modules
// publicly accessible packages
exports org.codefx.demo.advent.surprise;
}
Compiling and packaging is very similar to the previous section. It is in fact even easier because surprise contains no main class:
# compile
javac -d classes/org.codefx.demo.advent.surprise
${list of source files}
# package
jar --create
--file mods/org.codefx.demo.advent.surprise.jar
${compiled class files}
The calendar has fields and parameters with types from the surprise API so the module must depend on surprise.
Adding requires org.codefx.demo.advent.surprise
to the module achieves this.
But there is an additional twist:
The publicly accessible method Calendar::createWithSurprises
declares a parameter of type List<SurpriseFactory>
.
So all modules using this API must also read surprise.
Otherwise Jigsaw would prevent them from accessing these types, which would lead to compile and runtime errors.
Marking the requires
clause as transitive
fixes this.
With it any module that depends on calendar can also access surprise (called implied readability).
This module's API consists of the class Calendar
.
For it to be publicly accessible the containing package org.codefx.demo.advent.calendar
must be exported.
Note that CalendarSheet
, private to the same package, will not be visible outside the module.
This is analog to before where a package-private class from another package was also not visible.
The final module-info looks as follows:
module org.codefx.demo.advent.calendar {
// required modules
requires transitive org.codefx.demo.advent.surprise;
// publicly accessible packages
exports org.codefx.demo.advent.calendar;
}
Compilation is almost like before but the dependency on surprise must of course be reflected here.
For that it suffices to point the compiler to the directory mods
as it contains the required module (the sum of such directories is called the module path):
# compile
javac --module-path mods
-d classes/org.codefx.demo.advent.calendar
${list of source files}
# package
jar --create
--file mods/org.codefx.demo.advent.calendar.jar
${compiled class files}
The factories implement SurpriseFactory
so this module must obviously depend on theirs.
And since they return instances of Surprise
from published methods the same line of though as above leads to a requires transitive
clause.
The factories can be found in the package org.codefx.demo.advent.factories
so that must be exported.
Note that the package visible class AbstractSurpriseFactory
is not accessible outside this module.
As it is currently implemented Jigsaw will also not allow reflection to access it.
The only way around this are command line flags.
Together:
module org.codefx.demo.advent.factories {
// required modules
requires transitive org.codefx.demo.advent.surprise;
// publicly accessible packages
exports org.codefx.demo.advent.factories;
}
Compilation and packaging is analog to calendar.
Our application requires the two modules calendar and factories to compile and run. It has no API to export.
module org.codefx.demo.advent {
// required modules
requires org.codefx.demo.advent.calendar;
requires org.codefx.demo.advent.factories;
// no exports
}
Compiling and packaging is like with last section's single module except that the compiler needs to know where to look for required modules:
javac --module-path mods
-d classes/org.codefx.demo.advent
${list of source files}
jar --create \
--file mods/org.codefx.demo.advent.jar \
--main-class org.codefx.demo.advent.Main \
${compiled class files}
java --module-path mods -m org.codefx.demo.advent
Instead of compiling the modules one by one it is possible to compile all at once by telling the compiler where to find the sources for specific modules with --module-source-path
.
In our case all module's source folders are found below src
, so the command looks as follows:
javac9 -d classes \
--module-source-path "src" \
--module org.codefx.demo.advent
Note that it is not necessary to list source files.