The Java Module System
Nicolai Parlog
  • MEAP began March 2017
  • Publication in Spring 2019 (estimated)
  • ISBN 9781617294280
  • 400 pages (estimated)
  • printed in black & white

The best book about the Java Module System.

Ivan Milosavljevic, Senior Software Developer, Novomatic Lottery Solutions

Java's much-awaited "Project Jigsaw" is finally here! Java 9 will include a built-in modularity framework, and The Java Module System is your guide to discovering it. In this new book, you'll learn how the module system improves reliability and maintainability and can be used to reduce tight coupling of system components. You'll then discover how to build, compile, and run your own fully modular applications with best practices and expert techniques. Along the way, you'll also explore Java 9's compatibility challenges and how to migrate your application to the module system.

Table of Contents detailed table of contents

Part 1: Hello, Modules

1. First piece of the puzzle

1.1. What is modularity all about?

1.1.1. Visualizing software as graphs

1.1.2. The impact of design principles

1.1.3. What modularity is all about

1.2. Module erasure before Java 9

1.3. Complications before Java 9

1.3.1. Unexpressed dependencies between JARs

1.3.2. Shadowing of classes with the same name

1.3.3. Conflicts between different versions of the same project

1.3.4. Complex class loading

1.3.5. Weak encapsulation across JARs

1.3.6. Security checks have to be handcrafted

1.3.7. Poor startup performance

1.3.8. Rigid Java runtime

1.4. Bird’s-eye view on the module system

1.4.1. Everything is a module

1.4.2. Your first module

1.4.3. The module system in action

1.4.4. Your non-modular project will be fine—​mostly

1.5. Goals of the module system

1.5.1. Reliable configuration—​leaving no JAR behind

1.5.2. Strong encapsulation—​making module-internal code inaccessible

1.5.3. Automated security and improved maintainability

1.5.4. Improved startup performance

1.5.5. Scalable Java platform

1.5.6. Non-Goals

1.6. Skills, old and new

1.6.1. What you will learn

1.6.2. What you should know

1.7. Summary

2. Anatomy of a modular application

2.1. Introducing the ServiceMonitor

2.2. Modularizing the ServiceMonitor

2.2.1. Cutting the ServiceMonitor into modules

2.2.2. Laying files out in a directory structure

2.2.3. Declaring and describing modules

2.2.4. Writing code is much like before

2.2.5. Compiling and packaging modules

2.2.6. Running the ServiceMonitor

2.2.7. Extending a modular code base

2.3. Post mortem—​effects of the module system

2.3.1. What the module system does for us

2.3.2. What else the module system can do for us

2.4. Summary

3. Defining modules and their properties

3.1. Modules—​the building blocks of modular applications

3.1.1. Java modules (JMODs)--shipped with the JDK

3.1.2. Modular JARs—​home-grown modules

3.1.3. Module declarations—​defining a module’s properties

3.1.4. The many types of modules

3.2. Readability—​connecting the pieces

3.2.1. Achieving reliable configuration

3.2.2. Experimenting with unreliable configurations

3.3. Accessibility—​defining public APIs

3.3.1. Achieving strong encapsulation

3.3.2. Encapsulating transitive dependencies

3.3.3. Encapsulation skirmishes

3.4. The module path—​letting Java know about modules

3.4.1. Module resolution—​analysis and verification of an application’s structure

3.4.2. Module graph—​representation of an application’s structure

3.4.3. Adding modules to the graph

3.4.4. Adding edges to the graph

3.4.5. Accessibility is an ongoing effort

3.5. Summary

4. Building modules from source to JAR

4.1. Organizing your project in a directory structure

4.1.1. New proposal—​new convention?

4.1.2. Established directory structure

4.1.3. The place for module declarations

4.2. Compiling a single module

4.2.1. Compiling modular code

4.2.2. Modular or non-modular?

4.3. Compiling multiple modules

4.3.1. The naive approach

4.3.2. The module source path—​informing the compiler of the project structure

4.3.3. The asterisk as a token for the module name

4.3.4. Multiple module source path entries

4.3.5. Setting the initial module

4.3.6. Is it worth it?

4.4. Compiler options

4.5. Packaging a modular JAR

4.5.1. Quick recap of jar

4.5.2. Analyzing a JAR

4.5.3. Defining an entry point

4.5.4. Archiver options

4.6. Summary

5. Running and debugging modular applications

5.1. Launching the JVM with modules

5.1.1. Specifying the main class

5.1.2. Passing parameters to the application

5.2. Loading resources from modules

5.2.1. Resource loading before Java 9

5.2.2. Resource loading on Java 9 and later

5.2.3. Loading package resources across module boundaries

5.3. Debugging modules and modular applications

5.3.1. Analyzing individual modules

5.3.2. Validating sets of modules

5.3.3. Validating a module graph

5.3.4. Listing observable modules and dependencies

5.3.5. Excluding modules during resolution

5.3.6. Observing the module system with log messages

5.4. Java Virtual Machine options

