© Alexandru Jecan  2017

Alexandru Jecan, Java 9 Modularity Revealed, https://doi.org/10.1007/978-1-4842-2713-8_3

3. Modular JDK and Source Code

Alexandru Jecan

(1)Munich, Germany

This chapter focuses on describing the JDK modularization process that resulted in a new structure of the JDK and its source code. According to Open JDK, the aim of Java Enhancement Proposal 200 – The Modular JDK is to “divide the JDK into a set of modules that can be combined at compile time, build time, or runtime into a variety of configurations.” These configurations can have any size. They can represent one or more modules together with their transitive dependencies, but they can also comprise the entire JDK.

The JDK Module Summary consists of comprehensive information related to the modules that currently exist in the Java Platform. For each module it specifies the following:

  • The number of classes and resources it contains

  • The total size of the module together with the total size of its dependencies

  • The modules it requires

  • The types it exports

  • The services it uses and the services it provides

Note

The JDK Module Summary can be found online at http://cr.openjdk.java.net/∼mr/jigsaw/ea/module-summary.html .

As of September 2017, Project Jigsaw introduced 73 new modules in the Java platform, with a total size of more than 170 MB. Taking the number of classes in each module, the biggest module is java.desktop, which contains 5,900 classes and 284 resources. Its size is more than 26 MB, and the total size of its dependencies is about 55 MB. The second largest module is module java.base, which contains 5,684 classes and 17 resources. It has no dependencies because it’s the base module.

Modular JDK

In Java 9, the JDK is modularized. In order to list all modules that exist in the runtime system, the Java launcher can be used with the command-line option --list-modules. By running the following command, we get a full list of the existing modules in our runtime:

$ java --list-modules

Table 3-1 displays the results .

Table 3-1. The Modules of the Java Runtime System

java.activation

java.xml.crypto

jdk.jfr

java.base

java.xml.ws

jdk.jsobject

java.compiler

java.xml.ws.annotation

jdk.localedata

java.corba

javafx.base

jdk.management

java.datatransfer

javafx.controls

jdk.management.agent

java.desktop

javafx.deploy

jdk.naming.dns

java.instrument

javafx.fxml

jdk.naming.rmi

java.jnlp

javafx.graphics

jdk.net

java.logging

javafx.media

jdk.pack

java.management

javafx.swing

jdk.plugin

java.management.rmi

javafx.web

jdk.plugin.dom

java.naming

jdk.accessibility

jdk.plugin.server

java.prefs

jdk.charsets

jdk.scripting.nashorn

java.rmi

jdk.crypto.cryptoki

jdk.scripting.nashorn.shell

java.scripting

jdk.crypto.ec

jdk.sctp

java.se

jdk.crypto.mscapi

jdk.security.auth

java.se.ee

jdk.deploy

jdk.security.jgss

java.security.jgss

jdk.deploy.controlpanel

jdk.snmp

java.security.sasl

jdk.dynalink

jdk.unsupported

java.smartcardio

jdk.httpserver

jdk.xml.dom

java.sql

jdk.incubator.httpclient

jdk.zipfs

java.sql.rowset

jdk.internal.le

oracle.desktop

java.transaction

jdk.internal.vm.ci

oracle.net

java.xml

jdk.javaws

 

java.xml.bind

jdk.jdwp.agent

 

Table 3-2 contains a short description of each of the standard Java SE modules, as described in the JDK 9 API documentation .

Table 3-2. The Standard Modules According to the Java Platform Standard Edition 9 API Specification

Module name

Description

java.activation

Represents the JavaBeans Activation Framework API

java.base

Represents the primary APIs of the Java SE platform

java.compiler

Expresses the Annotation Processing, Language Model, and Java Compiler APIs

java.corba

Defines the RMI-IIOP API and the OMG CORBA APIs

java.datatransfer

Defines an API for exchanging information between applications

java.desktop

Comprises the AWT and Swing user interface toolkits and also APIs for printing, audio, imaging, and more

java.instrument

Contains the services that permit agents to instrument programs that execute on the Java Virtual Machine

java.logging

