Préparez-vous à
la modularité
selon Java 9
Rémi Forax
Alexis Hassler
#DevoxxFR
Official Hashtag
#DevoxxFR
#PreparezVousALaModularitéSelonJava9AvecRemiForaxEtAlexisHassle
#DevoxxFR
Dramatic intro
#DevoxxFR
Classpath - compile time
Build tool
Maven
Gradle
…
#DevoxxFR
Classpath - runtime
jarjarjarjar jarjarjar jar jarjar asm-3.1.jarjarjar asm-2.3.jar
#DevoxxFR
Scale Down
rt.jar
66 MB
guava.jar
2.3 MB
spring-
context.jar
1 MB
#DevoxxFR
Security
CVE-2015-6420, …
#DevoxxFR
Security
commons-collection.jar
#DevoxxFR
Security
commons-collection.jar
#DevoxxFR
Modular JDK
#DevoxxFR
Modular JDK
rt.jar tools.jar
#DevoxxFR
Modular JDK
java.basejava.desktop java.sql
java.naming java.xmljava.compiler
java.logging
java.management
#DevoxxFR
Modular JDK
java.basejava.desktop
java.sql
java.naming
java.xml
java.compiler
java.logging
java.management
#DevoxxFR
Deprecated JDK modules
java.xml.bind java.xml.ws
java.xml.ws.annotations
java.transaction
java.corba
java.activation
#DevoxxFR
Deprecated JDK modules
69… back to Java EE
#DevoxxFR
Non-standard JDK modules
sun.* com.sun.*
Deleted classes
Hidden packages
#DevoxxFR
Non-standard JDK modules
Internal modules
jdk.net jdk.attach
jdk.httpserver
jdk.*
#DevoxxFR
Module descriptor
/* module-info.java */
module app.main {
    requires java.base;
    requires java.sql;
    requires java.management;
}
#DevoxxFR
Build module
javac -d target/main/exploded                                  
      --source-path src/main/java                              
      $(find src/main/java -name "*.java")
jar --create                                                   
    --file target/main/artifact/app.main-1.0.jar               
    -C target/exploded/app.main .
#DevoxxFR
Run module
java --module-path modules                                
     --class-path dependencies/library.jar                
     -m app.main/com.app.main.Main
#DevoxxFR
java.base
java.xml.bind
jdk.httpserver java.desktop
image.show
#DevoxxFR
Modular application
#DevoxxFR
Modular application
app.main
app.module1
app.module2
#DevoxxFR
Module descriptor
/* module-info.java */
module app.main {
    requires java.sql;
    requires java.management;    
    requires app.module1;
    requires app.module2;
}
#DevoxxFR
Export package
module app.module1 {
    …;
    exports com.app.api;     
}
#DevoxxFR
Multi-modules layout
1 project = n modules
javac -d target/main/exploded                           
      --module-source-path src/main/java                
      $(find src/main/java -name "*.java")
#DevoxxFR
Multi-modules layout
src/main/java
app.main
module-info.java
fr/app/pkg
... .java
fr/app/main
Main.java
app.api
module-info.java
a folder with a qualified name
...
#DevoxxFR
Create module
jar --create                                                   
    --file target/main/artifact/app.main-1.0.jar               
    --main-class com.app.main.Main                             
    -C target/exploded/app.main .
#DevoxxFR
The Jigsaw Sandwich
Runtime
Compile time
#DevoxxFR
The Jigsaw Sandwich
app.main
@1.1
app.module1
@1.0
app.module2
@1.1
#DevoxxFR
Create module
jar --create                                                   
    --file target/main/artifact/app.main-1.0.jar               
    --module-version=1.0                                       
    --main-class com.app.main.Main                             
    -C target/exploded/app.main .