5.5. Summary

Part 2: Adapting Real World Applications

6. Compatibility challenges when moving to Java 9

6.1. Working with JEE modules

6.1.1. Why are the JEE modules special?

6.1.2. Manually resolving JEE modules

6.1.3. Dropping in third-party implementations of JEE modules

6.2. Casting to URLClassLoader

6.2.1. Application class loaders, then and now

6.2.2. Getting by without URLClassLoader

6.2.3. Finding troublesome casts

6.3. Updated runtime image directory layout

6.4. Selecting, replacing, and extending the platform

6.4.1. No more compact profiles

6.4.2. Extension mechanism was removed

6.4.3. Endorsed standards override mechanism was removed

6.4.4. Some boot class path options were removed

6.4.5. No compilation for Java 5

6.4.6. JRE version selection was removed

6.5. The little things that make big things fail

6.5.1. New Version Strings

6.5.2. Tool exodus

6.5.3. The littlest things

6.5.4. New deprecations in Java 9, 10, and 11

6.6. Summary

7. Recurring challenges when running on Java 9

7.1. Encapsulation of internal APIs

7.1.1. Internal APIs under the microscope

7.1.2. Analyzing dependencies with JDeps

7.1.3. Compiling against internal APIs

7.1.4. Executing against internal APIs

7.1.5. Compiler and JVM options for accessing internal APIs

7.2. Mending split packages

7.2.1. What is the problem with split packages?

7.2.2. The effects of split packages

7.2.3. Many ways to handle split packages

7.2.4. Patching modules—​last resort to handling split packages

7.2.5. Finding split packages with JDeps

7.2.6. A note on dependency version conflicts

7.3. Summary

8. Incremental modularization of existing projects

8.1. Why incremental modularization is even an option

8.1.1. If every JAR needed to be modular?

8.1.2. Mixing and matching plain JARs with modules

8.1.3. Technical underpinnings of incremental modularization

8.2. The unnamed module—​a.k.a the class path

8.2.1. The chaos of the class path—​captured by the unnamed module

8.2.2. Module resolution for the unnamed module

8.2.3. Depending on the unnamed module

8.3. Automatic modules—​plain JARs on the module path

8.3.1. Automatic module names—​small detail, big impact

8.3.2. Module resolution for automatic modules

8.3.3. All-in on automatic modules?

8.3.4. Depending on automatic modules

8.4. Summary

9. Migration and modularization strategies

9.1. Migration strategies

9.1.1. Preparatory updates

9.1.2. Estimating the effort

9.1.3. Continuously build on Java 9

9.1.4. Thoughts on command line options

9.2. Modularization strategies

9.2.1. Bottom-up modularization—​if all project dependencies are modular

9.2.2. Top-down modularization—​if your application can’t wait for its dependencies

9.2.3. Inside-out modularization—​if your project is in the middle of the stack

9.2.4. Applying these strategies within your project

9.3. Making JARs modular

9.3.1. Open modules as an intermediate step

9.3.2. Generating module declarations with JDeps

9.3.3. Hacking third-party JARs

9.3.4. Publishing modular JARs for Java 8 and older

9.4. Summary

Part 3: Advanced Module System Features

10. Using services to decouple modules

10.1. Services in the Java Platform Module System

10.1.1. Using, providing, and consuming services

10.1.2. Module resolution for services

10.2. Designing services well

10.2.1. Types that can be services

10.2.2. Using factories as services

10.2.3. Isolating consumers from global state

10.2.4. Organizing services, consumers, and providers into modules

10.2.5. Using services to break cyclic dependencies

10.2.6. Declaring services across different Java versions

10.3. Accessing services with the ServiceLoader API

10.3.1. Loading and accessing services

10.3.2. Idiosyncrasies of loading services

10.4. Summary

11. Refining dependencies and APIs

11.1. Implied readability—​passing dependencies on

11.1.1. Exposing a module’s dependencies

11.1.2. The transitive modifier—​implying readability on a dependency

11.1.3. When to use implied readability

11.1.4. When to rely on implied readability

11.1.5. Refactoring modules with implied readability

11.2. Optional dependencies

11.2.1. The conundrum of reliable configuration

11.2.2. The static modifier—​marking dependencies as optional

11.2.3. Module resolution of optional dependencies

11.2.4. Coding against optional dependencies

11.3. Qualified exports—​limiting accessibility to specific modules

11.3.1. Exposing internal APIs

11.3.2. Exporting packages to modules

11.3.3. When to use qualified exports

11.3.4. Exporting packages on the command line

11.4. Summary

12. Reflection in a modular world

12.1. Why exports directives are no good fit for reflection

12.1.1. Breaking into non-modular code

12.1.2. Forcing the publication of internal types

12.1.3. Qualified exports create coupling to specific modules

12.1.4. No support for deep reflection

12.2. Open packages and modules—​designed for the reflection use case

12.2.1. Opening packages to run-time access

12.2.2. Opening packages for specific modules

12.2.3. Exporting versus opening packages