Expresses the Java Logging API

java.management

Represents the Java Management Extensions API

java.management.rmi

Represents the RMI connector for the Java Management Extensions API

java.naming

Contains the Java Naming and Directory Interface API

java.prefs

Specifies the Preferences API

java.rmi

Holds the Remote Method Invocation API

java.scripting

Represents the Scripting API

java.se

Represents the core Java SE API

java.se.ee

Represents the full API of the Java SE platform

java.security.jgss

Includes the Java binding of the Generic Security Services API

java.security.sasl

Contains Java support for the Simple Authentication and Security Layer

java.sql

Represents the Java DataBase Connectivity API

java.sql.rowset

Determines the JDBC RowSet API

java.transaction

Specifies a subdivision of the Java Transaction API

java.xml

Includes the Java API for XML Processing, the Streaming API for XML, the Simple API for XML, and the W3C Document Object Model API

java.xml.bind

Represents the Java Architecture for XML Binding API

java.xml.crypto

Describes the XML Cryptography API

java.xml.ws

Specifies the Web Services Metadata API and the Java API for XML-based Web Services

java.xml.ws.annotation

Specifies a part of the Commons Annotations APIs to support programs running on the Java SE platform

Platform Modules

The JCP team put a lot of effort into modularizing the Java platform. The most difficult tasks were to investigate and evaluate the dependencies between different parts of the libraries and to split all the classes from the JDK and put them into modules.

The platform modules are the modules that resulted after splitting the JDK. They completely replace the monolithic JDK and enable us to create custom runtime images. These can consist of a specific configuration that contains a subset of modules together with their transitive dependencies. This subset of modules can represent one module or more than one module. It can also represent all the modules, which is the equivalent of the entire JDK. It’s also possible to combine platform modules together with our own created modules in order to form a runtime image.

Each module has a determined functionality and can define dependencies upon other modules. A platform module is part of the Java runtime and contains source code. Platform modules are able to export their packages so they can be accessible from other modules that read them. When we generally talk about modules, we mean not only the platform modules but also the modules created by application programmers. These modules don’t have a special definition. We could call them “developer modules” or “programmer modules” in order to make a clear distinction between them and the modules that are by default part of the platform, which are the platform modules.

There are two different kinds of platform modules: standard modules and non-standard modules.

Standard Modules

The standard modules are managed by the Java Community Process (JCP) . The names of the standard Java SE modules start with java.*. These names are explicit enough, so it’s quite easy to imagine what the role of the module is. For example, the module named java.rmi defines the Remote Method Invocation API, and the module named java.logging defines the Java Logging API. A standard module can consist of standard API packages as well as non-standard API packages. It can also depend upon one or more non-standard modules.

Non-standard Modules

The non-standard modules are specific to JDK. Their names start with jdk.*. Non-standard modules contain packages and specific JDK code, which may be distinct between various implementations of the Java Development Kit. Some JDK modules, such as tools or service providers, don’t export anything, which means they’re not visible outside the module.

Two things are very import to remember: first, that standard API packages must not be exported by non-standard modules, so their visibility remains hidden from outside. Second, and certainly the most important thing to keep in mind, is that the source code that depends only upon Java SE modules will depend only upon standard Java SE types. This is a great advantage because the code becomes portable to all the existing implementations of the Java SE platform, as stated in the official description of the JEP 200 under http://openjdk.java.net .

Turning a JDK-specific API into a Java standard API is possible, but special focus is required regarding compatibility. When considering doing this, you should take into account whether it’s feasible and necessary by looking at the way it’s used. For example, the reason why the Java Debug Interface wasn’t turned into a standard API is because it’s used only by tools and debuggers, so it definitely doesn’t make sense to enhance it to be part of the Java Standard API .

Every platform module contains a folder called classes inside a folder called share. The classes folder contains all the classes that compose the module, along with the module descriptor in a file called module-info.java. Some modules, such as the java.base module, have native code for different operating systems like Windows, Linux, macOS , and so on.

The JDK Module Graph

