module java.sql {
requires transitive java.logging
requires transitive java.xml
uses java.sql.Driver
exports java.sql
exports javax.sql
exports javax.transaction.xa
}
this is about bringing a Java 8 app
into the future
we’re mostly looking at the bad and the ugly
slides at slides.codefx.org
code on GitHub at
CodeFX-org/demo-java-9-migration
JARs have:
no name the JVM cares about
no explicit dependencies
no well-defined API
no concept of versions
Some consequences:
NoClassDefFoundError
no encapsulation across JARs
version conflicts
Modules are like JARs but have:
proper names
explicit dependencies
a well-defined API
no concept of versions 😭
Important goals:
reliable configuration
strong encapsulation
A file module-info.java
:
module java.sql {
requires transitive java.logging
requires transitive java.xml
uses java.sql.Driver
exports java.sql
exports javax.sql
exports javax.transaction.xa
}
module java.sql {
requires transitive java.logging
requires transitive java.xml
}
Module system enforces:
all required modules are present
no ambiguity
no static dependency cycles
no split packages
module java.sql {
exports java.sql
exports javax.sql
exports javax.transaction.xa
}
Say you want to access java.sql.ResultSet
.
Module system only grants access if:
ResultSet
is public
java.sql
is exported by java.sql
your module reads java.sql
decoupling via services
finer grained dependencies and exports
open packages and modules (for reflection)
unnamed and automatic modules (for migration)
layers (for containers)
jlink
to create runtime images
Some internal changes break existing code!
Just by running on JDK 11
(even without modularizing the application).
Modularized JDK and legacy JARs have to cooperate.
Two requirements:
for the module system to work,
everything needs to be a module
for compatibility, the class path
and regular JARs have to keep working
The Unnamed Module
contains all JARs on the class path
(including modular JARs).
has no name (surprise!)
can read all modules
exports all packages
Inside the unnamed module
"the chaos of the class path" lives on.
internal APIs
Java EE modules
split packages
runtime images
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
--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 were
deprecated for removal in ⑨
removed in ⑪
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
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:
Background:
And there are new version strings:
goodbye 1.9.0_31
, hello 9.0.1
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 11 and try it!
Java 10 contains few breaking changes:
mostly removal of deprecated methods
and command line options
bytecode level increases to 54.0
Every release will do that.
⇝ Get used to updating ASM, et al.
Java 11 contains few breaking changes:
removal of deprecated classes/methods
and command line options
removal of deprecated Java EE modules
bytecode level increases to 55.0
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
)
⇝ Pick a third-party implementation
only rely on standardized behavior
heed deprecation warnings (jdeprscan
)
keep dependencies and tools up to date
build on each release (including EA)
report problems
Projects may decide to keep
Java 8 as baseline.
Still:
run (nightly?) builds
on Java 9+
find problems early
avoid dead ends
Some problems may
prevent building on 9+:
outdated build tool
outdated build plugins
Try testing on 9+!
Essentially:
# regular Java 8 build
mvn clean install
# on Java 9+:
mvn surefire:test
mvn failsafe:integration-test
Class path is for regular JARs,
module path is for modular JARs?
Wrong!
Class path is for the unnamed module,
module path is for named modules!
You can put modular JARs in the class path
and everything works like before.
you can modularize projects,
without clients even realizing
start with the modularization
from the bottom up
What if you want to modularize your code
but your dependencies aren’t yet?
proper modules can not depend on
"the chaos on the class path"
this is not possible:
module monitor.rest {
// we need spark.core
requires unnamed;
}
An Automatic Module
is created for each "regular" JAR
on the module path.
name defined by manifest entry
AUTOMATIC-MODULE-NAME
or
derived from JAR name
can read all modules
(including the Unnamed Module)
exports all packages
put spark-core-2.7.0.jar
on the module path
then this works:
module monitor.rest {
requires spark.core;
}
Class Path | Module Path | |
---|---|---|
Regular JAR | Unnamed Module | Automatic Module |
Modular JAR | Unnamed Module | Named Module |
💻 codefx.org
🐦 @nipafx
Slides at slides.codefx.org
⇜ Get my book!
You can hire me:
training (Java 8-12, JUnit 5)
consulting (Java 8-12)