To JAR Hell And Back

A Live Migration To Java 11

Public Service Announcement

Java Platform 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

Some consequences:

  • NoClassDefFoundError

  • no encapsulation across JARs

  • version conflicts

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 Declaration

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

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

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.

Migration Challenges

What to look out for
when running on JDK 9

Break Stuff

Some internal changes break existing code!

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

Internal APIs

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

What to look for?

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

What else to look for?

  • look for reflection, especially

    • Class::forName

    • AccessibleObject::setAccessible

  • recursively check your dependencies!

What to do?

  1. fix your code

  2. contact library developers

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

  4. consider command line flags
    --add-exports, --add-opens, or
    --illegal-access

Java EE Modules

  • 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 are

  • deprecated for removal

  • not resolved by default

What to look for?

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

What to do?

Split Packages

  • 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

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?

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]

What to do?

Your artifacts:

  1. rename one of the packages

  2. merge package into the same artifact

  3. merge the artifacts

  4. place both artifacts on the class path

Otherwise:

  1. upgrade the JDK module with the artifact

  2. --patch-module with the artifact’s content

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
    (no way to append to its class path)

  • new URL schema for run-time image content

What to look for?

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

  • are URLs to JDK classes / resources handcrafted?

  • search for casts to URLClassLoader

Obsolete

  • Compact Profiles

  • Extension Mechanism

  • Endorsed Standards Override Mechanism

  • Boot Class Path Override

  • JRE version selection with -version:N

But wait, there’s more!

Yes, yes, there’s more:

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

Background:

And there are new version strings:

  • goodbye 1.9.0_31, hello 9.1.4
    (soon 18.3?)

General Advice I

The most relevant for most applications:

  • internal APIs

  • split packages

  • Java EE modules

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

  • download Java 9 and try it!

Migration Challenges

What to look out for
when running on JDK 10

Nothing much…​

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.

Migration Challenges

What to look out for
when running on JDK 11

Nothing much…​

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 EE Modules

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

General Advice

  • only rely on standardized behavior

  • heed deprecation warnings (jdeprscan)

  • keep dependencies and tools up to date

  • build on each release (including EA)

  • report problems

About Nicolai Parlog

37% off with
code fccparlog

tiny.cc/jms

Follow

Want More?

⇜ Get my book!

You can hire me:

  • training (Java 8-11, JUnit 5)

  • consulting (Java 8-11)

Image Credits