The modularization of the Java 9 platform can be well represented as a module graph. Figure 3-1 shows an excerpt of the new module graph of the JDK containing only the standard SE modules. It is resulted after splitting the JDK into modules.

A431534_1_En_3_Fig1_HTML.gif
Figure 3-1. A part of the module graph of JDK 9 representing only the standard SE modules

Only the standard Java SE modules are shown in this graph (the non-Java SE modules aren’t shown due to lack of space). In the graph , the modules are represented by the nodes, and the dependencies between modules are expressed with arrows. If a module depends on another module, there’s a direct arrow from one module to another.

We have two categories of lines between the modules . The solid lines illustrate an implied readability between modules, and the dashed lines mean there is only simple readability between modules but no implied readability. But in both cases, the module reads another module, meaning the module depends on the other module. For instance, there’s a solid line between module java.transaction and module java.rmi. This means that module java.transaction requires transitive module java.rmi. There’s also a dashed line between module java.xml.ws and module java.xml.ws.annotation, which means that module java.xml.ws requires module java.xml.ws.annotation. In other words, module java.xml.ws uses types from module java.xml.ws.annotation.

The graph is hierarchical and clean, has no cycles, and contains no split packages. It has no circular dependencies, because they’re not allowed. The java.base module is right at the bottom of the graph. It doesn’t depend on any other module. All the rest of the modules depend directly or indirectly on module java.base (not shown in the module graph due to lack of space). Therefore, there are lines to module java.base only for the modules that require only java.base and nothing else. For the modules that require at least one more module besides java.base, there’s no line to module java.base.

The java.se.ee module is located at the top of the module graph. It acts like an aggregator module and consists not only of all the Java SE modules, but also of the modules that overlap with the specification of Java EE. Module java.se.ee adds none content of its own. It has only a module descriptor that gathers the contents of the following modules:

  • module java.se

  • module java.activation

  • module java.xml.ws.annotation

  • module java.corba

  • module java.transaction

  • module java.xml.bind

  • module java.xml.ws

In chapter 4, we show what a module descriptor is. The module java.se.ee consists of all the Java SE APIs. Compared to module java.se.ee, the java.se module is an aggregator that consists of the parts of the Java SE that don’t overlap with Java EE. The java.se module gathers the contents of the following modules: java.datatransfer, java.logging, java.sql, java.instrument, java.security.jgss, java.security.sasl, java.prefs, java.xml.crypto, java.rmi, java.xml, java.naming, java.compiler, java.desktop, java.scripting, java.management.rmi, java.sql.rowset, java.management, and java.base.

Note

The module graph in Figure 3-1 shows only the standard SE modules. The non-standard SE modules (with names that start with jdk.*), the Java FX modules (names start with javafx.*), and the Oracle modules (names start with oracle.*) aren’t displayed in this module graph.

More on Modules

Now we will learn how to read the description of a module and to present the module java.base.

Read the Description of a Module

To get the entire description of a module , we can use the --describe-module command-line option of the Java launcher, followed by the module name:

$ java --describe-module <module_name>

By running the --describe-module option on module java.naming, we get the following output:

java.naming@9
exports javax.naming
exports javax.naming.directory
exports javax.naming.event
exports javax.naming.ldap
exports javax.naming.spi
requires java.base mandated
requires java.security.sasl
uses javax.naming.spi.InitialContextFactory
uses javax.naming.ldap.StartTlsResponse
provides java.security.Provider with sun.security.provider.certpath.ldap.JdkLDAP
qualified exports com.sun.jndi.toolkit.ctx to jdk.naming.dns
qualified exports com.sun.jndi.toolkit.url to jdk.naming.dns jdk.naming.rmi
contains com.sun.jndi.ldap
contains com.sun.jndi.ldap.ext
contains com.sun.jndi.ldap.pool
contains com.sun.jndi.ldap.sasl
contains com.sun.jndi.toolkit.dir
contains com.sun.jndi.url.ldap
contains com.sun.jndi.url.ldaps
contains com.sun.naming.internal
contains sun.security.provider.certpath.ldap

