This talk is for a very important new feature in Java SE 9. Code named Jigsaw, this feature modularizes the Java SE platform.
The coolest thing we do here is to create a custom JRE
Code: https://bitbucket.org/stybz/jigsaw.sty/
PPT: https://www.slideshare.net/mihailstoynov/modules-in-java-finally-openjdk-jigsaw
Video: https://www.youtube.com/watch?v=W5LeNPtPrqw
2. What is this talk all about? (Agenda)
• This talk is for a very important new feature in Java SE 9
• Code named Jigsaw, this feature modularizes the Java SE platform
• Agenda
• Who am I?
• Problems with monolithic java
• Solutions in Java SE 9
• Jigsaw in examples
• JDK9 Early Access with Jigsaw
• A modular example, transitivity
• Services and Custom JREs
3. Who am I? @mihailstoynov
• By day: sty.bz
• Java
• Security audits, web pen testing, sec tools
• Training, travelling ->
• By night: jug.bg
• Java evangelism
• Submitting Java patches, writing manuals,
early adoption
• jprime.io – organize big java conf in Sofia
• Co-authoring books, university courses
• Weekends
• Bikes
5. Why do we need jigsaw? 1
• "Small" devices can run Java,
but JRE size is a problem
• Clouds don't like wasting resources
loading a large JRE full of
unnecessary classes
8. Why do we need jigsaw? 4
• People use sun.misc.* or *.internal.* APIs, which was not intended
• Securing the platform is difficult if everyone can read anything
9. Problem: source code is monolithic 201
• JRE source code itself is monolithic – it has no modules
• Solution (JEP 201)
• Reorganize mercurial repo
• src/share/classes/java/lang/Object.java
src/share/native/java/lang/Object.c
->
src/java.base/share/classes/java/lang/Object.java
src/java.base/share/native/java/lang/Object.c
• Rename solaris to unix
• Compile repo -> compile modules
• Enforce module boundaries at build time
10. Problem: JRE code is not modular 200
• JRE itself is not using modules
• Solution (JEP 200)
• Create modules for code governed by JCP (module java.base)
• Modules for other code in the JDK (module jdk.javadoc )
• Define requires public
• All reside in $JAVA_HOME/jmods
11. Offtopic: jmods are not for you
• .jmod format was created for the platform
• can have native code
• Overall very cool
• $ jmod list $JAVA_HOME/jmods/java.base.jmod
--list of classes—-
--native code too (so, dylib)--
• $ jmod describe $JAVA_HOME/jmods/java.base.jmod
• Describes what it exports, what it conceals, who it exports it too
and stuff
12. Problem: JRE code is not modular 200
Check out the java.compact{1..3}
13. Problem: Internal APIs 260
• Many are using internal APIs (example: sun.misc.Unsafe)
• Solution (JEP 260)
• Provide safe alternative (other JEPs)
• Non critical (Base64Decoder) are encapsulated or deprecated
• Critical APIs (Unsafe) are rewritten and encapsulated (JEP 259)
14. Problem: JRE is too big 282
• The JRE is too big
• Some distributions are over 100MB
• In mobile devices: CPU is strong enough for java, but little space
• Solution (JEP 282)
• In Java 9 we can create a "custom runtime image"
• A tool that can do that is called jlink
• The same tool can also add our
application modules
• Only the ones we need
15. Problem: Put it all together 261 (376)
• JSR 376 (Java Platform Module System) proposes changes and
extensions to
• the Java programming language
• the Java virtual machine
• the standard Java APIs
• JSR 376 Will be implemented in JEP 261
• JCP = Java Community Process (IBM, SAP, RedHat "participate")
• JSR = Java Specification Request (specifies new standards, JCP)
• JEP = Java Enhancement Process (implementations, non JCP)
16. More problems
• The base classes had a lot of cyclic dependencies
• They had to be unwounded
• It took several years to specify the module format
• Several abandoned formats so far
• It took several years to specify the scope of Jigsaw
• For example no dynamic loading/unloading
• No luck for OSGi
• Mark Reinhold said that this will not be implemented soon
19. pre-Java9 class visibility
• Until Java 9 a class had the following visibility "levels":
• public
• friendly, package private (includes protected)
• protected
• private
20. post-Java9 class visibility
• In Java 9 new levels of "public" are provided:
• public
• To all
• To some modules (we specify them)
• Only to our module
• friendly, package private (includes protected)
• protected
• private
21. Creating a simple module bz.sty.logger
• Important note: just like packages, module names are dir names
• module-info.java
module bz.sty.logger {
requires java.base; //implicit
exports bz.sty.logger;
}
• Logger.java
package bz.sty.logger;
public class Logger {
public void log(String message) {
System.out.println("Logger: "+message);
}
}
• Compilation
$ javac -d mods/
Logger/bz.sty.logger/bz/sty/logger/Logger.java
Logger/bz.sty.logger/module-info.java
22. Referencing the log module bz.sty.main
• module-info.java:
module bz.sty.main {
requires bz.sty.logger; //implicit
//exports bz.sty.logger;
}
• Program.java
public class Program {
public void main(String... args) {
new Logger.log("Hello, World!");
}
}
• Compilation
$ javac -d mods
-modulepath mods/
Main/bz.sty.main/bz/sty/main/Program.java
Main/bz.sty.main/module-info.java
23. Compile multiple modules at once
$ javac
-d mods/
-modulesourcepath Logger/:Main/
$(find Logger/ Main/ -name *.java)
• What did we do here?
• All source paths are in modulesourcepath
• We use a bit of bash magic to add all java files
• All should be deployed
24. Running a multi module app
$ java
-modulepath mods/
-m bz.sty.main/bz.sty.main.Program
Logger: Hello, World!
25. Support for Jigsaw
• Maven, Gradle
• None
• IntelliJ IDEA, Eclipse
• None
• I use IDEA modules and duplicate the dependencies
• NetBeans
• http://wiki.netbeans.org/JDK9Support
• But I don't like it, so we won't use it
• When will it be released
• With Java SE 9
• Used to be mid'2016, jigsaw delayed it to Q1'2017
• http://www.java9countdown.xyz/
• Nobody believes it will be on time
27. What's in the jar?
$ jar --print-module-descriptor
--file=mlib/bz.sty.main.jar
bz.sty.main
requires bz.sty.logger
requires mandated java.base
conceals bz.sty.main
main-class bz.sty.main.Program
$ jar --print-module-descriptor
--file=mlib/bz.sty.logger@2.0.jar
bz.sty.logger@199.0
requires java.base
exports bz.sty.logger
28. Transitivity ("requires public")
• We create a new module, called prettylogger
• public class PrettyLogger extends Logger
• We change dependencies so that main prettylogger logger
• The new main:
public class Program {
public static void main(String... args) {
Logger logger = new PrettyLogger();
logger.log("Hello, World!");
}
}
• module-info.java
module bz.sty.prettylogger {
requires public bz.sty.logger;
exports bz.sty.prettylogger;
}
29. Quering the JDK module system
• $ java –listmods
• List all modules in the JDK
• Shows the version
• $ jmod describe $JAVA_HOME/jmods/java.base.jmod
• Shows a very detailed description
• $ jmod list $JAVA_HOME/jmods/java.base.jmod
• A list of all classes in the jmod
31. Services
• Services allow for loose coupling between service
consumers modules and service providers modules
• Since Java SE 6, ServiceLoader API allows extending applications
• SL detects implementations of an interface and loads them
• This solution still works nicely with Java modules
• It is now sufficient the modules to be present on module-path
• Basically we define an interface/abstract class and we state that we
depend on their implementations
• we cant run without an implementation
• Other modules implement that interface/abstract class
• All is defined in the module-info
32. The module and the provider
module bz.sty.pluggablelogger {
exports bz.sty.pluggablelogger;
exports bz.sty.pluggablelogger.spi;
uses bz.sty.pluggablelogger.spi.PluggableLoggerProvider;
}
public abstract class PluggableLoggerProvider {
protected PluggableLoggerProvider() { }
public abstract PluggableLogger getPluggableLogger();
}
33. PluggableLogger
public abstract class PluggableLogger {
public static PluggableLogger get() {
ServiceLoader<PluggableLoggerProvider> sl
= ServiceLoader.load(PluggableLoggerProvider.class);
Iterator<PluggableLoggerProvider> iter = sl.iterator();
if (!iter.hasNext())
throw new RuntimeException("No service providers found!");
PluggableLoggerProvider provider = iter.next();
return provider.getPluggableLogger();
}
protected PluggableLogger() { }
public abstract void log(String message);
}
34. SuperLogger (implementing module)
module bz.sty.superlogger {
requires bz.sty.pluggablelogger;
exports bz.sty.superlogger;
provides bz.sty.pluggablelogger.spi.PluggableLoggerProvider
with bz.sty.superlogger.SuperLoggerProvider;
}
public class SuperLoggerProvider extends PluggableLoggerProvider {
public PluggableLogger getPluggableLogger() {
return new SuperLogger();
}
}
public class SuperLogger extends PluggableLogger {
public void log(String message) {
System.out.println("SuperLogger: " + message);
}
}
35. Running it all together
$ javac -d mods –modulesourcepath
PluggableLogger:PluggableLoggerImpl:PluggableLoggerMain
$(find Pluggable* -name *.java)
$ jar --create --file=X.jar –C mods/mdl .
$ java -mp mlib/ -m bz.sty.pluggableloggerexample
SuperLogger: Hello, World!
$ java -Xdiag:resolver
37. Create a custom JRE
• And now a drum roll for the coolest feature
• We hinted that it's now possible to create custom JREs
• The tool is called JLINK
• jlink takes the smallest set of needed jars and jmods and creates a
new JRE in a dir. Very WOW
39. Stuff we didn't discuss, but it's important
• Jdeps
• A tool to check if you use internal APIs
• Unnamed modules
• All old jars
• Automatic modules
• Making old jars to modules
• Migrating an application gradually
• Not difficult at all, but only after IntelliJ/Eclipse and maven support
• The console is difficult
• Mixing --classpath and --modulepath
• It takes some getting used to