#DevoxxFR
fr.sw.img.web
fr.sw.img.db
fr.sw.img.inmemoryfr.sw.img.service fr.sw.img.data
fr.sw.common.*
#DevoxxFR
image.show
sw.common
image.backend
image.data
#DevoxxFR
Encapsulation
#DevoxxFR
Visibility
Public
Protected
Package
Private
#DevoxxFR
Visibility
Public
Protected
Package
Private
Inside a module
#DevoxxFR
Visibility
Public       For other module
module app.api {
    …;
    exports com.app.api;
}
#DevoxxFR
Split package
#DevoxxFR
Regular export
module app.impl {
    …
    exports com.app.internal;    
}
#DevoxxFR
app.main
com.app.internal
app.impl
app.api
com.app.api
?
Restricted export
module app.impl {
    …
    exports com.app.internal    
         to app.api
}
#DevoxxFR
app.main
com.comp.app.internal
app.impl
app.api
com.comp.app.api
?
fr.sw.img.service
image.backend
fr.sw.img.db
fr.sw.img.inmemory
#DevoxxFR
image.backend
fr.sw.img.service
image.db
fr.sw.img.db
image.inmem
fr.sw.img.inmemory
#DevoxxFR
sun.misc.Unsafe
module java.base {
    …
    exports sun.misc;
    exports jdk.internal.misc to           
        java.rmi,
        java.sql,
        jdk.charsets,
        …
}
module jdk.unsupported {
    exports sun.misc;
}
 New internal unsafe API
(hidden)

Keep sun.misc in 9,
remove features when
public replacement APIs exist
#DevoxxFR
Deep reflection
 
  field.setAccessible(true);
 
  java.lang.reflect.InaccessibleObjectException
#DevoxxFR
Deep reflection
Open a package
module app.impl {
    …;
    opens exports com.app.internal;
}
Open all the packages of the module
open module app.impl {
    …;
}
#DevoxxFR
Module dependency
#DevoxxFR
Transitive dependency
app.main
app.module1
app.module2
transitive
#DevoxxFR
Transitive dependency
module app.module1 {
    requires …;
    requires transitive app.module2;
}
module app.main {
    requires …;
    requires app.module1;
    requires app.module2;
}
#DevoxxFR
Static dependency
module app.module1 {
    requires …;
    requires static app.module2;   
}
Compile time only
#DevoxxFR
Plain old jar
app.main
app.module1
app.module2
old-lib-1.2
?
#DevoxxFR
Plain old jar
No dependency to classpath
javac --class-path deps/old-lib-1.2.jar                  
      …
#DevoxxFR
Plain old jar
app.main app.module1
old-lib-1.2classpath
modulepath
#DevoxxFR
Automatic Module
Plain old jar in module-path
javac --module-path deps                                 
      …
module app.module1 {
    …;
    requires old.lib;       
}
#DevoxxFR
Automatic Module
app.main app.module1
old-lib-1.2
classpath
modulepath
#DevoxxFR
Mixed mode
app.main app.module1
old-lib
classpath
modulepath
other-lib
#DevoxxFR
Mixed mode
javac --module-path deps                                
      --class-path deps/other-lib.jar                   
      …
#DevoxxFR
image.show slf4j-api
slf4j-simple
#DevoxxFR
Module Service
#DevoxxFR
Interface / Implementations
#DevoxxFR
Main
Service
ServiceImpl
Interface / Implementations
#DevoxxFR
Main
Service
ServiceImpl
User / Provider
module app.main {
    requires app.api;
    uses … .api.Service;    
}
module app.impl {
    requires app.api;
    provides … .api.Service
        with … .service.ServiceImpl;    
}
#DevoxxFR
Main
Service
ServiceImpl
module app.api {
    exports … .api;    
}
Usage
    …
    ServiceLoader.load(ModuleInput.class)
                 .stream()
                 . … ;
    …
#DevoxxFR
image.db
image.inmemory
image.backend
sw.fwk
ImageDB
ImageInMemory
DAO
#DevoxxFR
Running on Java 9
Compiling in Java 8
#DevoxxFR
Big kill switch
--permit-illegal-access
 This option will only be supported in JDK 9.
It will be removed in JDK 10.
#DevoxxFR
Command line java / javac
Add root module
--add-modules
#DevoxxFR
Command line java / javac
--add-reads module=anotherModule
--add-exports module/package=anotherModule
--add-opens module/package=anotherModule
#DevoxxFR
jdeps
Diagnostic tool
since Java 8
jdeps --jdk-internals
#DevoxxFR
Tests
#DevoxxFR
Test module
src/main/java
module-info.java
fr/app/pkg
AClass.java
src/test/java
module-info.java
fr/app/pkg/test
AClassTest.java
#DevoxxFR
Test module
Compile time
app.module1
com.app.pkg1
<<requires>> <<requires>>
com.app.pkg1.test
app.module1.test
<<reads>>
junit
org.junit
#DevoxxFR
Test module
Test module-info.java
module app.module1.test {     
    requires junit;
    requires app.module1;
}
#DevoxxFR
Test module
Compile
$JAVA_HOME/bin/javac                                             
    -d target/test/exploded                                      
    --module-path dependencies:target/main/artifact              
    $(find src/test/java/ -name "*.java")