The preceding code displays the entire information contained in the module-info.java file of the java.naming module. It additionally contains the contains clauses, which aren’t displayed in the module-info.java file:

  • The exports statements denote that module java.naming make the packages javax.naming, javax.naming.directory, javax.naming.event, javax.naming.ldap, and javax.naming.spi available to any other module that depends on it (on module java.naming).

  • The requires statements from the preceding code denote that module java.naming depends on module java.base and also on module java.security.sasl, meaning that the exported types in those two modules are used inside the java.naming module. For instance, the class LDAPCertStore from the java.naming module imports all the java.security subpackages that are part of module java.security.sasl.

  • The uses statement takes as an argument a type name that represent a service type. In our case, module java.naming consumes instances of InitialContextFactory and StartTlsResponse.

  • The provides statement specifies that the module provides the implementation of java.security.Provider (from module java.base) with sun.security.provider.certpath.ldap.JdkLDAP.

  • The statement qualified exports com.sun.jndi.toolkit.url to jdk.naming.rmi means that package com.sun.jndi.toolkit.url from the java.naming module should be accessible only in the module jdk.naming.rmi. If we take a look in the jdk.naming.rmi module, we find there a class called rmiUrlContext that imports the class com.sun.jndi.toolkit.url.GenericURLContext from module java.naming. This is why module java.naming needs to specify that it exports the package to module jdk.naming.rmi.

  • The contains clauses list all the packages from the module that aren’t part of the standard API.

Chapter 4 explains in detail what the exports, requires, uses, and provides clauses mean and how they can be used.

Module java.base

With more than 5,600 classes and a size of more than 43 MB, java.base is one of the largest modules of the Java Platform Module System. Every module depends on java.base by default, as the java.base module is located at the bottom of the module graph, representing the core of the system.

The module java.base contains the core APIs and encapsulates the Java runtime. It’s not mandatory for a module to explicitly declare that it depends on module java.base because it depends on it automatically. Writing requires java.base is allowed, but it’s not necessary because the compiler inserts it anyway by default.

The module java.base contains the following packages:

  • java.io

  • java.lang.*

  • java.math

  • java.net.*

  • java.nio.*

  • java.security.*

  • java.text.*

  • java.time.*

  • java.util.*

  • javax.crypto.*

  • javax.net.*

  • javax.security .*

Note

I used *to indicate the package and all its corresponding sub-packages.

The module java.base represents the root of the module system because it contains classes like java.lang.Object, java.lang.Class, java.lang.String, java.lang.System, and reflection classes like java.lang.reflect.Constructor and java.lang.reflect.Method.

All the Java platform packages are exported by the java.base module, meaning they’re accessible to any other modules :

// module-info.java (module java.base)
module java.base {
        exports java.io;
        exports java.lang;
        exports java.lang.module;
        ...
        ...
        exports java.text;
        exports java.time;
        exports java.util;
        exports javax.net;
}

The module java.base, called the base module of the system, depends upon no other module. We say that it has no dependencies upon other modules, meaning that its module-info.java file contains no requires clauses. Module java.base also contains the new java.lang.module package introduced in JDK 9, which is part of the new module API. Chapter 9 covers the java.lang.module package. It’s also important to remember that java.base is not an aggregator module.

There will be situations when java.base will be the only module you need in order to compile and run a simple Java application. If all the types you need are in packages contained in the java.base module, then java.base is all you need. Due to the fact that java.base has no dependencies on other modules, there aren’t any other modules that have to be utilized together with java.base. This is a great advantage because before JDK 9 we had to take the entire JDK since even a simple class like java.lang.Object previously had to be used with a large number of classes. A module also contains a new file called module-info.java, which represents the module descriptor, which Chapter 4 describes in detail.

In addition, java.base contains lots of JDK internal packages like jdk.internal.util, sun.io, sun.text, sun.util, com.sun.crypto.provider, com.sun.net.ssl, and more.

Note

Use the Java launcher with the option --describe-module java.base to find out all the available information related to the java.base module descriptor.

We’ve seen how the JDK was modularized and what the module graph looks like. In the next section, you’ll learn about the modularization of the source code .

Modular Source Code

