The Java 9 Module System In Action

Nicolai Parlog

codefx.org / @nipafx

Impedance Mismatch

Where the JVM disagrees with us

How do you think about Software?

What is it made of?

How I think about Software

  • interacting parts

  • parts have

    • names

    • dependencies

    • capabilities

  • creates a graph

How the JVM thinks about it

  • 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

Consequences

  • JAR hell

    • unexpressed, transitive dependencies

    • shadowing, version conflicts

    • complex class loading

  • slow

  • unsecure

  • maintenance nightmare

Project Jigsaw

Teaching the JVM about that graph

Quick History

2005

first discussions about modularity in JDK

2008

Project Jigsaw is created

2011-2014

exploratory phase; ends with JSR 376

2015

prototype is released

2017

Java 9 gets released with Jigsaw

Disclaimer / Call to arms

  • this is based on early access builds

  • some things can still change

  • time for community feedback
    is running out

Goals

  • Reliable Configuration

  • Strong Encapsulation

  • Scalable Systems (esp. the JDK)

  • Security, Performance, Maintainability

Non-Goals

  • Multiple Versions

  • Version Selection

Means

Introducing modules, which

  • have a name

  • express dependencies

  • encapsulate internals

Everything else follows from here!

Concepts & Features

  • Modules, Readability, Accessibility

  • Implied Readability, Qualified Exports

  • Modular JARs, Module Path, Module Graph

  • Services

  • Unnamed Modules, Automatic Modules

  • Reflection, Layers

  • Run-time Images

Java Module System Basics

Getting started...

Modules

Pieces of a puzzle

These are the nodes in our graph.

Definition

Modules

  • have a unique name

  • express their dependencies

  • export specific packages
    (and hide the rest)

Implementation

  • 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

Putting the pieces together

Readability brings edges into our graph.

It is the basis for Reliable Configuration.

Definition

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

Reliable Configuration

Java will only compile/launch when

  • every dependency is fulfilled

  • there are no cycles

  • there is no ambiguity

Accessibility

Hiding internals

Accessibility governs which types a module can see.

It builds on top of Readability.

It is the basis for Strong Encapsulation.

Definition

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

Strong Encapsulation

  • public is no longer public

  • even reflection doesn’t work

  • command line provides escape hatches

Consequences

  • great boost for maintainability

  • also the major reason for community unrest

  • critical APIs might survive until Java 10
    (e.g. sun.misc.Unsafe — see JEP 260)

  • life might get tougher for reflection-based
    libraries and frameworks

Jigsaw Advent Calendar

A simple example

Find it on GitHub!

Structure

advent calendar structure

Code

public static void main(String[] args) {
	List<SurpriseF_> fac = asList(
		new ChocolateF_(), new QuoteF_());
	Calendar cal = Calendar.create(fac);
	println(cal.asText());
}
_

No Module

  • 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...)

A single module

Modularization

advent calendar module single
module advent {
	// java.base is implicitly required
	// requires no other modules
	// exports no API
}

(Still Boring...)

A single module

Readability & Accessibility

advent calendar readability accessibility

Multiple Modules

b2e21fbf

Multiple Modules

advent calendar module multi
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;
}

Multiple Modules

Compilation, Packaging, Execution

# First compile/package the other modules
#   ('surprise', 'calendar', 'factories')
#   into folder 'mods'.
# Compile/package 'advent':
javac -p mods -d classes/advent ${*.java}
jar -c --file=mods/advent.jar
	--main-class=org.codefx.advent.Main
	${*.class}
# Launch the application:
java -p mods -m advent

Migration

Moving Into Module Land

Why Is It Even An Option?

  • most module systems are "in or out",

  • but modularized JDK and legacy JARs
    have to cooperate!

  • so migration has to be possible

Enablers

Migration is enabled by two features:

  • Unnamed Module(s)

  • Automatic Modules

And the fact that module and class path coexist:

  • modular JARs can be put on either

  • "regular" JARs can be put on either

The Unnamed Module

Why The Class Path "Just Works"

Definition

The Unnamed Module
contains all JARs on the class path
(including modular JARs).

  • has no name (surprise!)

  • can read all modules

  • exports all packages

Example

Put all your JARs on the class path.

migration unnamed

No Access

  • what if your code was modularized?
    and your dependencies were not?

  • proper modules can not depend on
    "the chaos on the class path"

  • this is not possible:

module advent {
	requires unnamed;
}

No Access

migration unnamed dependency

Automatic Modules

From Modules To The Class Path

Definition

