Project Jigsaw: Under The Hood by Mark Reinhold

By: Devoxx

14   0   1134

Uploaded on 11/11/2016

The goals of Project Jigsaw are reliable dependencies and strong encapsulation, but what does that mean precisely? This session explains the process of module resolution in JDK 9 and shows how it avoids errors relative to the class path in JDK 8. It also explores the double-edged sword of modular accessibility, where the ability to hide a module’s internals must be balanced against the needs of frameworks that perform reflection. Prepare for a deep dive into module graphs, class loaders, unnamed modules, and the once and future king, setAccessible.

Mark Reinhold is Chief Architect of the Java Platform Group at Oracle. His past contributions to the platform include character-stream readers and writers, reference objects, shutdown hooks, the NIO high-performance I/O APIs, library generification, and service loaders. Mark was the lead engineer for the JDK 1.2 and 5.0 releases, the JCP specification lead for Java SE 6, and both the project and specification lead for JDK 7 (Java SE 7) and JDK 8 (Java SE 8). He currently leads the JDK 9 and Jigsaw projects in the OpenJDK Community, where he also serves on the Governing Board. Mark holds a Ph.D. in computer science from the Massachusetts Institute of Technology.

[VNH-0402]

Comments (7):

By codetojoy    2017-09-20

re: feels like generics. Quite right. From Spec Lead, Mark Reinhold: https://youtu.be/fxB9cVNcyZo?t=41m50s

Original Thread

By anonymous    2017-09-20

For the sake of argument, let's assert that Java 8 (and earlier) already has a "form" of modules (jars) and module system (the classpath). But there are well-known problems with these.

By examining the problems, we can illustrate the motivation for Jigsaw. (The following assumes we are not using OSGi, JBoss Modules, etc, which certainly offer solutions.)

Problem 1: public is too public

Consider the following classes (assume both are public):

com.acme.foo.db.api.UserDao
com.acme.foo.db.impl.UserDaoImpl

At Foo.com, we might decide that our team should use UserDao and not use UserDaoImpl directly. However, there is no way to enforce that on the classpath.

In Jigsaw, a module contains a module-info.java file which allows us to explicitly state what is public to other modules. That is, public has nuance. For example:

// com.acme.foo.db.api.UserDao is accessible, but
// com.acme.foo.db.impl.UserDaoImpl is not 
module com.acme.foo.db {
    exports com.acme.foo.db.api;
}

Problem 2: reflection is unbridled

Given the classes in #1, someone could still do this in Java 8:

Class c = Class.forName("com.acme.foo.db.impl.UserDaoImpl");
Object obj = c.getConstructor().newInstance();

That is to say: reflection is powerful and essential, but if unchecked, it can be used to reach into the internals of a module in undesirable ways. Mark Reinhold has a rather alarming example. (The SO post is here.)

In Jigsaw, strong encapsulation offers the ability to deny access to a class, including reflection. (This may depend on command-line settings, pending the revised tech spec for JDK 9.) Note that because Jigsaw is used for the JDK itself, Oracle claims that this will allow the Java team to innovate the platform internals more quickly.

Problem 3: the classpath erases architectural relationships

A team typically has a mental model about the relationships between jars. For example, foo-app.jar may use foo-services.jar which uses foo-db.jar. We might assert that classes in foo-app.jar should not bypass "the service layer" and use foo-db.jar directly. However, there is no way to enforce that via the classpath. Mark Reinhold mentions this here.

By comparison, Jigsaw offers an explicit, reliable accessibility model for modules.

Problem 4: monolithic run-time

The Java runtime is in the monolithic rt.jar. On my machine, it is 60+ MB with 20k classes! In an age of micro-services, IoT devices, etc, it is undesirable to have Corba, Swing, XML, and other libraries on disk if they aren't being used.

Jigsaw breaks up the JDK itself into many modules; e.g. java.sql contains the familiar SQL classes. There are several benefits to this, but a new one is the jlink tool. Assuming an app is completely modularized, jlink generates a distributable run-time image that is trimmed to contain only the modules specified (and their dependencies). Looking ahead, Oracle envisions a future where the JDK modules are compiled ahead-of-time into native code. Though jlink is optional, and AOT compilation is experimental, they are major indications of where Oracle is headed.