Whereas JEP 200’s role is to divide the JDK into a set of modules, JEP 201’s role is, according to Open JDK, “to reorganize the source code in the JDK into modules, enhance the build system to compile modules, and enforce module boundaries at build time.” The layout of the source code was entirely changed in JDK and replaced by modules. The whole source code of a module is now inside a single directory. Therefore, in JDK 9 we have a new scheme of the source code, and that’s the focus of this section.

New Scheme for the Source Code

Figure 3-2 illustrates the new scheme of the source code in the JDK.

A431534_1_En_3_Fig2_HTML.gif
Figure 3-2. New scheme of JDK 9 source code

The src directory of the JDK contains a list of directories that represent modules names. Each module has its own directory. The directories’ modules names start with java.* or jdk.* and represent the names of the modules.

Every module directory contains a share directory, which consists of cross-platform source code. Additionally, a module directory can also contain other directories related to operating systems, like aix, linux, macosx, solaris, unix, and windows. These directories contain source code pertaining to a single operating system only. Not all the modules contain all the operating-system directories just listed. There are modules that contain operating-system-specific source code for all the operating systems (like java.base or java.desktop) or only for a part of them. For instance, the module java.prefs consists only of three operating-system directories: macosx, unix, and windows—meaning that this module doesn’t have any source code specific to Linux or Solaris inside it.

Note

The aggregator modules like java.se.ee and java.se don’t have any operating-system-specific source code because they don’t have any source code inside their directories.

The next level of directories inside a module contains directories with names like classes, conf, lib, native, and doc. Except for the conf directories, the other four directories can also be found in JDK 8 under the share directory.

The classes directories in JDK 9 consist of Java source files grouped into directories that designate the structure of their packages. We want to point out two main differences to JDK 8: first, in JDK 9 there’s a classes directory for each of the existing modules . Even the aggregator modules have a classes directory. Second, in JDK 9 a module-info.java file is placed in the root of the classes directory. The module-info.java file represents the module descriptor and was introduced in Java 9. Each module has a module-info.java file inside its classes directory. The classes directory can contain java, javax, jdk, sun, com, or org directories, depending on the packages contained in it.

The conf directory includes configuration files, which may be properties files, security policy files, policy files, and so on. This is a new directory that didn’t exist in JDK 8.

The lib directory is present only in java.base and consists of the file default.policy in the directories share, solaris, and windows.

The native directory holds C and C++ source files, native classes, and procedures. It can contain some of the following directories: include, launcher, common, libfdlibm, libjava, libjimage, libjli, libnet, libnio, libverify, libzip, and more. According to Open JDK, the names of the directories correspond “to the names of the shared libraries into which the compiled code will be linked.” The include folder represents an exception to this rule because it has C/C++ header files in it.

Note

The classes and native directories weren’t renamed in JDK 9 because doing so could create confusion and slow the adoption of JDK 9.

Table 3-3 shows where some of the most important classes in Java are now located.

Table 3-3. Location of Some of the Most Important Java Classes

Class name

Location

java.lang.Object

src / java.base / share / classes / java / lang

java.lang.String

src / java.base / share / classes / java / lang

java.lang.Exception

src / java.base / share / classes / java / lang

java.lang.Class

src / java.base / share / classes / java / lang

java.util.ArrayList

src / java.base / share / classes / java / util

java.util.Date

src / java.base / share / classes / java / util

java.io.File

src / java.base / share / classes / java / io

java.net.URL

src / java.base / share / classes / java / net

java.text.Format

src / java.base / share / classes / java / text

java.util.logging.Logger

src / java.logging / share / classes / java / util / logging

java.sql.DriverManager

src / java.sql / share / classes / java / sql

Note

As you can see in Table 3-3, the most important and useful Java classes are in module java.base, the base module.

The next subsection describes the new structure of the source code in JDK 9.

Comparison Source Code Structure

Here’s a short comparison between the structure of the source code in JDK 8 and JDK 9. In JDK 8, the structure of the source code looks like this:

jdk / src / share / {back; bin; classes; demo; doc; instrument; javavm; lib; native; npt; sample; transport}