An Automatic Module
is created for each "regular" JAR
on the module path.

  • gets a name based on the file name

  • can read all modules
    (including the Unnamed Module)

  • exports all packages

Example

  • put guava-19.0.jar on the module path

  • then this works:

module advent {
	requires guava;
}

Example

migration automatic

What Goes Where?

Class PathModule Path

Regular JAR

Unnamed Module

Automatic Module

Modular JAR

Unnamed Module

Named Module

Migration Strategies

Two strategies emerge:

  • bottom-up migration

  • top-down migration

Bottom-Up Migration

Works best for Projects without
unmodularized dependencies
(libraries).

  • turn project JARs into modules

  • they still work on the class path

  • clients can move them to the module path
    whenever they want

Top-Down Migration

Required for Projects with
unmodularized dependencies
(applications).

  • turn project JARs into modules

Top-Down Migration

  • modularized dependencies:

    • require direct ones

    • put all on the module path

  • unmodularized dependencies:

    • require direct ones with automatic name

    • put direct ones on the module path

    • put others on the class path

Top-Down Migration

When dependencies get modularized:

  • hopefully the name didn’t change

  • if they are already on the module path,
    nothing changes

  • otherwise move them there

  • check their dependencies

Compatibility

What to look out for
when running on JDK 9

Break Stuff

Some internal changes can break existing code!

Just by running on JDK 9
(even without modularizing the application).

JEP 261 contains a list of risks.

Internal APIs

  • internal APIs are:

    • all in sun.*

    • most in com.sun.*
      (unless marked @jdk.Exported)

  • most if that will be encapsulated

  • critical APIs will survive until Java 10
    (e.g. sun.misc.Unsafe — see JEP 260)

  • six deprecated methods adding/removing
    PropertyChangeListener got removed
    from LogManager and Pack200

What to look for?

  • use jdeps (preferably from JDK 9)

    • either manually:
      jdeps --jdk-internals {.class/.jar}

    • or as part of your build (e.g. with JDeps Mvn)

  • look for reflection, especially

    • Class::forName

    • AccessibleObject::setAccessible

  • recursively check your dependencies!

What to do?

  1. contact library developers

  2. look for alternatives
    (in the JDK or other libraries)

  3. consider command line flag --add-exports

  4. turn to the Jigsaw mailing lists

Split Packages

  • packages should have a unique origin

  • no module must read the same package
    from two modules

The current implementation is even stricter:

  • no two modules must contain
    the same package (exported or not)

  • split packages on the class path
    will be inaccessible

Examples

  • some libraries split java.xml.*, e.g. xml-apis

  • some JBoss modules split, e.g.,
    java.transaction, java.xml.ws

  • jsr305 splits javax.annotation

What to look for?

  • search your code and dependencies
    for java(x) packages (jdeps can help)

  • no tool support (yet?)

What to do?

  1. is the split on purpose / necessary?

  2. find other ways to solve the problem

  3. upgradeable modules to replace run-time modules

  4. command line --patch-module to add individual classes

Run-Time Images

  • new JDK/JRE layout

  • internal JARs are gone (e.g. rt.jar, tools.jar)

  • JARs are now JMODs

  • application class loader is no URLClassLoader

  • new URL schema for run-time image content

What to look for?

  • does the code rummage around
    in the JDK / JRE folder?

  • search for casts to URLClassLoader

  • are URLs to JDK classes / resources handcrafted?

Obsolete

  • Endorsed Standards Override Mechanism

  • Extension Mechanism

  • Boot Class Path Override

But wait, there’s more!

  • yes, yes, there’s more

  • you should really check JEP 261

  • and JEP 223 (new version strings)

    • goodbye 1.9.0_31

    • hello 9.1.4

General Advice I

The most relevant for most applications:

  • internal APIs

  • split packages

General Advice II

  • 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

  • get an EA-build and try it!

About

Find Me

Want More?

2-day course with 42talents
Zürich, 20.04. - 21.04.2017
~> register

or buy my book ~> subscribe

What About OSGi?

Brief comparison
of Jigsaw and OSGi

Jigsaw vs. OSGi

OSGi Bundles:

  • are JARs with a descriptor (MANIFEST.MF)

  • have a name

  • import packages or bundles

  • define public API by exporting packages

Jigsaw vs. OSGi

JigsawOSGi

Versioning

not at all

packages and modules

Run-time Behavior

mostly static

dynamic

Services

declarative via ServiceLoader

declarative or programmatically;
more flexible

Class Loaders

operates below

one per bundle

Image Credits

Introduction

Project Jigsaw

Java Module System

Migration

Compatibility

Rest