Chapter 11 Modules Notes Flashcards
Modules
Introducing Modules
- The Java Platform Module System (JPMS) was introduced in Java 9
- The main purpose of a module is to provide groups of related packages to offer a particular set of functionality to developers.
The Java Platform Module System includes the following:
* A format for module JAR files
* Partitioning of the JDK into modules
* Additional command-line options for Java tools
EXPLORING A MODULE
A module
is a group of one or more packages
plus a special file called module-info.java
.
dependencies where one module relies on code in another
BENEFITS OF MODULES
-
Better Access Control
- a fifth level of access control.
- They can expose packages within the modular JAR to specific other packages.
- This stronger form of encapsulation really does create internal packages.
-
Clearer Dependency Management
specify their dependencies in the module-info.java file. -
Custom Java Builds
- allows developers to specify what modules they actually need.
- A tool called
jlink
is used to create this runtime image.
-
Improved Performance
improves startup time and requires less memory to run. -
Unique Package Enforcement
A package is only allowed to be supplied by one module.
Better Access Control
what if we wrote some complex logic that we wanted to restrict to just some packages?
- Modules solve this problem by acting as a fifth level of access control.
- They can expose packages within the modular JAR to specific other packages.
- This stronger form of encapsulation really does create internal packages.
- You’ll see how to code it when we talk about the module-info.java file later in this chapter.
Clearer Dependency Management
In a fully modular environment, each of the open source projects would specify their dependencies in the module-info.java file.
Custom Java Builds
- The Java Platform Module System allows developers to specify what modules they actually need.
- This makes it possible to create a smaller runtime image that is customized to what the application needs and nothing more.
- Users can run that image without having Java installed at all.
- A tool called jlink is used to create this runtime image. Luckily, you only need to know that custom smaller runtimes are possible. How to create them is out of scope for the exam.
In addition to the smaller scale package, this approach improves security. If you don’t use AWT and a security vulnerability is reported for AWT, applications that packaged a runtime image without AWT aren’t affected.
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
Another manifestation of JAR hell is when the same package is in two JARs. There are a number of causes of this problem including renaming JARs, clever developers using a package name that is already taken, and having two versions of the same JAR on the classpath.
The Java Platform Module System prevents this scenario. A package is only allowed to be supplied by one module. No more unpleasant surprises about a package at runtime.
MODULES FOR EXISTING CODE
test
Creating and Running a Modular Program
-
CREATING THE FILES
-
class file
- Java classes should be in packages.
-
module-info.java
- module keyward
- the root directory of your module.
-
class file
- The next step is to make sure the files are in the right directory structure.
-
COMPILING OUR FIRST MODULE
javac --module-path mods -d feeding feeding/zoo/animal/feeding/*.java feeding/module-info.java
CREATING THE FILES
Create a class file:
package zoo.animal.feeding; public class Task { public static void main(String... args) { System.out.println("All fed!"); } }
module-info.java
file. This is the simplest possible one.
module zoo.animal.feeding { }
There are a few key differences between a module-info
file and a regular Java class
:
- The
module-info
file must be in the root directory of your module. Regular Java classes should be in packages. - The
module-info
file must use the keywordmodule
instead of class, interface, or enum. - The module name follows the naming rules for package names. It often includes periods (.) in its name. Regular class and package names are not allowed to have dashes (-). Module names follow the same rule.
That’s a lot of rules for the simplest possible file. There will be many more rules when we flesh out this file later in the chapter.
CAN A MODULE-INFO.JAVA
FILE BE EMPTY?
Yes. As a bit of trivia,
it was legal to compile any empty file with a .java extension even before modules.
The compiler sees there isn’t a class in there and exits without creating a .class file.
COMPILING OUR FIRST MODULE
> [!NOTE]
When you’re entering commands at the command line, they should be typed all on one line.
javac --module-path mods -d feeding feeding/zoo/animal/feeding/*.java feeding/module-info.java
- the
-d
option specifies the directory to place the class files in. - The end of the command is a list of the .java files to compile. You can list the files individually or use a wildcard for all .java files in a subdirectory.
-
--module-path
option indicates the location of any custom module files. - The syntax
--module-path
and-p
are equivalent.
The following four commands show the -p option:
javac -p mods -d feeding feeding/zoo/animal/feeding/*.java feeding/*.java javac -p mods -d feeding feeding/zoo/animal/feeding/*.java feeding/module-info.java javac -p mods -d feeding feeding/zoo/animal/feeding/Task.java feeding/module-info.java javac -p mods -d feeding feeding/zoo/animal/feeding/Task.java feeding/*.java
Options you need to know for using modules with javac
-
Directory for class files,
-d <dir>
-
Module path,
-p <path>, --module-path <path>
WHAT HAPPENED TO THE CLASSPATH
?
You can still use these options in Java 11. In fact, it is common to do so when writing nonmodular programs.
-cp, --class-path, and -classpath
BUILDING MODULES
Do be sure to memorize the module command syntax. You will be tested on it on the exam. We will be sure to give you lots of practice questions on the syntax to reinforce it.
RUNNING OUR FIRST MODULE
Pay special attention to the book.module/com.sybex.OCP part. It is important to remember that you specify the module name followed by a slash (/) followed by the fully qualified class name.
java --module-path mods --module book.module/com.sybex.OCP
-
Location of modules,
--module-path, -p
-
Module name,
--module, -m
- Module/package separator
- Package name
- Class name
java --module-path feeding --module zoo.animal.feeding/zoo.animal.feeding.Task java -p feeding -m zoo.animal.feeding/zoo.animal.feeding.Task
--module-path uses the short form of -p
--module short option is -m
Options you need to know for using modules with java
-
Module name,
-m <name>, --module <name>
-
Module path,
-p <path>, --module-path <path>
PACKAGING OUR FIRST MODULE
Be sure to create a mods directory before running this command:
jar -cvf mods/zoo.animal.feeding.jar -C feeding/ .
- We are packaging everything under the feeding directory
- and storing it in a JAR file named zoo.animal.feeding.jar under the mods folder.
run the program again, but this time using the mods directory instead of the loose classes:
java -p mods -m zoo.animal.feeding/zoo.animal.feeding.Task
Since the module path is used, a module JAR is being run.
> [!NOTE]
It is possible to version your module using the –module-version option.
This isn’t on the exam but is good to do when you are ready to share your module with others.
version your module using the –module-version option.
Updating Our Example for Multiple Modules
test
UPDATING THE FEEDING MODULE
other modules call code in the zoo.animal.feeding package, we need to declare this intent in the module-info file.
The exports keyword is used to indicate that a module intends for those packages to be used by Java code outside the module. As you might expect, without an exports keyword, the module is only available to be run from the command line on its own. In
module zoo.animal.feeding { exports zoo.animal.feeding; }
Recompiling and repackaging the module will update the module-info inside our zoo.animal.feeding.jar file. These are the same javac and jar commands you ran previously.
javac -p 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
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 to be used by this module. It will not be exported from the module.
The module contains two basic packages and classes in addition to the module-info.java file:
// HippoBirthday.java package zoo.animal.care.details; import zoo.animal.feeding.*; public class HippoBirthday { private Task task; } // Diet.java package zoo.animal.care.medical; public class Diet { }
This time the module-info.java file specifies three things.
1: module zoo.animal.care { 2: exports zoo.animal.care.medical; 3: requires zoo.animal.feeding; 4: }
- Line 1 specifies the name of the module.
- Line 2 lists the package we are exporting so it can be used by other modules. So far, this is similar to the zoo.animal.feeding module.
- On line 3, we see a new keyword. The requires statement specifies that a module is needed. The zoo.animal.care module depends on the zoo.animal.feeding module.
javac -p mods -d care care/zoo/animal/care/details/*.java care/zoo/animal/care/medical/*.java care/module-info.java
This time the module-info.java
file specifies three things.
1: module zoo.animal.care { 2: exports zoo.animal.care.medical; 3: requires zoo.animal.feeding; 4: }
- Line 1 specifies the name of the module.
- Line 2 lists the package we are exporting so it can be used by other modules. So far, this is similar to the zoo.animal.feeding module.
- On line 3, we see a new keyword. The requires statement specifies that a module is needed. The zoo.animal.care module depends on the zoo.animal.feeding module.
ORDER MATTERS!
Order matters when compiling a module.
javac -p mods -d care care/module-info.java care/zoo/animal/care/details/*.java care/zoo/animal/care/medical/*.java
The compiler complains that it doesn’t know anything about the package zoo.animal.care.medical.
care/module-info.java:3: error: package is empty or does not exist: zoo.animal.care.medical exports zoo.animal.care.medical;
- A package must have at least one class in it in order to be exported.
- Since we haven’t yet compiled zoo.animal.care.medical.Diet, the compiler acts as if it doesn’t exist.
- If you get this error message, you can reorder the javac statement.
- Alternatively, you can compile the packages in a separate javac command, before compiling the module-info file.
jar -cvf mods/zoo.animal.care.jar -C care/ .
CREATING THE TALKS MODULE
the module-info.java file for zoo.animal.talks:
1: module zoo.animal.talks { 2: exports zoo.animal.talks.content; 3: exports zoo.animal.talks.media; 4: exports zoo.animal.talks.schedule; 5: 6: requires zoo.animal.feeding; 7: requires zoo.animal.care; 8: }
- Line 1 shows the module name.
- Lines 2–4 allow other modules to reference all three packages.
- Lines 6–7 specify the two modules that this module depends on.
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
module zoo.staff { requires zoo.animal.feeding; requires zoo.animal.care; requires zoo.animal.talks; }
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
these are not Java keywords they are directives, can appear in any order in the module-info file.
- exports,
- requires,
- provides,
- uses,
- and opens.
ARE EXPORTS AND REQUIRES KEYWORDS?
These special keywords are called directives.
- exports,
- requires,
- provides,
- uses,
- and opens.
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.staff; exports zoo.animal.talks.media; exports zoo.animal.talks.schedule; requires zoo.animal.feeding; requires zoo.animal.care; }
EXPORTED TYPES
Exporting a package
- All public classes, interfaces, and enums are exported.
- Further, any public and protected fields and methods in those files are visible.
- Fields and methods that are private are not visible because they are not accessible outside the class.
- Similarly, package-private fields and methods are not visible because they are not accessible outside the package.
REQUIRES TRANSITIVE
requires moduleName specifies that the current module depends on moduleName.
There’s also a requires transitive moduleName, which means that any module that requires this module will also depend on moduleName.
module zoo.animal.feeding { exports zoo.animal.feeding; } module zoo.animal.care { exports zoo.animal.care.medical; requires transitive zoo.animal.feeding; } module zoo.animal.talks { exports zoo.animal.talks.content to zoo.staff; exports zoo.animal.talks.media; exports zoo.animal.talks.schedule; // no longer needed requires zoo.animal.feeding; // no longer needed requires zoo.animal.care; requires transitive zoo.animal.care; } module zoo.staff { // no longer needed requires zoo.animal.feeding; // no longer needed requires zoo.animal.care; requires zoo.animal.talks; }
Effects of requires transitive
test
Duplicate requires Statements
module bad.module { requires zoo.animal.talks; requires transitive zoo.animal.talks; }
Java doesn’t allow you to repeat the same module in a requires clause. It is redundant and most like an error in coding. Keep in mind that requires transitive is like requires plus some extra behavior.
PROVIDES, USES, AND OPENS
The provides keyword specifies that a class provides an implementation of a service.
To use it, you supply the API and class name that implements the API:
provides zoo.staff.ZooApi with zoo.staff.ZooImpl
The uses keyword specifies that a module is relying on a service. To code
it, you supply the API you want to call:
uses zoo.staff.ZooApi
Discovering Modules
- “unjar” it and open the module-info file.
- THE JAVA COMMAND
java -p mods -d zoo.animal.feeding java -p mods --describe-module zoo.animal.feeding
*
Discovering Modules
The java command now has an option to describe a module. The following two commands are equivalent:
java -p mods -d zoo.animal.feeding java -p mods --describe-module zoo.animal.feeding
Each prints information about the module. For example, it might print this:
zoo.animal.feeding file:///absolutePath/mods/zoo.animal.feeding.jar exports zoo.animal.feeding requires java.base mandated
- The first line is the module we asked about: zoo.animal.feeding.
- The second line starts information about the module. In our case, it is the same package exports statement we had in the module-info file.
- On the third line, we see requires java.base mandated. Now wait a minute. The module-info file very clearly does not specify any modules that zoo.animal.feeding has as dependencies.
The java.base module is special. It is automatically added as a dependency to all modules. This module has frequently used packages like java.util. That’s what the mandated is about. You get java.base whether you asked for it or not.
In classes, the java.lang package is automatically imported whether you type it or not. The java.base module works the same way. It is automatically available to all other modules.
zoo.animal.feeding file:///absolutePath/mods/zoo.animal.feeding.jar exports zoo.animal.feeding requires java.base mandated
- The first line is the module we asked about: zoo.animal.feeding.
- The second line starts information about the module. In our case, it is the same package exports statement we had in the module-info file.
- On the third line, we see requires java.base mandated. Now wait a minute. The module-info file very clearly does not specify any modules that zoo.animal.feeding has as dependencies.
- The java.base module is special. It is automatically added as a dependency to all modules. This module has frequently used packages like java.util. That’s what the mandated is about. You get java.base whether you asked for it or not.
- In classes, the java.lang package is automatically imported whether you type it or not. The java.base module works the same way. It is automatically available to all other modules.
module-info in zoo.animal.care:
module zoo.animal.care { exports zoo.animal.care.medical to zoo.staff; requires transitive zoo.animal.feeding; }
Now we have the command to describe the module and the output.
java -p mods -d zoo.animal.care zoo.animal.care file:///absolutePath/mods/zoo.animal.care.jar requires zoo.animal.feeding transitive requires java.base mandated qualified exports zoo.animal.care.medical to zoo.staff contains zoo.animal.care.details
- The first line of the output is the absolute path of the module file.
- The two requires lines should look familiar as well.
- The first is in the module-info, and the other is added to all modules.
- Next comes something new. The qualified exports is the full name of exporting to a specific module.
- Finally, the contains means that there is a package in the module that is not exported at all. This is true. Our module has two packages, and one is available only to code inside the module.
THE JAVA COMMAND
test
Describing a Module
test
MORE ABOUT DESCRIBING MODULES
As a reminder, the following are the contents of module-info in zoo.animal.care:
module zoo.animal.care { exports zoo.animal.care.medical to zoo.staff; requires transitive zoo.animal.feeding; }
Now we have the command to describe the module and the output.
java -p mods -d zoo.animal.care zoo.animal.care file:///absolutePath/mods/zoo.animal.care.jar requires zoo.animal.feeding transitive requires java.base mandated qualified exports zoo.animal.care.medical to zoo.staff contains zoo.animal.care.details
The first line of the output is the absolute path of the module file. The two requires lines should look familiar as well. The first is in the module-info, and the other is added to all modules. Next comes something new. The qualified exports is the full name of exporting to a specific module.
Finally, the contains means that there is a package in the module that is not exported at all. This is true. Our module has two packages, and one is available only to code inside the module.
Listing Available Modules
you can use the java command to list the modules that are available. The simplest form lists the modules that are part of the JDK:
java --list-modules
When we ran it, the output went on for 70 lines and 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. You can tell that we were using Java 11.0.2 when testing this example.
java -p mods --list-modules
zoo.animal.care file:///absolutePath/mods/zoo.animal.care.jar zoo.animal.feeding file:///absolutePath/mods/zoo.animal.feeding.jar zoo.animal.talks file:///absolutePath/mods/zoo.animal.talks.jar zoo.staff file:///absolutePath/mods/zoo.staff.jar
Since these are custom modules, we get a location on the file system. If the project had a module version number, it would have both the version number and the file system path.
Showing Module Resolution
In case listing the modules didn’t give you enough output, you can also use the –show-module-resolution option. You can think of it as a way of debugging modules. It spits out a lot of output when the program starts up.
Then it runs the program.
java --show-module-resolution -p feeding -m zoo.animal.feeding/zoo.animal.feeding.Task
Luckily you don’t need to understand this output. That said, having seen it will make it easier to remember. Here’s a snippet of the output:
root zoo.animal.feeding file:///absolutePath/feeding/ java.base binds java.desktop jrt:/java.desktop java.base binds jdk.jartool jrt:/jdk.jartool ... jdk.security.auth requires java.naming jrt:/java.naming jdk.security.auth requires java.security.jgss jrt:/java.security.jgss ... All fed!
It starts out by listing the root module. That’s the one we are running:
zoo.animal .feeding. Then it lists many lines of packages included by the mandatory java.base module. After a while, it lists modules that have dependencies. Finally, it outputs the result of the program All fed!. The total output of this command is 66 lines.
THE JAR COMMAND
the jar command can describe a module. Both of these commands are equivalent:
jar -f mods/zoo.animal.feeding.jar -d jar --file mods/zoo.animal.feeding.jar --describe-module
The output is slightly different from when we used the java command to describe the module. With jar, it outputs the following:
zoo.animal.feeding jar:file:///absolutePath/mods/zoo.animal.feeding.jar /!module-info.class exports zoo.animal.feeding requires java.base mandated
The JAR version includes the module-info in the filename, which is not a particularly significant difference in the scheme of things. You don’t need to know this difference. You do need to know that both commands can describe a module.
THE JDEPS COMMAND
The jdeps command gives you information about dependencies within 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.
Let’s start with a simple example and ask for a summary of the dependencies in zoo.animal.feeding. Both of these commands give the same output:
jdeps -s mods/zoo.animal.feeding.jar jdeps -summary mods/zoo.animal.feeding.jar
Notice that there is one dash (-) before -summary rather than two.
Regardless, the output tells you that there is only one package and it depends on the built-in java.base module.
zoo.animal.feeding -> java.base
Alternatively, you can call jdeps without the summary option and get the long form:
jdeps mods/zoo.animal.feeding.jar [file:///absolutePath/mods/zoo.animal.feeding.jar] requires mandated java.base (@11.0.2) zoo.animal.feeding -> java.base zoo.animal.feeding -> java.io java.base zoo.animal.feeding -> java.lang java.base
- The first part of the output shows the module filename and path.
- The second part lists the required java.base dependency and version number.
- This has the high-level summary that matches the previous example.
- Finally, the last four lines of the output list the specific packages within the java.base modules that are used by zoo.animal.feeding.
jdeps -s --module-path mods mods/zoo.animal.care.jar jdeps -summary --module-path mods mods/zoo.animal.care.jar
There is not a short form of –module-path in the jdeps command. The output is only two lines:
zoo.animal.care -> java.base zoo.animal.care -> zoo.animal.feeding
We can see that the zoo.animal.care module depends on our custom zoo.animal.feeding module along with the built-in java.base.
In case you were worried the output was too short, we can run it in full mode:
jdeps --module-path mods mods/zoo.animal.care.jar
This time we get lots of output:
zoo.animal.care [file:///absolutePath/mods/zoo.animal.care.jar] requires mandated java.base (@11.0.2) requires transitive zoo.animal.feeding zoo.animal.care -> java.base zoo.animal.care -> zoo.animal.feeding zoo.animal.care.details -> java.lang java.base zoo.animal.care.details -> zoo.animal.feeding zoo.animal.feeding zoo.animal.care.medical -> java.lang java.base
As before, there are three sections.
* The first section is the filename and required dependencies.
* The second section is the summary showing the two module dependencies with an arrow.
* The last six lines show the package-level dependencies.
THE JMOD COMMAND
JMOD files are recommended only when you have native libraries or something that can’t go inside a JAR file. This is unlikely to affect you in the real world.
The most important thing to remember is that jmod is only for working with the JMOD files.
Reviewing Command-Line Options
test