To JAR Hell And Back

A Life Migration To
The Java 9 Module System

Public Service Announcements

Java Module System

Delaying Java since 2008!

JAR Hell is bad, mkay?

JARs have:

  • no name the JVM cares about

  • no explicit dependencies

  • no well-defined API

  • no concept of versions

Modules

Modules are like JARs but have:

  • proper names

  • explicit dependencies

  • a well-defined API

  • no concept of versions

Important goals:

  • reliable configuration

  • strong encapsulation

Module Descriptor

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
}

Reliable Configuration

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

Strong Encapsulation

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

Platform Modules

platform modules

Other Features

  • 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

Migration To Java 9

Compiling and Running on Java 9

Of Modules And JARs

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

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.

Access to Internal APIs

Internal APIs are encapsulated by default.

What exactly is "internal"?

  • everything not exported by a module

  • includes most com.sun.* packages

  • includes all sun.* packages
    except parts of sun.misc

Unsafe and others are available
in jdk.unsupported for a while.

Breaking Into Modules

Compilation will fail for internal dependencies:

  • use --add-exports to export packages

In Java 9, run time access is allowed
but reflection will result in a warning.

  • use --add-exports to export packages

  • use --add-opens to open packages
    for deep reflection

  • use --illegal-access to configure behavior

Adding Java EE Modules

Java EE modules are not included by default:

java.activation, java.corba, java.transaction
java.xml.bind, java.xml.ws, java.xml.ws.annotation

Have to be added to compiler and JVM
with --add-modules.

Splitting Packages

No two modules can contain the same packages
(exported or not).

  • for named modules this is checked

  • for the unnamed module it is not

  • classes in the unnamed package are "invisible"

Splitting Packages

Two ways to mend the split:

  • for upgreadable modules, use the JAR instead
    of the module (--upgrade-module-path)

  • for individual classes, merge the JAR with
    the module (--patch-module)

Modularizing Your Project

Making Use Of The Module System

The Tale Of Two Paths

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!

From The Bottom Up

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

From The Top Down

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;
    }

Automatic Modules

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

  • gets a name based on the file name or a
    manifest entry AUTOMATIC-MODULE-NAME

  • can read all modules
    (including the Unnamed Module)

  • exports all packages

Automatic Module Example

  • put spark-core-2.6.0.jar on the module path

  • then this works:

    module monitor.rest {
    	requires spark.core;
    }

What Goes Where?

Class PathModule Path

Regular JAR

Unnamed Module

Automatic Module

Modular JAR

Unnamed Module

Named Module

Advanced JPMS features

Getting the most out of the module system

Services

The module system as a service registry:

module java.sql {
	uses java.sql.Driver
}

module mysql.driver {
	provides java.sql.Driver
		with com.mysql.MySQLDriver;
}

Loading Services

Code in java.sql can now do this:

List<Driver> drivers = new ArrayList<>();
ServiceLoader
	.load(Driver.class)
	.forEach(drivers::add);

Full Picture

If you want more details:

Java 9 Migration Guide
(tiny.cc/java-9-migration)

If you run into problems:

For the the full picture get my book:

The Java 9 Module System
(tiny.cc/j9ms)

About Nicolai Parlog

37% off with
code fccparlog

tiny.cc/j9ms

Want More?

⇜ Get my book!

You can hire me:

  • training (Java 8/9, JUnit 5)

  • consulting (Java 8/9)

Image Credits