module $module_name {
requires $other_module;
exports $api_package;
}
new release schedule
Oracle JDK ≠ OpenJDK
Java on the desktop
starting with Java 9,
new major release every six months
Java 10 comes out today!
Java 11 comes out in September
no free Oracle JDK from Java 11 on
Oracle ships OpenJDK builds
but only for current major version:
⇝ 10 ⇝ 10.0.1 ⇝ 10.0.4
⇝ 11 ⇝ 11.0.1 ⇝ 11.0.4
⇝ 12 ⇝ 12.0.1 ⇝ 12.0.4
there is no spoon free LTS by Oracle!
⇝ More in my weekly:
No Free Java LTS Version?
Oracle released a
Java Client Roadmap Update
with plans for:
Applets
Web Start
JavaFX
Swing/AWT
Applets are deprecated in Java 9
Applets are removed from Java SE 11
Applets won’t be in Oracle’s OpenJDK 11 builds
Web Start is deprecated in Java 9
Web Start is removed from Java SE 11
Web Start won’t be in Oracle’s OpenJDK 11 builds
was never part of Java SE
(unlikely to change soon)
was never in Oracle’s OpenJDK builds
(just as unlikely to change)
will be removed from Oracle JDK 11
If your project uses JavaFX,
you can’t rely on its presence
on your customers' machines!
Quote:
Oracle is working with interested third parties to make it easier to build and maintain JavaFX as a separately distributable open-source module.
Not a quote:
Oracle wants to reduce/terminate investment in JavaFX.
Quote:
Oracle will continue developing Swing and AWT in Java SE 8 and Java SE 11 (18.9 LTS). This means they will be supported by Oracle through at least 2026.
What this doesn’t say:
support is free
support goes beyond Java 11
Hypothetically Oracle could
remove Swing/AWT from Java SE 12
and its statement still holds.
⇝ More in my weekly:
Java Desktop, Quo Vadis?
To hear about such news:
follow me on Twitter: @nipafx
subscribe to my weekly: tiny.cc/fx-weekly
there’s much to talk about
⇝ this can only cover parts of the JPMS
slides at slides.codefx.org
interacting parts
parts have
names
dependencies
capabilities
creates a graph
parts are packaged as JARs
to the JVM JARs
have no names
dependencies are unexpressed
have no coherent surface
JVM rolls them into one big ball of mud
unexpressed, transitive dependencies
shadowing, version conflicts
complex class loading
slow
unsecure
maintenance nightmare
first discussions about modularity in JDK
Project Jigsaw is created
exploratory phase; ends with JSR 376
prototype is released
Java 9 gets released with module system
Reliable Configuration
Strong Encapsulation
Scalable Systems (esp. the JDK)
Security, Performance, Maintainability
Version Selection
Multiple Versions
Package Isolation
Introducing modules, which
have a name
express dependencies
encapsulate internals
Everything else follows from here!
Modules, Readability, Accessibility
Implied Readability, Qualified Exports
Modular JARs, Module Path, Module Graph
Services
Unnamed Modules, Automatic Modules
Reflection, Layers
Run-time Images
These are the nodes in our graph.
Modules
have a unique name
express their dependencies
export specific packages
(and hide the rest)
Modules are JARs with a module-info.class
(aka Modular JAR)
gets generated from module-info.java
:
module $module_name {
requires $other_module;
exports $api_package;
}
this is called a Module Declaration or a
Module Descriptor
Readability brings edges into our graph.
It is the basis for Reliable Configuration.
For two modules A
and B
with
module A {
requires B;
}
we say
A requires B
A depends on B
A reads B
B is readable by A
Java will only compile/launch when
every dependency is fulfilled
there are no cycles
there is no ambiguity
boost for reliability
module system is strict and rigid
(no way to easily alter dependencies)
module name changes are not supported
accidental long cycles will cause problems
Accessibility governs which types a module can see.
It builds on top of Readability.
It is the basis for Strong Encapsulation.
A type in one module is only accessible
by code in another module if
the type is public
the package is exported
the second module reads the first
public
is no longer public
even reflection doesn’t work
more fine-grained mechanisms exist:
for module authors in module declaration
for module users as command line arguments
great boost for maintainability
major reason for community unrest
critical APIs survive until Java 10
(e.g. sun.misc.Unsafe
— see JEP 260)
life gets tougher for reflection-based
libraries and frameworks
Find it on GitHub!
public static void main(String[] args) {
List<SurpriseFactory> factories = asList(
new ChocolateFactory(), new QuoteFactory());
Calendar cal = Calendar.create(factories);
println(cal.asText());
}
modularization is not required
JARs continue to work as today!
(Unless you do forbidden things, more on that later.)
we can just put the application
on the class path as before
(Boring...)
# compile
$ javac -d classes/advent ${*.java}
# package with manifest
$ jar --create --file jars/advent.jar
--manifest ${manifest}
${*.class}
# run
$ java -jar jars/advent.jar
module advent {
// java.base is implicitly required
// requires no other modules
// exports no API
}
(Still Boring...)
# compile with module-info.java
$ javac -d classes/advent ${*.java}
# package with module-info.class
# and specify main class
$ jar --create
--file mods/advent.jar
--main-class advent.Main
${*.class}
# run by specifying a module path
# and a module to run (by name)
$ java --module-path mods --module advent
module surprise {
// requires no other modules
exports org.codefx.advent.surprise;
}
module calendar {
requires surprise;
exports org.codefx.advent.calendar;
}
module factories {
requires surprise;
exports org.codefx.advent.factories;
}
module advent {
requires calendar;
requires factories;
requires surprise;
}
# compile all modules at once
$ javac -d classes
--module-source-path "src"
--module advent
# package one by one, eventually advent
$ jar --create
--file mods/advent.jar
--main-class advent.Main
${*.class}
# launch the application
$ java --module-path mods --module advent
Transitive Dependencies |
Optional Dependencies |
Services |
Qualified Exports |
Reflective Access |
Transitive Dependencies |
Optional Dependencies |
Services |
Qualified Exports |
Reflective Access |
Not all dependencies are created equal:
most are used within a module
some are used on the boundary
between modules
Transitive dependencies are about the latter.
Example in calendar exposing surprise:
public static Calendar create(
List<SurpriseFactory> factories) {
// ...
}
⇝ Module calendar is unusable without surprise!
How can users of exposing module
determine required dependencies?
Try and error?
No!
Exposing module can mark dependencies
on exposed modules with
requires transitive
:
module A {
requires transitive B;
}
A reads B as usual
modules reading A will read B
without having to require it
⇝ A implies readability of B
Applied to the advent calendar:
module calendar {
requires transitive surprise;
// ...
}
Implied readability is surprisingly versatile
aggregator modules
splitting modules up
even merging modules
renaming modules
Making it easier to consume
calendar, factories, surprise:
module adventcalendar {
requires transitive calendar;
requires transitive factories;
requires transitive surprise;
}
If factories gets split into
api, chocolate, and quotes:
module factories {
requires transitive factory.api;
requires transitive factory.chocolate;
requires transitive factory.quotes;
}
If calendar, factories, surprise
are merged into adventcalendar:
module calendar {
requires transitive adventcalendar;
}
module factories {
requires transitive adventcalendar;
}
module surprise {
requires transitive adventcalendar;
}
Careful: Users suddenly depend on a large module!
If factories becomes surprisefactories:
module factories {
requires transitive surprisefactories;
}
With A requires transitive B
:
A reads B
any module reading A reads B
Applications:
make API usable without further dependencies
aggregator modules
splitting, merging, renaming modules
More at codefx.org:
Transitive Dependencies |
Optional Dependencies |
Services |
Qualified Exports |
Reflective Access |
Not all dependencies are equally required:
some are needed for a module to function
some can be absent and code adapts
some are only needed to enhance
another module’s features
Optional dependencies are about the latter two.
Use case:
a library may be absent from some deployments
code is aware and does not call absent modules
Example:
each SurpriseFactory
implementation
has its own module
advent works without any specific factory
Use case:
a project may provide usability functions
for other libraries
such code will not be called if library modules
are absent
Example:
hypothetical library uber-lib
provides usability functions for various libraries
With what we know so far:
for code to compile against another module
that module has to be required
a required module has to be present
at launch time
⇝ If a module’s types are used
it has to be present at run time
(Reliable configuration!)
Dependency can be marked requires static
:
module A {
requires static B;
}
at compile time: A requires B as usual
at run time:
if B is present, A reads B
otherwise, app can launch
but access to B can fail
For advent and the two factories:
module advent {
requires calendar;
requires surprise;
requires static factory.chocolate;
requires static factory.quote;
}
Checking whether module is present:
Optional<SurpriseFactory> createChocolateFactory() {
if (isModulePresent("factory.chocolate"))
return Optional.of(new ChocolateFactory());
else
return Optional.empty();
}
For uber-lib:
module uber.lib {
requires static com.google.guava;
requires static org.apache.commons.lang;
requires static io.vavr;
requires static com.aol.cyclops;
}
Assumptions:
nobody calls into Guava part without using Guava
no runtime checks necessary
With A requires static B
:
at compile time: A requires B as usual
at runtime: B may be absent
Two kinds of applications:
modules with code adapting to absence
utility libraries that aren’t called
without that dependency
More at codefx.org:
Transitive Dependencies |
Optional Dependencies |
Services |
Qualified Exports |
Reflective Access |
Consumers and implementations
of an API should be decoupled.
Service locator pattern:
service registry as central arbiter
implementors inform registry
consumers call registry to get implementations
In the JPMS:
modules declare which services they use
modules declare which services they provide
ServiceLoader
is the registry
code can interact with it to load services
Module declarations:
// consumer
module A {
uses some.Service;
}
// provider
module B {
provides some.Service
with some.Type;
}
(A and B need access to some.Service
)
A never "sees" providers like B
module system picks up all providers
A can get providers from ServiceLoader
ServiceLoader.load(Service.class)
module advent {
requires surprise;
uses surprise.SurpriseFactory;
}
module factory.chocolate {
requires surprise;
provides surprise.SurpriseFactory
with factory.quote.ChocolateFactory;
}
module factory.quote {
requires surprise;
provides surprise.SurpriseFactory
with factory.quote.QuoteFactory;
}
public static void main(String[] args) {
List<SurpriseFactory> factories = ServiceLoader
.load(SurpriseFactory.class).stream()
.map(Provider::get)
.collect(toList());
Calendar cal = Calendar.create(factories);
System.out.println(cal.asText());
}
To decouple API consumers and providers:
consumer uses Service
provider provides Service with Impl
Module system is service locator;
request implementations from ServiceLoader
:
ServiceLoader.load(Service.class)
Transitive Dependencies |
Optional Dependencies |
Services |
Qualified Exports |
Reflective Access |
So far, exported packages are visible
to every reading module.
What if a set of modules wants to share code?
Similar to utility classes:
if class should be visible outside of package,
it has to be public ⇝ visible to everybody
if package should be visible outside of module,
it has to be exported ⇝ visible to everybody
Module system fixes the former.
What about the latter?
module A {
exports some.pack to B;
}
B can access some.pack
as if regularly exported
other modules can not access it
as if not exported at all
To ease implementation of SurpriseFactory
:
create new module factory
add class AbstractSurpriseFactory
export that package only to
factory implementation modules
module factory {
requires transitive surprise;
exports factory
to factory.chocolate, factory.quote;
}
With A exports pack to B
:
only B can access types in some.pack
other modules behave as if some.pack
is not exported
Use to share sensible code between modules.
Transitive Dependencies |
Optional Dependencies |
Services |
Qualified Exports |
Reflective Access |
Exporting a package makes it public API:
modules can compile code against it
clients expect it to be stable
What if a type is only meant
to be accessed via reflection?
(Think Spring, Hibernate, etc.)
module A {
opens some.pack;
}
at compile time:
types in some.pack
are not accessible
at run time:
all types and members in some.pack
are accessible
A qualified variant (opens to
) exists.
open module A {
// no more `opens` directives
}
The same as open packages
but for all of them!
With open modules or open packages:
code can be made accessible
at run time only
particularly valuable to open
for reflection
Use to make types available for reflection
without making them public API.
a command line tool
analyzes bytecode to
detect dependencies
exists since Java 8
really shines with modules
$ jdeps sh-2.6.3.jar
> sh-2.6.3.jar -> java.base
> sh-2.6.3.jar -> java.datatransfer
> sh-2.6.3.jar -> java.desktop
# ... truncated more module dependencies ...
> edu.udo.sh -> com.beust.jcommander not found
> edu.udo.sh -> edu.udo.sh.data sh-2.6.3.jar
> edu.udo.sh -> edu.udo.sh.gui sh-2.6.3.jar
> edu.udo.sh -> edu.udo.sh.gui.util sh-2.6.3.jar
> edu.udo.sh -> edu.udo.sh.util sh-2.6.3.jar
> edu.udo.sh -> java.io java.base
> edu.udo.sh -> java.lang java.base
> edu.udo.sh -> javax.swing java.desktop
# ... truncated many more package dependencies ...
$ jdeps
--class-path 'libs/*' -recursive
sh-2.6.3.jar
# ... truncated split package warnings ...
# ... truncated some module/JAR dependencies...
> sh-2.6.3.jar -> libs/commons-codec-1.6.jar
> sh-2.6.3.jar -> libs/commons-io-2.4.jar
> sh-2.6.3.jar -> libs/dom4j-1.6.1.jar
> sh-2.6.3.jar -> libs/exp4j-0.1.38.jar
> sh-2.6.3.jar -> libs/guava-18.0.jar
# ... truncated more module/JAR dependencies...
# ... truncated many, many package dependencies ...
$ jdeps
--class-path 'libs/*' -recursive
--dot-output dots
sh-2.6.3.jar
# sed -i 's/-1.0-SNAPSHOT.jar//g' summary.dot
# sed -i '/java.base/d' summary.dot
$ dot -Tpng -O dots/summary.dot
$ jdeps
--class-path 'libs/*' -recursive
--jdk-internals
sh-2.6.3.jar
Some internal changes break existing code!
Just by running on JDK 9
(even without modularizing the application).
internal APIs are:
all in sun.*
most in com.sun.*
(unless marked @jdk.Exported
)
encapsulated at compile time
accessible at run time
for some time
critical APIs may survive longer
(e.g. sun.misc.Unsafe
)
JDeps can report internal dependencies:
$ jdeps --jdk-internals
-recursive --class-path 'libs/*'
scaffold-hunter-2.6.3.jar
> batik-codec.jar -> JDK removed internal API
> JPEGImageWriter -> JPEGCodec
> guava-18.0.jar -> jdk.unsupported
> Striped64 -> Unsafe
> scaffold-hunter-2.6.3.jar -> java.desktop
> SteppedComboBox -> WindowsComboBoxUI
look for reflection, especially
Class::forName
AccessibleObject::setAccessible
recursively check your dependencies!
fix your code
contact library developers
look for alternatives
(in the JDK or other libraries)
consider command line flags
--add-exports
, --add-opens
, or
--permit-illegal-access
java.activation (javax.activation
)
java.corba (CORBA packages)
java.transaction (javax.transaction
)
java.xml.bind (javax.xml.bind.*
)
java.xml.ws (JAX-WS packages)
java.xml.ws.annotation (javax.annotation
)
These are
deprecated for removal
not resolved by default
JDeps shows dependencies on platform modules:
$ jdeps -summary sh-2.6.3.jar
> sh-2.6.3.jar -> java.base
> sh-2.6.3.jar -> java.datatransfer
> sh-2.6.3.jar -> java.desktop
> sh-2.6.3.jar -> java.logging
> sh-2.6.3.jar -> java.prefs
> sh-2.6.3.jar -> java.sql
> sh-2.6.3.jar -> java.xml
add Java EE platform modules
with --add-modules
packages should have a unique origin
no module must read the same package
from two modules
The implementation is even stricter:
no two modules must contain
the same package (exported or not)
split packages on class path
are inaccessible
some libraries split java.xml.*
, e.g. xml-apis
some JBoss modules split, e.g.,
java.transaction
, java.xml.ws
jsr305 splits javax.annotation
JDeps reports split packages:
$ jdeps -summary
-recursive --class-path 'libs/*'
project.jar
> split package: javax.annotation
> [jrt:/java.xml.ws.annotation,
> libs/jsr305-3.0.2.jar]
Your artifacts:
rename one of the packages
merge package into the same artifact
merge the artifacts
place both artifacts on the class path
Otherwise:
upgrade the JDK module with the artifact
--patch-module
with the artifact’s content
new JDK/JRE layout
internal JARs are gone (e.g. rt.jar
, tools.jar
)
JARs are now JMODs
application class loader is no URLClassLoader
(no way to append to its class path)
new URL schema for run-time image content
does the code rummage around
in the JDK / JRE folder?
are URLs to JDK classes / resources handcrafted?
search for casts to URLClassLoader
Compact Profiles
Extension Mechanism
Endorsed Standards Override Mechanism
Boot Class Path Override
JRE version selection with -version:N
Yes, yes, there’s more:
Java 9 Migration Guide
(tiny.cc/java-9-migration)
Background:
And there are new version strings:
goodbye 1.9.0_31
, hello 9.1.4
(soon 18.3
?)
The most relevant for most applications:
internal APIs
split packages
Java EE modules
get your code in shape
(and prevent relapses)
check your dependencies and tools
if any are suspicious
(automatically true for IDEs, build tools):
make sure they’re alive
get them up to date!
or look for alternatives
download Java 9 and try it!
Create a JDK install with just the code you need:
know which modules your app uses (⇝ jdeps
)
create an image with those modules (⇝ jlink
)
This is about jlink
.
Create with jlink
$ jlink
# define output folder for the image
--output jdk
# where to find modules?
--module-path $JAVA_HOME/jmods
# which modules to add (includes dependencies!)
--add-modules java.base
Try it out:
$ jdk/bin/java --list-modules
> java.base
Say you use JAX-WS, JAXP, JAXB, JDBC, and JUL:
$ jlink
--output jdk
--module-path $JAVA_HOME/jmods
--add-modules java.xml.ws,java.xml,java.xml.bind
--add-modules java.sql,java.logging
$ jdk/bin/java --list-modules
> java.activation
> java.base
> java.compiler
> java.datatransfer
> java.desktop
> java.logging
> java.management
> java.prefs
> java.sql
> java.xml
> java.xml.bind
> java.xml.ws
> java.xml.ws.annotation
> jdk.httpserver
> jdk.unsupported
To create an image for your app:
all JARs need to be modularized!
including dependencies
Unless you use Gunnar Morling’s ModiTect,
which creates module descriptors on the fly.
Creating the image:
$ jlink
--output jdk
--module-path $JAVA_HOME/jmods:mods
--add-modules advent
# services are not resolves automatically
--add-modules factory.surprise,factory.chocolate
Launching the app:
jdk/bin/java --module advent
You can even create a launcher:
$ jlink
--output jdk
--module-path $JAVA_HOME/jmods:mods
--add-modules advent,...
# --launcher <name>=<module>[/<mainclass>]
--launcher calendar=advent
Launching the app:
jdk/bin/calendar
automatic service binding
(with --bind-services
)
various optimizations
(size and launch performance)
plugin API (not yet public)
cross OS image generation
You can use jlink
to:
create a runtime image
with just the right classes
create an application image
including your code
This should make certain kinds of deploys
smaller and easier.
⇜ Get my book!
You can hire me:
training (Java 8/9, JUnit 5)
consulting (Java 8/9)
puzzle-people: Kevin Dooley (CC-BY 2.0)
binary-code: Christiaan Colen (CC-BY-SA 2.0)
ball-of-mud-2: Andi Gentsch (CC-BY-SA 2.0)
jar-hell: Wellcome Library, London (CC-BY 4.0)
flag-amsterdam: George Rex (CC-BY-SA 2.0)
puzzle-cubed: David Singleton (CC-BY 2.0)
puzzle-piece-green:
StockMonkeys.com
(CC-BY 2.0)
puzzle-pieces-put-together:
Ken Teegardin
(CC-BY-SA 2.0)
iceberg:
NOAA’s National Ocean Service
(CC-BY 2.0)
class and module diagrams:
Nicolai Parlog
(CC-BY-NC 4.0)
keep-out: Brian Smithson (CC-BY 2.0)
garbage-only: Peter Kaminski (CC-BY 2.0)
golden-gate: Nicolas Raymond (CC-BY 2.0)
confusion: Procsilas Moscas (CC-BY 2.0)
module diagrams:
Nicolai Parlog
(CC-BY-NC 4.0)
broken-glass:
Eric Schmuttenmaer
(CC-BY-SA 2.0)
internals: David French (CC-BY 2.0)
cut: Jinx! (CC-BY-SA 2.0)
cells: Jonathan Lin (CC-BY-SA 2.0)
obsolete: Trevor King (CC-BY 2.0)
sign: Duncan Harris (CC-BY-SA 2.0)
question-mark: Milos Milosevic (CC-BY 2.0)
bundles: Danumurthi Mahendra (CC-BY 2.0)