Tuesday, 19 September 2017

Java 9, Jigsaw, JPMS, and Modules: A Personal Exploration

Java 9 delayed so many times because of Project Jigsaw, and you may be heard a lot of thing about modules, modularity, and other stuff, so, what it’s all about? What the heck is modularization and what do we mean by modularized platform? Java Platform Module System (JPMS)? Is it going to be a revolution in Java ecosystem?

This post is my exploration of the most important thing that happened to the JDK, the Module System. I will explain what modularization is, why you need it, and how you can create your modularized project.

Java 9, Oracle Java JPMS, Oracle Java Certifications

What & Why:


Maintainability is one of the most significant concerns in software design and evolution. We want a code base that is loosely coupled, highly cohesive, extremely readable and can be understandable in one eye shot. We design our classes and organize them in packages. So far so good, but when we have hundreds of packages the dependencies between them is not visible in one eye shot. Therefore, we need something more than packages to organize our code base and makes it more maintainable.
Another problem is java classpath and how it runs our codes. All jar classes and libraries are flattened into the classpath. When these jar files have multiple version of a class on the runtime, Java ClassLoader can load only one version of that class, in this way, there is ambiguity about how your program is going to work, and ambiguity is a bad thing. This issue is so frequent that it has its name called “JAR Hell.”

Another problem with classpath is that it doesn’t follow the “Fail First” principle. You may have missing classes that exist in the classpath, but it doesn’t exist in the production environment. Until the JavaClassDefError exception at runtime, you can’t be sure what is missing. Finally, The big issue with classpath is encapsulation. Every class on the classpath access to each other and this is an encapsulation violation. We want to hide our internal APIs, and that’s why we need another level of encapsulation (“Strong Encapsulation”) and control the access to our classes in our packages.

Modules are going to fix these issues. What is a module? A module has a name, it groups related code and is self-contained. A module describes explicitly what it needs from other modules and which part of him is visible to other modules. In this manner, dependencies between modules are crystal clear. We have Strong Encapsulation which means we can hide our internal APIs, and finally, we now follow the “Fail First” principle therefore when there is a missing module or confliction you will get an error.

Java 9, Oracle Java JPMS, Oracle Java Certifications

Modularizing JDK allows JDK developers to manage the huge complexity of it. When you write a tiny and straightforward application that doesn’t use RMI, CORBA, JavaEE, and other stuff, why you need a full, huge, and heavy Java Runtime Environment? Isn’t that wiser to have your Runtime Image that only contains the modules you need? Now with a modularized platform, it’s possible.

This is how JDK now looks like. On the bottom, we have “java.base” module that every other module implicitly or explicitly depends on. As you can see, this dependency graph is a DAG which means no circular dependency allowed.

Java 9, Oracle Java JPMS, Oracle Java Certifications

The picture below shows essentially what module is. Each module has a module descriptor called “module-info.java.”

Java 9, Oracle Java JPMS, Oracle Java Certifications

In module-info.java file you describe the name of your module, what it requires to work and which packages are visible outside this module. For example, you can see what packages java.sql exports (make visible) and which modules it requires.

Java 9, Oracle Java JPMS, Oracle Java Certifications

So in the simplest form, the module-info.java looks like the image below:

Java 9, Oracle Java JPMS, Oracle Java Certifications

In the next section, I will show how you can work with these modules and create your modules.


How:


First of all, you need to download and install Java 9. You can find it here.

Java Version

$ java -version
java version "9"
Java(TM) SE Runtime Environment (build 9+181)
Java HotSpot(TM) 64-Bit Server VM (build 9+181, mixed mode

Let’s build a project in IntelliJ IDEA:

Java 9, Oracle Java JPMS, Oracle Java Certifications

The picture below shows how to create a module:

Java 9, Oracle Java JPMS, Oracle Java Certifications

After creating a module, you need to create a module-info.java file inside the src:

Java 9, Oracle Java JPMS, Oracle Java Certifications

I’ve built a project that has two module: “com.mhrimaz.gui” and “com.mhrimaz.logic.” You can see the structure of the project in the image:

Java 9, Oracle Java JPMS, Oracle Java Certifications

In com.mhrimaz.logic module I have two classes called “InternalGreeting” and “Greeting.”

InternalGreeting.java

package com.mhrimaz.logic.internals;

public class InternalGreeting {
    public static String sayHello(String name){
        return "Hello, This Greeting is internal dear "+ name;
    }
}

Greeting.java

package com.mhrimaz.logic;

public class Greeting {
    public static String sayHello(String name){
        return "Hello, " + name;
    }
}

The module-info.java of com.mhrimaz.logic, is the following:

module com.mhrimaz.logic {
    exports com.mhrimaz.logic;
}

This means the package com.mhrimaz.logic (it’s a package name not a module name don’t confuse) is visible outside this module but the package com.mhrimaz.logic.internals is not visible.

The MianApplication file is a simple JavaFX program:

package com.mhrimaz.gui;

import com.mhrimaz.logic.Greeting;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class MainApplication extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        Label label = new Label(Greeting.sayHello("Hossein"));
        StackPane pane = new StackPane();
        pane.getChildren().add(label);

        Scene scene = new Scene(pane);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

It seems that this package doesn’t need to export anything it only requires javafx.base and javafx.controls and in order to use Greeting class, we also should require com.mhrimaz.logic. The module-info of the com.mhrimaz.gui module looks like this:

module com.mhrimaz.gui {
    requires javafx.base;
    requires javafx.controls;
    requires com.mhrimaz.logic;
}

When we run our application we will get an exception:

Caused by: java.lang.IllegalAccessException: class com.sun.javafx.application.LauncherImpl 
(in module javafx.graphics) cannot access class com.mhrimaz.gui.MainApplication 
(in module com.mhrimaz.gui) because module com.mhrimaz.gui does not export com.mhrimaz.gui to module javafx.graphics

so obviously it tells that we need to export com.mhrimaz.gui package, This means javafx.graphics uses MainApplication to pass the Stage to it and you need to export your package to the javafx.graphics (Note: You can only export a package to specific module or export it to all modules)
So now the module-info.java looks like this:

module com.mhrimaz.gui {
    requires javafx.base;
    requires javafx.controls;
    requires com.mhrimaz.logic;
    exports com.mhrimaz.gui to javafx.graphics;
}

And the result seems like a bug in JavaFX implementation in Java 9 but this is our result:

Java 9, Oracle Java JPMS, Oracle Java Certifications

The story doesn’t end here, There is a whole lot of details about modules, dependencies between them that you can read them in Java 9 Revealed or Java 9 Modularity book.

Related Posts