In JDK 9, the structure of the source looks like this:

jdk / src / <module_name> / share / {classes; conf; lib; native; doc}

As you can see, an intermediary directory containing the module name was added between the directories src and share. Another important difference between JDK 8 and 9 is that in JDK 9 for each classes directory of each module there’s a module-info.java file at the root level. In JDK 9, under the classes directory and its subdirectories are only the packages and the classes that belong to the corresponding module. In JDK 8, the classes directory and its subdirectories contain all the packages and classes that comprise the Java platform.

Build Process Adjustments

Not only the source code, but the build itself was organized around modules. There are a new proposed layout and new build targets. The output emitted during the build is different in Java 9 compared to previous versions. The build was changed so that it builds everything as modules. Besides that, the make files have been split into module specific files.

Table 3-4 shows the new structure of the build system in JDK 9 compared to the one that existed in JDK 8.

Table 3-4. Comparison Between the Structure of the Build System in JDK 8 and JDK 9

Structure of the Build System in JDK 8

Structure of the Build System in JDK 9

jdk / classes / *.class

jdk / modules / <module_name> / *.class

Table 3-5 shows a list of target commands together with their descriptions that are used to build the JDK 9 . These are described in the official JDK 9 API specification.

Table 3-5. Target commands Used to Build the JDK 9

Target Command

Description

make java

Compiles all Java classes from the system

make java.sql

Compiles Java code as well as native code in the java.sql module together with all its dependencies

make java.sql-java

Compiles only the Java classes in the java.sql module together with all its dependencies

make [default]

Compiles everything

make all

Builds everything (JARs, docs, images, and so on), executes a verification tool on the Java classes which finds broken module boundaries

make images

Same functionality as JDK 8

make hotspot

Same functionality as JDK 8

make docs

Builds the entire documentation

make docs-javadocs

Builds only the javadoc

make gensrc

Executes all the steps involving the generation of source code

The prospect of compiling only one module at a time is one of the most important changes to the build system. A module can be compiled together with its dependencies, and the compiled classes are divided into modules. During the build process, the modules that are independent can be compiled at the same time. If module boundaries are violated, the build process won’t succeed.

The first module to be compiled is the java.base module because it’s required by all other modules. The module graph is traversed in a reversed order (starting from bottom to top) during the compilation. This makes sense because by compiling the modules successively from java.base up to the top of the module graph, we avoid the situation of compiling a module that has a dependency on a module that has not been compiled.

Note

A great advantage of being able to compile modules lies in the fact that the source code in the JAX-WS, JAXP, and CORBA repositories can now use the new Java language APIs. In the Java versions prior to version 9, that wasn’t possible because those repositories were compiled before the JDK repository.

The JCP team performed significant modifications to the build system in order for modules to be built independently. For instance , a change performed in the java.logging module won’t determine a new build of the java.base module. This is a great achievement because it boosts productivity.

Summary

This chapter covered two of the most important JEPs of the Java Platform Module System: the Modular SDK and the Modular Source Code.

First, we showed how to list all the modules of the Java runtime system using the --list-modules command-line option. We then gave a brief explanation of the standard modules that are part of the Java Platform Standard Edition 9 API Specification. We explained what the platform modules are and talked about the characteristics of the standard and non-standard modules that comprise the Java Platform Module System. We also showed the new JDK module graph that resulted after the JDK modularization. And we showed how to get the entire content of the module descriptor of module java.naming using the --describe-module <module_name> command-line option. The end of the first part discussed the module java.base, the most important module of the Java Platform Module System.

The second part of the chapter focused on describing the changes performed at the source code level due to the implementation of JEP 201 (the Modular Source Code). We started by presenting the new scheme of the JDK 9 source code and then described how the source code was organized into directories and what each directory represents. We pointed out the differences between the source code layout in JDK 8 and JDK 9. The chapter finished by talking about the way the build system was enhanced in order to meet the requirements of the newly introduced modules that are now first-class components of the Java platform.

Chapter 4 explains what a module is and shows how you can define and use your own modules.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.35.193