#DevoxxFR
Test module
Run time
hamcrest-core
app.module1
com.app.pkg1
<<requires>> <<requires>>
com.app.pkg1.test
app.module1.test
<<reads>><<reflection>>
junit
org.junit
#DevoxxFR
Test module
Run
$JAVA_HOME/bin/java                                                 
    --module-path dependencies:                                     
                  target/main/artifact:target/test/artifact         
    --class-path dependencies/hamcrest-core-1.3.jar                 
    --add-modules app.module1.test                                  
    -m junit/org.junit.runner.JUnitCore com.app.pkg1.test.AClassTest
#DevoxxFR
Single module
src/main/java
module-info.java
fr/app/pkg
AClass.java
src/test/java
module-info.java
fr/app/pkg
AClassTest.java
#DevoxxFR
Single module
Compile time
junit
<<requires>>
app.module1
com.app.pkg1com.app.pkg1org.junit
#DevoxxFR
Single module
Compile
$JAVA_HOME/bin/javac                                             
    -d target/test/exploded                                      
    --module-path dependencies:target/main/artifact              
    --add-reads app.module1=junit                                
    --add-modules junit                                          
    --patch-module app.module1=src/test/java                     
    $(find src/test/java/ -name "*.java")
#DevoxxFR
Single module
Run time
app.module1
com.app.pkg1com.app.pkg1
hamcrest-core
<<requires>>
<<reflection>>
junit
org.junit
#DevoxxFR
Single module
Run
$JAVA_HOME/bin/java                                              
    --module-path dependencies:                                  
                  target/main/artifact                           
    --class-path dependencies/hamcrest-core-1.3.jar              
    --add-modules app.module1                                    
    --add-reads app.module1=junit                                
    --add-exports app.module1/com.app.pkg1=junit                 
    --patch-module app.module1=target/test/exploded              
    -m junit/org.junit.runner.JUnitCore com.app.pkg1.AClassTest
#DevoxxFR
junit image.inmem
fr.sw.img.inmemoryfr.sw.img.inmemoryorg.junit
#DevoxxFR
Going Native
#DevoxxFR
jlink
Custom runtime image
Specific for a platform
For small devices or in the cloud
#DevoxxFR
jlink
$ jlink --module-path $JAVA_HOME/jmods:            
                      target/modules               
        --add-modules app.main,                    
                      app.service                  
        --launcher appx=app.main                   
        --output target/image
#DevoxxFR
#DevoxxFR
Minimal VM
$ jlink …                                          
        --vm=minimal                               
        --output image
size of the classical VM: 20 megs
size of the minimal VM: 3 megs
#DevoxxFR
Ahead Of Time compilation
 experimental feature !
Module ⇒ shared library
.so, .dll, .dynlib
$ jaotc --module-path target/modules            
        --module app.main
#DevoxxFR
Ahead Of Time compilation
 experimental feature !
Run in AOT mode
./image/bin/java                                          
    -XX:+UseAOT                                           
    -XX:AOTLibrary=./lib/libjava.base.so                  
    …
#DevoxxFR
Benchmarks
#DevoxxFR
Benchmarks
#DevoxxFR
Conclusion
#DevoxxFR
Jigsaw Goals
Reliable configuration
No classpath anymore
compile time / runtime fidelity
#DevoxxFR
Jigsaw Goals
Scale down the platform
downsized unit of re-use
#DevoxxFR
Jigsaw Goals
Strong encapsulation
Separate public types from implementation
Enhanced security
#DevoxxFR
Jigsaw Goals
Closed World
Ahead of time optimizations
#DevoxxFR
Jigsaw Goals
Reliable configuration
Scale down the platform
Strong encapsulation
Closed World
#DevoxxFR
Planning
#DevoxxFR
Questions ?
#DevoxxFR
Références
Download JDK 9 Early Access
https://jdk9.java.net/download/
Oracle JDK 9 Documentation
http://docs.oracle.com/javase/9/
JavaOne, DevoxxBE talks
http://openjdk.java.net/projects/jigsaw/talks/
#DevoxxFR
Références
Demo
https://github.com/hasalex/devoxxfr17-demo
Moduletools
https://github.com/forax/moduletools
Pro
https://github.com/forax/pro
#DevoxxFR
Merci
#DevoxxFR

Devoxx17 - Préparez-vous à la modularité selon Java 9