12.2.4. Opening modules—​reflection closeout

12.3. Reflecting over modules

12.3.1. Updating reflecting code for modules (or not)

12.3.2. Using variable handles instead of reflection

12.3.3. Analyzing module properties with reflection

12.3.4. Modifying module properties with reflection

12.3.5. Forwarding open packages

12.4. Dynamically creating module graphs with layers

12.4.1. What are layers?

12.4.2. Analyzing layers

12.4.3. Creating module layers

12.5. Summary

13. Module versions—​what’s possible and what’s not

13.1. The lack of version support by the JPMS

13.1.1. No support for multiple versions

13.1.2. No support for version selection

13.1.3. What the future may bring

13.2. Recording version information

13.2.1. Recording versions while building modules

13.2.2. Accessing module versions

13.3. Running multiple versions of a module in separate layers

13.3.1. Why you need a starter to spin up additional layers

13.3.2. Spinning up layers for your application, Apache Twill, and Cassandra Java Driver

13.4. Summary

14. Customizing runtime images with jlink

14.1. Creating custom runtime images

14.1.2. Image content and structure

14.1.3. Including services in runtime images

14.2. Creating self-contained application images

14.2.1. Including application modules in images

14.2.2. Generating a native launcher for your application

14.2.3. Security, performance, and stability

14.3. Generating images across operating systems

14.4.2. Reducing image size

14.4.3. Improving runtime performance

14.6. Summary

15. Putting the pieces together

15.1. Adding bells and whistles to ServiceMonitor

15.1.1. Diversified dependencies

15.1.2. Reduced visibility

15.1.3. Decoupled with services

15.1.4. Loads code at run time with layers

15.1.5. Handles dependencies on plain JARs

15.2. Tips for a modular application

15.2.1. Modular or not?

15.2.2. The ideal module

15.2.3. Take care of your module declarations

15.2.4. Breaking code by editing module declarations

15.3. Technology landscape

15.3.1. Java 9 beyond the module system

15.3.2. Maven, Gradle, and other build tools

15.3.3. OSGi

15.3.4. Microservices

15.4. Thoughts on a modular ecosystem


Appendix A: Class path recap

A.1. Using the class path to load application JARs

A.2. The class path since Java 9

A.3. Summary

Appendix B: High-level introduction to the reflection API

B.1. Fundamental types and methods

B.2. Breaking into APIs with setAccessible

B.3. Annotations mark code for reflection

B.4. Summary

Appendix C: Observing the JVM with unified logging

C.1. What is unified logging?

C.2. Defining which messages should be shown

C.3. Defining where messages should go

C.4. Defining what messages should say

C.5. Configuring the entire logging pipeline

C.6. Summary

Appendix D: Analyzing a project’s dependencies with JDeps

D.1. Getting to know JDeps

D.2. Including dependencies in the analysis

D.3. Configuring JDeps' output

D.4. Drilling deeper into your project’s dependencies

D.5. JDeps understands modules

D.6. Summary

Appendix E: Targeting multiple Java versions with multi-release JARs

E.1. Creating a multi-release JAR

E.2. Internal workings of multi-release JARs

E.3. Usage recommendations

E.3.1. Organizing the source code

E.3.2. Organizing the byte code

E.3.3. When to use multi-release JARs

E.4. Summary

About the Technology

Experienced developers understand the benefits of strong encapsulation; when two components can interact only through the interfaces you define, you reduce the chance that you'll introduce hidden dependencies and unwanted tight coupling into your code. The Java Module System, aka "Project Jigsaw", gives Java developers the ability to define and enforce modularity without an outside framework. A modular application allows a team to more-safely approach large projects and produce cleaner code with superior run-time stability. The benefits don't just stop there however, the shift to modules also promises reliable configuration - allowing individual modules to declare their dependencies on other modules, along with allowing both the developers and JVM the same vision of the running application; changing how you develop with Java for the better!

What's inside

  • The anatomy of a modular Java app
  • Building modules from source to JAR
  • Compiling both single and multiple modules
  • Debugging your finished applications

About the reader

If you are a developer who has experience working with Java, then this book is perfect for you.

About the author

Nicolai Parlog is a thirty year old boy, as the narrator would put it, who has found his passion in software development. He constantly reads, thinks, and writes about it, and codes for a living as well as for fun. He is the editor of SitePoint's Java channel, blogs about software development on, and is a long-tail contributor to several open source projects. The best way to get in touch is via Twitter.

Manning Early Access Program (MEAP) Read chapters as they are written, get the finished eBook as soon as it’s ready, and receive the pBook long before it's in bookstores.

placing your order...

Don't refresh or navigate away from the page.

FREE domestic shipping on three or more pBooks

Really clean, clear code. If only everybody would write like that...

Mark Dechamps, Java/Spring/JPA developer, Agfa HealthCare

A very pragmatic book that doesn't waste time in useless information

Guido Pio Mariotti, Research Engineer, Linkoping University

Gripping and interesting.

Maria Gemini, Student