Java Modules

(the code for this is available at http://www.selikoff.net/ocp11-1

(do a more in-depth review of this chapter, if there’s enough time)

Introducing Modules

The Java Platform Module System (JPMS) was introduced in Java 9 to group code at a higher level. The main purpose of a module is to provide groups of related packages to offer a particular set of functionality to developers.
It’s like a JAR file except a developer chooses which packages are accessible outside the module.

Exploring a Module

A module is a group of one or more packages plus a special file called module-info.java.

(insert image of module)

Each block is a module. The arrows between them represent dependencies where one module relies on code in another.

(insert image of module)

Inside them, there’re three packages with two classes each. There’s also a module-info.java. This class is required to be inside all modules.

Benefits of Modules

Better Access Control

Aside from the existing access control (private, public…), they allow us to restrict access to just some packages, or implement complex access logic.
They act as a fifth level of access control. They can expose packages within the modular JAR to specific other packages (internal packages).

Clearer Dependency Management

It’s common for libraries to depend on other libraries. Usually you need to find this by reading documentation or project files.
In a fully modular environment, each project specifies its dependencies in a module-info.java file. If a dependency is missing, Java complains when launching the program.

Custom Java Builds

The JDK is way to big. The module system allows developers to specify what modules they actually need. This makes possible to create a smaller runtime image that’s customized to what the app needs and nothing more. Users can run that image without having Java installed at all.

The tool jlink is used to create this runtime image.

Improved Performance

Since Java now knows which modules are required, it only needs to look at those at class loading time. This improves startup time for big programs and requires less memory to run.

Unique Package Enforcement

It prevents the scenario of the same package being in two JARs. A package is only allowed to be supplied by one module.

Creating and Running a Modular Program

(insert image of module)

Creating the Files

Create the following classes and packages.

Task.java

package zoo.animal.feeding;

public class Task {
	public static void main(String[]args) {
		sout("All fed!");
	}
}

module-info.java

module zoo.animal.feeding {
}

Key differences between module-info.java and a regular Java class

  • This file must be in the root directory of your module. Regular Java classes should be in packages.
  • This file must use the keyword module instead of class.
  • Its name follows the naming rules for package names. It often includes periods in its name.

The file may be empty, but it won’t create a .class.

Directory structure

- mods
- feeding
	- module-info.java
	- zoo
		- animal
			- feeding
				- Task.java

The directory called mods (short for module) is for storing the module artifacts.

Compiling Our First Module

Before we can run modular code, we need to compile it.

javac --module-path mods
	-d feeding
	feeding/zoo/animal/feeding/*.java
	feeding/module-info.java

-d specifies the directory to place the class files in.
The syntax --module-path and -p are equivalent.

Running Our First Module

java --module-path feeding
	--module zoo.animal.feeding/zoo.animal.feeding.Task

The short version for --module is -m.

Packaging Out First Module

A module isn’t much use if we can run it only in the folder it was cerated in.

jar -cvf mods/zoo.animal.feeding.jar -C feeding/ .

Versioning

It’s possible to version your module using --module-version,

Updating Our Example For Multiple Modules

Updating the Feeding Module

We need to declare the intent of our modules calling each other inside module-info.
The exports keyword is used to indicate that a module intends for those packages to be used by Java code outside the module. Without it, a module is only available to be run from the command line on its own.

module zoo.animal.feeding {
	exports zoo.animal.feeding;
}

Recompiling and repackaging the module will update the module-info inside our zoo.animals.feeding.jar file.

javac --module-path mods
	-d feeding
	feeding/zoo/animal/feeding/*.java
	feeding/module-info.java

jar -cvf mods/zoo.animal.feeding.jar -C feeding/ .

Creating a Care Module

Let’s create the zoo.animal.care module. We’re going to have two packages. The zoo.animal.care.medical package will have the classes and methods that are intended for use by other modules. The zoo.animal.care.details package is only going ot be used by this module. It won’t be exported from the module.

(insert image of module)

package zoo.animal.care.details;

import zoo.animal.feeding.*;

public class HippoBirthday {
	private Task task;
}
package zoo.animal.care.medical;

public class Diet { }

This time the module-info.java specifies three things.

module zoo.animal.care {
	exports zoo.animal.care.medical;
	requires zoo.animal.feeding;
}
  • It specifies the name of the module.
  • It lists the package we are exporting so it can be used by other modules.
  • It specifies that a module is needed.

We now compile and package the module

javac -p mods
	-d care
	care/zoo/animal/care/details/*.java
	care/zoo/animal/care/medical/*.java
	care/module-info.java

Note that when compiling a module, order matters! module-info goes last.

Now that we have compiled code, it’s time to create a modular JAR.

jar -cvf mods/zoo.animal.care.jar -C care/ .

Creating the Talks Module

So far we’ve used only one exports and requires in a module. Now we’ll handle exporting multiple packages or requiring multiple modules.

module zoo.animals.talks {
	// allow other modules to reference all three packages
	exports zoo.animal.talks.content;
	exports zoo.animal.talks.media;
	exports zoo.aniaml.talks.schedule;

	// specifies two modules that this module depends on
	requires zoo.animal.feeding;
	requires zoo.animal.care;
}

Then we have the six classes:

package zoo.animal.talks.content;

public class ElephantScript {}
package zoo.animal.talks.content;

public class SeaLionScript {}
package zoo.animal.talks.media;

public class Announcement {
	public static void main(String[] args) {
		sout("we will be having talks");
	}
}
package zoo.animal.talks.media;

public class Signage {}
package zoo.animal.talks.schedule;

public class Weekday {}

We compile and build the module

javac -p mods
	-d talks
	talks/zoo/animal/talks/content/*.java
	talks/zoo/animal/talks/media/*.java
	talks/zoo/animal/talks/schedule/*.java
	talks/module-info.java

jar -cvf mods/zoo.animal.talks.jar -C talks/

Creating the Staff Module

This is the module-info

module zoo.staff {
	requires zoo.animal.feeding;
	requires zoo.animal.care;
	requires zoo.animal.talks;
}

We have a single class in this module

package zoo.staff;

public class Jobs {}

Compile & build

javac -p mods
	-d staff
	staff/zoo/staff/*.java
	staff/module-info.java

jar -cvf mods/zoo.staff.jar -C staff/

Diving into the module-info File

exports and requires are “keywords”, meaning that they’re only keywords inside a module-info.java file. In other files, like classes or interfaces, they’re free to be used. This kind of special “keyword” are called directives.

exports

exports packageName exports a package to other modules.
It’s also possible to export a package to a specific module.

module zoo.animal.talks {
	exports zoo.animal.talks.content to zoo.animal.staff;
	exports zoo.animal.talks.media;
	exports zoo.animal.talks.schedule;

	requires zoo.animal.feeding;
	requires zoo.animal.care;
}

From zoo.animal.staff module nothing has changed. However, no other modules would be allowed to access that package.

Exported Types

Exporting a package means, that all public classes, interfaces and enums are exported. Any public and protected fields and methods in those files are visible.

requires transitive

(check again: book 1. Page 528)

requires moduleName specifies that the current module depends on moduleName.
There’s also requires transitive moduleName, which means any module that requires this module will also depend on moduleName.

module zoo.animal.care {
	exports zoo.animal.care.medical;
	requires transitive zoo.animal.feeding;
}

Effects of requires transitive

(check again: book 1. Page 530)

Duplicate requires Statements

(!) One place the exam might try to trick you is mixing requires and requires transitive together. (!)

module bad.module {
	requires zoo.animal.talks;
	requires transitive zoo.animal.talks;
}

Thsi doesn’t compile, because Java doesn’t allow you to repeat the same module in a requires clause. It’s redundant. Keep in mind that requires transitive is like requires with some extra behaviour.

provides, uses, and opens

  • provides - specifies that a class provides an implementation of a service. It’s kind of a service as a fany interface. To use it you supply the API and class name that implements it.
provides zoo.staff.ZooApi with zoo.staff.ZooImpl
  • uses - specifies that a module is relying on a service. To code it you supply the API you want to call.
uses zoo.staff.ZooApi
  • opens - It specifies explicitly that developers are allowed to use reflection.
opens zoo.animal.talks.schedule;
opens zoo.animal.taslks.media to zoo.staff;

The first example allows any module using this one to use reflection.
The second one, gives that privilege only to the zoo.staff package.

Discovering Modules

Since Java 9, the classes built into the JDK were modularized as well.

The java Command

It has three module-related options.

Describing a Module

Suppose you’re given the zoo.animal.feeding module JAR file and want to know about its module structure.
You could unjar it and open the module-info. This would show you that the moduel exports one package and doesn’t require any modules.

module zoo.animal.feeding {
	exports zoo.animal.feeding;
}

There’s an easier way. The java command now has an option to describe a module.

This 2 commands are equivalent

java -p mods
    -d zoo.animal.feeding
java -p mods
	--describe-module zoo.animal.feeding

Each prints information about the module such as

zoo.animal.feeding file:///aboslutePath/mods/zoo.animal.feeding.jar
exports zoo.animal.feeding
requires java.base mandated

The first line is the module we asked about.
The second one, starts information about the module.
The third one, we see mandated as keyword. The java.base module is special. It’s automatically added as a dependency to all modules.

Listing Available Modules

List all modules that are available.

java --list-modules

the output are 70 lines that looked like this

java.base@11.0.2
java.compiler@11.0.2
java.datatransfer@11.0.2

This is a listing of all the modules that come with Java and their version numbers. Here 11.0.2 is the Java version.

This command may be used with custom code.

java -p mods --list-modules

Showing Module Resolution

In case listing modules didn’t give you enough output, you can also use --show-module-resolution. You can think of it as a way of debugging modules. It spits out a lot of output when the program starts up.

java --show-module-resolution
	-p feeding
	-m zoo.animal.feeding/zoo.animal.feeding.Task

The jar Command

It can describe a module, just like the java command.

Both of this commands are equivalent.

jar -f mods/zoo.animal.feeding.jar -d
jar --file mods/zoo.animal.feeding.jar --describe-module

The jar version includes the module-info in the filename.

You just need to know that both commands can describe a module.

The jdeps Command

It gives you information about dependencies within a module. Unlike describing a module, it looks at the code in addition to the module-info file. This tells you what dependencies are actually used rather than simply declared.

Both of these commands give the same output.

jdeps -s mods/zoo.animal.feeding.jar
jdeps -summary mods/zoo.animal.feeding.jar

(review this again more in depth. book 1 page 536)

The jmod Command

JMOD files are recommended only when you have native libraries or something can’t go inside a JAR file.

jmod is only for working with the JMOD files.

The following table are JMOD’s modes or syntax

operation description
create creates a JMOD file
extract extracts all files frmo the JMOD. Works like unzipping.
describe prints the module details such as requires
list lists all files in the JMOD file
hash shows a long string that goes with the file

Reviewing Command-Line Options

This table shows the command lines you should expect to encounter on the exam.

Description Syntax
Compile nonmodular code javac -cp classpath -d directory classesToCompile
  javac –class-path classpath -d directory classesToCompile
  javac -classpath classpath -d directory classesToCompile
Run nonmodular code java -cp classpath package.className
  java –classpath classpath package.className
  java –class-path classpath package.className
Compile a module javac -p moduleFOlderName -d directory classesToCompileIncludingModuleInfo
  javac –module-path moduleFOlderName -d directory classesToCompileIncludingModuleInfo
Run a module java -p moduleFolderName -m moduleName/package.className
  java –module-path moduleFolderName –module moduleName/package.className
Describe a module java -p moduleFolderName -d moduleName
  java –module-path moduleFolderName –describe-module moduleName
  jar –file jarName –describe-module
  jar -f jarName -d
List available modules java –module-path moduleFolderName –list-modules
  java -p moduleFolderName –list-modules
  java –list-modules
View dependencies jdeps -summary –module-path moduleFolderName jarName
  jdeps -s –module-path moduleFolderName jarName
Show module resolution java –show-module-resolution -p moduleFolderName -d moduelName
  java –show-module-resolution –module-path moduleFolderName –describe-module moduleName

javac modifiers

|Modifier|Description| |:—:|:—:| |-cp classpath|location of JARs in a nonmodular program| |-classpath classpath|| |–class-path classpath|| |-d dir|directory to place generated class files| |-p path|location of JARs in a modular program| |–module-path path||

java modifiers

|Modifier|Description| |:—:|:—:| |-p path|location of JARs in a modular program| |–module-path path|| |-m name|module name to run| |–module name|| |-d|describes the details of a module| |–describe-module|| |–list-modules|list observable modules without running a program| |–show-module-resolution|shows modules when running a program|

jar modifiers

|Modifier|Description| |:—:|:—:| |-c| craete a new JAR file| |–create|| |-v|prints details when working with JAR files| |–verbose|| |-f|JAR filename| |–file|| |-C|Directory containing giles to be used to create the JAR| |-d|Describes the details of a module| |–describe-module||

jdeps modifiers

|Modifier|Description| |:—:|:—:| |–module-path path|Location of JARs in a modular program| |-s|summarizes output| |-summary||

Summary

The Java Platform Module System organizes code at a higher level than packages. Each module contains one or more packages and a module-info file. Advantages of the JPMS include better access control, clearer dependency management, custom runtime images, improved performance, and unique package enforcement.

The process of compiling and running modules uses the –module-path, also known as -p. Running a module uses the –module option, also known as -m. The class to run is specified in the format moduleName/className.

The module-info file supports a number of keywords. The exports keyword specifies that a package should be accessible outside the module. It can optionally restrict that export to a specific package. The requires keyword is used when a module depends on code in another module. Additionally, requires transitive can be used when all modules that require one module should always require another. The provides and uses keywords are used when sharing and consuming an API. Finally, the opens keyword is used for allowing access via reflection.

Both the java and jar commands can be used to describe the contents of a module. The java command can additionally list available modules and show module resolution. The jdeps command prints information about packages used in addition to module-level information. Finally, the jmod command is used when dealing with files that don’t meet the requirements for a JAR.

Exam Essentials

Identify benefits of the Java Platform Module System. Be able to identify benefits of the JPMS from a list such as access control, dependency management, custom runtime images, performance, and unique package enforcement. Also be able to differentiate benefits of the JPMS from benefits of Java as a whole. For example, garbage collection is not a benefit of the JPMS.

Use command-line syntax with modules. Use the command-line options for javac, java, and jar. In particular, understand the module (-m) and module path (-p) options.

Create basic module-info files. Place the module-info.java file in the root directory of the module. Know how to code using exports to expose a package and how to export to a specific module. Also, know how to code using requires and requires transitive to declare a dependency on a package or to share that dependency with any modules using the current module.

Identify advanced module-info keywords. The provides keyword is used when exposing an API. The uses keyword is for consuming an API. The opens keyword is for allowing the use of reflection.

Display information about modules. The java command can describe a module, list available modules, or show the module resolution. The jar command can describe a module similar to how the java command does. The jdeps command prints details about a module and packages. The jmod command provides various modes for working with JMOD files rather than JAR files.