Problem 5: versioning

It is well-known that the classpath does not allow us to use multiple versions of the same jar: e.g. bar-lib-1.1.jar and bar-lib-2.2.jar.

Jigsaw does not address this problem; Mark Reinhold states the rationale here. The gist is that Maven, Gradle, and other tools represent a large ecosystem for dependency management, and another solution will be more harmful than beneficial.

It should be noted that other solutions (e.g. OSGi) do indeed address this problem (and others, aside from #4).

Bottom Line

That's some key points for Jigsaw, motivated by specific problems.

Note that explaining the controversy between Jigsaw, OSGi, JBoss Modules, etc is a separate discussion that belongs on another Stack Exchange site. There are many more differences between the solutions than described here. What's more, there was sufficient consensus to approve the Public Review Reconsideration Ballot for JSR 376.

Original Thread

By anonymous    2017-09-20

Your question and comment imply that the classpath is going away: it is not.

Before addressing automatic modules, there are other concepts to understand.

In Java 9, the classpath is still around, and familiar legacy jars (i.e. non-modularized jars) will still work. However, there is also a new modulepath which contains modular jars.

Modular jars contain a module-info.class which is very explicit about (a) the modules required as dependencies and (b) which packages are exported.

A crucial point is the interaction between the classpath and the modulepath:

  • Legacy jars on the classpath don't know anything about the modulepath, so they are granted access to all modules.
  • Because the classpath is generally chaotic, it is known as the unnamed module. Because there is no name, modular jars on the modulepath can't reference legacy jars in their module-info.java.

With this much information, it would be impossible to modularize a project in a piecemeal fashion: if an application jar requires a 3rd-party library, we would have to (a) wait for the library authors to modularize it or (b) attempt to modularize it ourselves. Both are non-starters.

Enter automatic modules. They are legacy (non-modular) jars which reside on the modulepath. They act as the bridge between the classpath and the modulepath because:

  • Automatic modules are named (the mechanism is a probably a separate question), and can be referenced by modules.
  • Automatic modules are granted read access to everything in the unnamed module (i.e. legacy jars on the classpath) and all modules on the modulepath (i.e. JDK modules).

This video and this video offer illustrations.

Original Thread

By anonymous    2017-09-20

[Edit: this answer was written prior to Mark's authoritative answer. I've revised mine to provide a simple example, available on GitHub.]

Per this video, class loading in Java 9 is unchanged.

As an example, let's say we have:

  • an example.jar that contains an image in the package net.codetojoy.example.resources
  • to beef up the jar, net.codetojoy.example.Composer is public (and exported, where applicable)
  • a simple App class that uses example.jar as a library and attempts to load the image from it

The relevant code in App:

static InputStream getResourceAsStream(String resource) 
    throws Exception {

    // Load net/codetojoy/example/resource/image.jpg
    // Assume net.codetojoy.example.Composer is public/exported
    // resource is 'resource/image.jpg'

    InputStream result = Composer.class.getResourceAsStream(resource);

    return result;
}   

Here are a few cases for example.jar in JDK 9:

Old-Fashioned, Non-Modular Jar

If example.jar is not a module, the code just works. Class loading is unchanged.

Modular Jar With Open Package

In this case, this is the module-info.java file:

module net.codetojoy.example {
    // export the Composer class
    exports net.codetojoy.example;

    // image is available
    opens net.codetojoy.example.resources;
}

In this case, the image can be loaded by the client, because the package is open.

Modular Jar Without Open Package

In this case, module-info.java is:

module net.codetojoy.example {
    // export the Composer class
    exports net.codetojoy.example;

    // package not opened: image not available
    // opens net.codetojoy.example.resources;
}

In this case, the image cannot be loaded, because of strong encapsulation: the module is protecting the image by not opening the package.

Full source here on GitHub.

Original Thread

Popular Videos 98

Submit Your Video

If you have some great dev videos to share, please fill out this form.