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

Migration Challenges

What to look out for
when running on JDK 11

Break Stuff

Some internal changes break existing code!

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

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.

Challenges

  • internal APIs

  • Java EE modules

  • split packages

  • runtime images

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 were

  • deprecated for removal in ⑨

  • removed in ⑪

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:

Background:

And there are new version strings:

  • goodbye 1.9.0_31, hello 9.0.1

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 11 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-12, JUnit 5)

  • consulting (Java 8-12)

Image Credits