© Alexandru Jecan  2017

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

9. The New Module API

Alexandru Jecan

(1)Munich, Germany

Project Jigsaw introduced a new layer in JDK 9, modules, and added many new features to the Java platform. Among the new features, a new module API was introduced for handling the work with modules. The new module API was added in the module java.base in the packages java.lang and java.lang.module. It contains a set of classes, interfaces, enumerations (enums), and exceptions that can be used to work with modules.

The module API allows us to perform different operations on modules, such as extracting the information from the module descriptors, accessing the resources of a module, searching for modules on the module path or for all the system modules, creating layers, and more. The module API can also be used to dynamically add reads, opens, or exports directives to other modules at runtime. We’ll start this chapter by briefly presenting the structure of the new module API: its interfaces, classes, enums, and exceptions.

According to the JDK 9 API specification, two interfaces were added for working with modules. Table 9-1 shows the new interfaces of the module API.

Table 9-1. The Interfaces of the Module API

Name

Type

Description

ModuleFinder

Interface

Finds modules during resolution or service binding

ModuleReader

Interface

Accesses the module’s content

Table 9-2 lists the classes of the new module API.

Table 9-2. The Classes of the Module API

Name

Type

Description

Configuration

Class

A configuration that contains the readability graph

Module

Class

A module at runtime

ModuleDescriptor

Class

A module descriptor

ModuleDescriptor.Builder

Class

A builder for creating module descriptors

ModuleDescriptor.Exports

Class

A package exported by a module

ModuleDescriptor.Opens

Class

A package opened by a module

ModuleDescriptor.Provides

Class

A service with implementations provided by the module

ModuleDescriptor.Requires

Class

A dependence upon a module

ModuleDescriptor.Version

Class

A module version

ModuleReference

Class

A reference to a module

ResolvedModule

Class

A module in a graph of resolved modules

Table 9-3 shows the enumerations of the module API.

Table 9-3. The Enumerations of the Module API

Name

Type

Description

ModuleDescriptor.Exports.Modifier

Enum

A modifier on an exported package

ModuleDescriptor.Modifier

Enum

A modifier on an module

ModuleDescriptor.Opens. Modifier

Enum

A modifier on an open package

ModuleDescriptor.Requires.Modifier

Enum

A modifier on a module dependence

Table 9-4 shows the exceptions of the module API.

Table 9-4. The Exceptions of the Module API

Name

Type

Description

FindException

Exception

Thrown when an error occurs while finding a module

InvalidModuleDescriptorException

Exception

Thrown when a module descriptor has an invalid format

ResolutionException

Exception

Thrown when the process of resolving a set of modules fails

LayerInstantiationException

Exception

Thrown when an error occurs while creating a module layer

We’ll next look at some of the most important classes and interfaces of the new module API in detail, starting with the fundamental one, the Module class.

The Module Class

The Module class represents a module at runtime, which can be either a named module or an unnamed module. The Module class was introduced in Java 9 and is located in module java.base in the package java.lang in the following location:

java.baseshareclasessjavalangModule.java

Let’s explore the attributes, constructors, and methods defined by the Module class.

Attributes

Following are some of the most important attributes defined by the Module class:

  • ModuleLayer layer: Represents the layer that contains this module. The layer can also be null.

  • String name: Represents the name of this module.

  • ClassLoader loader: Represents the ClassLoader of this module.

  • ModuleDescriptor descriptor: Represents the ModuleDescriptor of this module.

  • Map<String, Set<Module>> exportedPackages: Represents the packages that are exported by this module.

  • Map<String, Set<Module>> openPackages: Represents the packages that are open to other modules.

  • Set<Module> reads: Represents the modules that this module reads.

  • static final Module ALL_UNNAMED_MODULE: Represents a special module that defines the entire set of unnamed modules. An unnamed module has no ClassLoader object , no ModuleLayer object and no ModuleDescriptor object defined.

Constructors

The Module class has three types of constructors, as stated in the JDK 9 API specification:

  • Module(ClassLoader loader): This constructor creates the unnamed module for the given ClassLoader. The ClassLoader object can also be null. An unnamed module contains no ModuleDescriptor and no ModuleLayer.

  • Module(ClassLoader loader, ModuleDescriptor descriptor): This constructor creates a named module that’s not inside a ModuleLayer.

  • Module(ModuleLayer layer, ClassLoader loader, ModuleDescriptor descriptor, URI uri): This constructor creates a named module inside a ModuleLayer, which means that the module is represented inside the virtual machine.

Methods

The most important methods defined by the Module class are, according to the JDK 9 API specification, the following:

  • boolean isNamed(): Returns true if the module is a named module and false otherwise.

  • String getName(): Returns the name of the module if the module is a named module. If the module is in an unnamed module, it returns null.

  • ClassLoader getClassLoader(): Returns the ClassLoader for this module.

  • ModuleDescriptor getDescriptor(): Returns the ModuleDescriptor for this module if the module is a named module. If the module is an unnamed module, it returns null.

  • ModuleLayer getLayer(): Returns the ModuleLayer that contains this module. If this module is not in a module layer, it returns null. If the module is in an unnamed module, null is returned.

  • boolean canRead(Module other): Returns true if this module reads the module given as parameter and false otherwise. If our module is an unnamed module, then true will be always returned, since an unnamed module reads all the modules.

  • Module addReads(Module other): Updates this module to read the given module.

  • boolean isExported(String packageName): Returns true if this module exports the given package. If our module is in an unnamed module, then true is by default returned. If the package is opened, then this method returns true because an opened package is also exported at runtime.

  • boolean isExported(String packageName, Module other): Returns true if this module exports the given package to at least the given module.

  • boolean isOpen(String packageName): Returns true if this module has opened the given package. If the module is in an unnamed module, then true is always returned.

  • boolean isOpen(String packageName, Module other): Returns true if this module has opened the given package to at least the given module specified as the second parameter.

  • Module addExports(String packageName, Module other): Updates this module to export the given package to the given module.

  • Module addOpens(String packageName, Module other): Updates this module to open the given package to the given module.

  • Module addUses(Class<?> service ): Updates this module to add a service dependence on the given service type.

  • boolean canUse(Class<?> service): Returns true if this module has a service dependence on the given service type. If our module is an automatic module or is in an unnamed module, the method returns true by default.

  • Set<String> getPackages(): Returns a set of package names for all the packages in this module.

  • InputStream getResourceAsStream(String name): Returns an InputStream object for reading a resource in this module. The resource is identified by the given name.

Changes in java.lang.Class

The class java.lang.Class has been enhanced with three methods in order to better fit into the newly added module system. Here are the methods added in Java 9 inside the Class class, according to the JDK 9 API specification:

  • Class<?> forName(Module module, String className): Returns the Class object of the given class from the given module.

  • Module getModule(): Returns the module that this class or interface is a member of.

  • String getPackageName(): Returns a String representing the name of the package of this class.

The ModuleDescriptor class

The ModuleDescriptor class is located inside the java.lang.module package of the java.base module. This class represents a module descriptor of a named module. An instance of this class expresses a module descriptor that’s obtained from a module-info.class file.

Because a ModuleDescriptor object is immutable, obtaining a ModuleDescriptor object is straightforward. It’s obtained by calling the getDescriptor() method on the corresponding Module object:

java.lang.Module myModule = MyClass.class.getModule();
java.lang.ModuleDescriptor myModuleDescriptor = myModule.getDescriptor();

But this is not the only way we can get a ModuleDescriptor object. We can create a ModuleDescriptor using the build() method of the Builder class, which is an inner class of the ModuleDescriptor class. Creating a ModuleDescriptor using the Builder class is not the common approach, so we don’t cover it in this book.

Note

A module descriptor can’t describe the unnamed module. It describes all the existing types of modules except the unnamed module: normal modules, open modules, and automatic modules.

The ModuleDescriptor class consists of a set of nested classes, discussed later in this section:

  • ModuleDescriptor.Builder class

  • ModuleDescriptor.Exports class

  • ModuleDescriptor.Modifier class

  • ModuleDescriptor.Opens class

  • ModuleDescriptor.Provides class

  • ModuleDescriptor.Requires class

  • ModuleDescriptor.Version class

The instances of four of these nested classes (ModuleDescriptor.Exports, ModuleDescriptor.Opens, ModuleDescriptor.Provides, and ModuleDescriptor.Requires) represent statements that can reside inside a module declaration module-info.java file: exports, opens, provides, and requires.

We’ll continue by looking at some of the attributes defined inside the ModuleDescriptor class.

ModuleDescriptor Attributes

The most important attributes defined by the ModuleDescriptor class are the following, and they’re described more fully in the next subsection:

  • String name

  • Version version

  • Set<Modifier> modifiers

  • boolean open

  • boolean automatic

  • Set<Requires> requires

  • Set<Exports> exports

  • Set<Opens> opens

  • Set<String> uses

  • Set<Provides> provides

  • Set<String> packages

  • String mainClass

  • static enum Modifier {OPEN, AUTOMATIC, SYNTHETIC, MANDATED}

Next up: the most important methods of the ModuleDescriptor class.

ModuleDescriptor Methods

The JDK 9 API specification defines a couple of methods for the ModuleDescriptor class. The most important methods of the ModuleDescriptor class are as follows:

  • String name(): Returns the name of the module.

  • Set<Modifier> modifiers(): Returns a set of the Modifier enum, which represents the module modifiers. A Modifier enum contains the following values: OPEN, AUTOMATIC, SYNTHETHIC, and MANDATE. Modifier.OPEN denotes an open module. Modifier.AUTOMATIC represents an automatic module. Modifier.SYNTHETIC specifies that the module wasn’t declared, either explicitly nor implicitly. Modifier.MANDATED states that the module was implicitly declared.

  • boolean isOpen(): Returns true if the module is open and false otherwise.

  • boolean isAutomatic(): Returns true if this is an automatic module and false otherwise.

  • Set<Requires> requires(): Returns a set of Requires objects that denote the dependencies of the module.

  • Set<Exports> exports(): Returns a set of Exports objects that stand for the exported packages of the module.

  • Set<Opens> opens(): Returns a set of Opens objects that represent the open packages.

  • Set<String> uses(): Returns a set of Strings that represent the service dependencies of the module.

  • Set<Provides> provides(): Returns a set of Provides objects that expresses the services provided by the module.

  • Optional<Version> version(): Returns the version of the module.

  • String toNameAndVersion(): Returns the module name and the version formatted as <module_name>@<version>.

  • Optional<String> mainClass(): Returns the main class of the module.

  • Set<String> packages(): Returns a set of Strings that expresses the packages from the module.

The following sections focus on the nested classes of the ModuleDescriptor class.

The ModuleDescriptor.Requires Class

The ModuleDescriptor.Requires class, whose instance expresses a requires clause in a module descriptor, contains some attributes and methods, described next.

It contains an enum called Modifier with the following values:

  • TRANSITIVE: As stated in the official JDK 9 documentation, “this dependence causes any module which depends on current module to have an implicitly declared dependence on the module named by the requires.”

  • STATIC: This dependence is mandatory at compile-time but is optional at runtime.

  • SYNTHETIC: This dependence wasn’t declared in the module declaration.

  • MANDATED: This dependence was declared in the module declaration.

The methods defined by the ModuleDescriptor.Requires class are as follows:

  • Set<Modifier> modifiers(): Returns a set of Modifier objects

  • String name(): Returns the name of the module

  • Optional<Version> compiledVersion(): Returns a Version object representing the version of the module

  • Optional<String> rawCompiledVersion(): Returns a String representing the unparseable version of the module

The ModuleDescriptor.Exports Class

The ModuleDescriptor.Exports class can be instantiated to express an exports clause inside the module declaration. The class contains an enum called Modifier with the following values: SYNTHETIC and MANDATED.

Here are the methods defined by this class:

  • Set<Modifier> modifiers(): Returns a set of Modifier objects

  • boolean isQualified(): Returns true if the export is qualified or false otherwise

  • String source(): Returns a String representing the name of the package

  • Set<String> targets(): Returns a set representing the names of the modules to which the package is exported. If the export is unqualified, it returns an empty set.

The ModuleDescriptor.Opens Class

The ModuleDescriptor.Opens class can be instantiated to express an opens clause inside the module declaration. The class contains an enum called Modifier with the following values: Modifier.SYNTHETIC and Modifier.MANDATED. In this case, MANDATED means that the opens statement was declared in the module declaration, and SYNTHETIC means that the opens statement was not declared in the module declaration.

According to the JDK 9 API specification, the methods defined by this class are as follows:

  • Set<Modifier> modifiers(): Returns a set of Modifier objects.

  • boolean isQualified(): Returns true if it is a qualified opens operation and false otherwise. A qualified opens operation is characterized by the fact that the modules to which the package is opened are specified in the module declaration. An unqualified opens operation doesn’t specify any modules in the module declaration, which means that the package is opened to all the modules.

  • String source(): Returns the name of the package as String.

  • Set<String> targets(): Returns a set of String that represents the names of the modules to which the package is open, but for an unqualified opens it returns an empty set.

The ModuleDescriptor.Provides Class

The ModuleDescriptor.Provides class is, in a manner of speaking, the correspondent of the provides statement from the module-info.java file. To recap, the provides statement’s role is to define a service type. The syntax of the provides statement is like this: provides <interface_name> with <class_name>, where <class_name> represents the implementation class for the service type defined by the interface with the name <interface_name>.

The ModuleDescriptor.Provides class defines the following variables: String service and List<String> providers. This means we can create one instance of the ModuleDescriptor.Provides class to define a single service and one or more providers.

There are two methods defined by the ModuleDescriptor.Provides class:

  • String service(): This method returns the fully qualified class name of the service type.

  • List<String> providers(): This method returns a list of Strings representing the fully qualified class names of the providers or provider factories.

Further, we can define three provides statements inside our module descriptor:

module com.apress.myModule {
        provides ServiceType1 with package1.Class1;
        provides ServiceType1 with package1.Class2;
        provides ServiceType1 with package2.Class3;
}

Because we have only a single instance of the service type, we have only one instance of the class ModuleDescriptor.Provides. We can get all the names of the providers classes by calling the method providers() and the name of the service type by calling the method service().

The ModuleDescriptor.Version Class

The nested class ModuleDescriptor.Version represents the version of a module. The version of a module is used only for documentation, because the JPMS does not support versioning. The most used methods provided by the ModuleDescriptor.Version class are as follows:

  • Version parse(String): Parses the given String as a version String

  • int compareTo(Version version): Compares this module version to the given module version

The ModuleFinder Interface

According to the JDK 9 API specification, this interface “represents a finder of modules and is used to find methods during resolution or service binding.” Here are the methods contained in this interface:

  • Optional<ModuleReference> find(String moduleName): Finds and returns a ModuleReference object to a module whose name is passed as parameter

  • Set<ModuleReference> findAll(): Returns a set containing all the ModuleReference objects that can be located in the system

  • static ModuleFinder ofSystem(): Returns a ModuleFinder object that locates all the system modules from the Java runtime

  • static ModuleFinder of(Path… entries): Returns a ModuleFinder object that locates modules on the file system by searching a sequence of directories or packaged modules

The role of the ModuleFinder interface is to find modules. A ModuleFinder finds only a single module. It can’t find more than one module. If we search for modules inside directories, the ModuleFinder will retrieve only the first module found.

For instance, if we have two directories and want to find a module named myModule, we could first get the MethodFinder by passing the sequence of directories as an argument to the method of() of the ModuleFinder interface:

ModuleFinder moduleFinder = ModuleFinder.of(directoryA, directoryB);

We can call the find() method on the the ModuleFinder object by passing the name of the module we’re searching for:

Optional<ModuleReference> moduleReference = moduleFinder.find("myModule");

The find() method returns a ModuleReference object to the module with the name myModule.

From a ModuleReference object, we can derive a ModuleDescriptor object:

if(moduleReference.isPresent()) {
        ModuleDescriptor moduleDescriptor = moduleReference.get().descriptor();
}

The findAll() method returns a set of all the module references that can be located. Hence, we could then find all the modules located inside the two directories.

The ModuleReader Interface

The role of the ModuleReader interface is to help access the content of a module. As stated in the official JDK 9 API documentation, “a module reader is intended for cases where access to the resources in a module is required, regardless of whether the module has been loaded. A framework that scans a collection of packaged modules on the file system, for example, may use a module reader to access a specific resource in each module.”

The JDK 9 API specification defines a couple of methods for the ModuleReader interface. The most important methods defined by the ModuleReader interface are as follows:

  • Optional<URI> find(String resourceName): This method finds the resource identified by the name resourceName. It returns a URI object to the resource in the module. It can throw an I/O Exception if the module reader is closed.

  • Optional<InputStream> open(String resourceName): This method opens a resource with name resourceName. It returns an InputStream object to read the resource in the module.

  • Optional<ByteBuffer> read(String resourceName): This method reads the given resource and returns a ByteBuffer object that contains the contents of the resource.

  • Stream<String> list(): This method lists the contents of the module. It returns a Stream of String objects that represents the names of all the resources in the module. Like the find(resourceName) and open(resourceName) methods, it can throw an I/O Exception if the module reader is closed.

The following example uses what we’ve learned so far to read some information from a module. We will search inside the java.base module for all the implementation classes. Once we find them, we load them and then print their name and the name of their package.

Listing 9-1 shows the class ModuleReaderExample, which loads all the implementation classes from module java.base and then prints their name and their package name:

Listing 9-1. The Class ModuleReaderExample
package com.apress.apimodule;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReader;
import java.lang.module.ModuleReference;
import java.util.*;


public class ModuleReaderExample {

    public static void main(String[] args) {

        List<Class<?>> listClasses = getClassesByModuleName("java.base");

        for(Class<?> myClass : listClasses) {
            System.out.println("Name of the class is: " + myClass.getName());
            System.out.println("Name of the package is: " + myClass.getPackageName());
        }
    }


    private static List<Class<?>> getClassesByModuleName(String moduleName) {

        ModuleFinder finder = ModuleFinder.ofSystem();
        Optional<ModuleReference> optionalModuleReference = finder.find(moduleName);
        ModuleReference moduleReference = optionalModuleReference.get();


        try (ModuleReader moduleReader = moduleReference.open()) {
            return moduleReader.list()
                    .filter(name -> name.endsWith("Impl.class"))
                    .map(ModuleReaderExample::classLoadByFileName)
                    .collect(Collectors.toList());
        } catch(IOException ioException) {
            throw new UncheckedIOException(ioException);
        }
    }


    private static Class<?> classLoadByFileName(String classFileName) {

        ClassLoader classLoader = ModuleReaderExample.class.getClassLoader();
        String nameOfClass = classFileName.substring(0, classFileName.length() - ".class".length());


        try {
            nameOfClass = nameOfClass.replace('/','.');
            return classLoader.loadClass(nameOfClass);
        }
        catch (ClassNotFoundException classNotFoundException) {
            throw new UncheckedIOException(new IOException(classNotFoundException));
        }
    }


}

The method static Class<?> classLoadByFileName(String classFileName) is simple. It uses the ClassLoader object to load the given class and returns an instance of object Class. The method List<Class<?>> getClassesByModuleName(String moduleName) is interesting. It returns a list of Class objects that are located in the given module. First, we get an instance of the ModuleFinder by calling the method ofSystem(). This method returns a module finder that locates the system modules. Then we find a reference to a module with the name moduleName by calling the find(moduleName) method on the finder object:

Optional<ModuleReference> optionalModuleReference = finder.find(moduleName);
ModuleReference moduleReference = optionalModuleReference.get();

A ModuleReference object is a reference to the module’s content—in our case, module java.base. To open the module for reading, we call the method open() on the ModuleReference object. Thus we obtain an instance of a ModuleReader object:

ModuleReader moduleReader = moduleReference.open()

Next we call the list() method on the ModuleReader object and filter the results by searching only for the classes with names ending in Impl.class. In the end, we call the method classLoadByFileName() to get a Class object of the corresponding class. In the main method, we load all the implementation classes of the module java.base and print their names together with their package names.

The output is huge, so we’ll show only a small snippet of it here:

Name of the class is: sun.util.locale.provider.BreakIteratorProviderImpl
Name of the package is: sun.util.locale.provider
Name of the class is: java.lang.ProcessImpl
Name of the package is: java.lang
Name of the class is: java.util.jar.JavaUtilJarAccessImpl
Name of the package is: java.util.jar
...

Performing Operations on Modules

This section covers some operations we can perform on modules programmatically, such as getting the module of a class, accessing the resources of a module, searching for all the modules in the module path, or getting the module information,

Getting the Module of a Class

As we already learned in this chapter, a module at runtime is expressed by the Module class defined in package java.lang of the module java.base. The Module class can represent either a named or unnamed module. In order to return a Module object for our class called ModuleCore, we call the method getModule() of the class Class:

Class<ModuleCore> myClass = ModuleCore.class;
Module module = myClass.getModule();

This method returns a module that the class ModuleCore is a member of. If the class is in the unnamed module, the method getUnnamedModule() from ClassLoader.java is called.

Accessing Resources of a Module

Listing 9-2 shows how we can access resources of the module using the getResourceAsStream() method , which returns an InputStream object.

Listing 9-2. Accessing Resources of a Module Using Method getResourceAsStream()
this.class.getModule().getResourceAsStream("file.properties");

We can also access resources of a module using the getResource() method, which returns a URL object, as in Listing 9-3.

Listing 9-3. Accessing Resources of a Module Using Method getResource()
this.getClass().getResource("file.properties")
ClassLoader.getPlatformClassLoader().getResource("file.properties")

Searching for all Modules in the Module Path

Using the new module API, we can even find all the modules in the module path . Listing 9-4 shows how to search for all the modules in the module path in the system environment variable jdk.module.path, get their module descriptors, and print their module names.

Listing 9-4. Searching for All the Modules in the Module Path
ModuleFinder.of(Paths.get(System.getProperty("jdk.module.path"))).
        .findAll()
        .stream()
        .forEach(ref -> {
                System.out.println(moduleReference.descriptor().name());
});

Getting Module Information

Using the methods and classes described throughout this chapter, we can get complete information about a module. Listing 9-5 shows an example where we make use of the new classes and interfaces in order to get all the available information from the module java.base.

Listing 9-5. Print Extensive Information from Module java.base
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReference;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Set;


public class BaseModule {

    public static void main(String[] args) {

        String moduleName;
        Optional<String> mainClass;
        Set<ModuleDescriptor.Exports> exports;
        boolean isAutomatic;
        boolean isOpen;
        Set<String> allPackagesNames;
        Set<ModuleDescriptor.Provides> provides;
        Set<ModuleDescriptor.Requires> dependencies;
        String moduleNameVersion;
        Set<String> serviceDependencies;
        ModuleDescriptor.Version version;


        ModuleFinder finder = ModuleFinder.ofSystem();
        Optional<ModuleReference> moduleReference = finder.find("java.base");


        if(moduleReference.isPresent()) {
            ModuleDescriptor moduleDescriptor = moduleReference.get().descriptor();


            // get the name of the module
            moduleName = moduleDescriptor.name();


            // get the module's main class
            mainClass = moduleDescriptor.mainClass();
            exports = moduleDescriptor.exports();
            isAutomatic = moduleDescriptor.isAutomatic();
            isOpen = moduleDescriptor.isOpen();
            allPackagesNames = moduleDescriptor.packages();
            provides = moduleDescriptor.provides();
            dependencies = moduleDescriptor.requires();
            moduleNameVersion = moduleDescriptor.toNameAndVersion();
            serviceDependencies = moduleDescriptor.uses();


            try {
                Optional<ModuleDescriptor.Version> versionOptional = moduleDescriptor.version();
                version = versionOptional.get();
            }
            catch (NoSuchElementException exception) {
                version = null;
            }


            System.out.println("Module name is: " + moduleName);
            System.out.println();


            System.out.println("Main class is: ");
            if(mainClass.isPresent())  {
                System.out.println(mainClass);
            }
            else {
                System.out.println("Not exists");
            }
            System.out.println();


            System.out.println("The module exports the packages with the following name: ");
            for(ModuleDescriptor.Exports moduleExport : exports) {
                System.out.print(moduleExport.source());
                System.out.print(", ");
            }


            System.out.println();
            System.out.println();
            System.out.println("Is an automatic module: " + isAutomatic);


            System.out.println();
            System.out.println("Is an open module: " + isOpen);


            System.out.println();
            System.out.println("All packages names: ");
            for(String packageName : allPackagesNames) {
                System.out.print(packageName);
                System.out.print(", ");
            }


            System.out.println();
            System.out.println();
            System.out.println("The services provided by the module: ");
            for(ModuleDescriptor.Provides provide : provides) {
                System.out.print("Service " + provide.service());
                for(String p : provide.providers()) {
                    System.out.print(" with providers: " + p);
                    System.out.print(", ");
                }
            }

            System.out.println();
            System.out.println("The name of the dependencies of the module: ");
            for(ModuleDescriptor.Requires dependency : dependencies) {
                System.out.print(dependency.name());
                System.out.print(", ");
            }


            System.out.println();
            System.out.println("Module name and version: " + moduleNameVersion);


            System.out.println();
            System.out.println("The service dependencies of the module: ");
            for(String serviceDependency : serviceDependencies) {
                System.out.print(serviceDependency);
                System.out.print(", ");
            }


            System.out.println();
            System.out.println("The version of the module: " + version);
        }
    }
}

We use the interface ModuleFinder to locate all the system modules. On the resulting object, we call the find() method and pass the string "java.base" representing the module’s name. This will return a ModuleReference to the java.base module. We verify whether the ModuleReference was found using the isPresent() method. Further, we call the descriptor() method on the ModuleReference object in order to get the module descriptor. The ModuleDescriptor object contains comprehensive information about a module, such as its name, its main class, its packages, its dependencies, its service dependencies, its provided services, its version name, and so on. We retrieve this information and print it. Listing 9-6 shows only the most important parts of the output.

Listing 9-6. Output After Running the Preceding Code that Prints the Information Regarding Module java.base
Module name is: java.base

Main class is:
Not exists


The module exports the packages with the following name:
jdk.internal.module, javax.net.ssl, java.time.format, java.nio.charset.spi, sun.security.ssl, sun.security.pkcs, sun.security.internal.interfaces, jdk.internal.util.jar, java.security.interfaces, sun.util.logging, jdk.internal.perf, java.util.function, sun.net.util, jdk.internal.misc, javax.security.auth.login, sun.security.x509, sun.security.rsa, jdk.internal.util.xml, jdk.internal, java.util.jar, java.util.regex, sun.security.action, jdk.internal.jmod, java.util.stream,
.........................................................


Is an automatic module: false

Is an open module: false

All packages names:
jdk.internal.org.objectweb.asm.signature, sun.text.bidi, sun.text.normalizer, sun.security.action, sun.util.logging, sun.security.internal.interfaces, jdk.internal.jimage.decompressor, jdk.internal.util.jar, java.net.spi, sun.reflect.generics.factory, sun.util.resources.cldr, sun.security.tools, com.sun.java.util.jar.pack, java.text.spi, java.nio, jdk.internal.ref, sun.security.tools.keytool, java.security.spec, sun.security.util, java.nio.channels.spi, sun.net.www.protocol.ftp, java.util, sun.util.cldr, sun.reflect.generics.reflectiveObjects, java.util.spi, java.lang.ref,
..............................


The services provided by the module:
Service java.nio.file.spi.FileSystemProvider with providers: jdk.internal.jrtfs.JrtFileSystemProvider,
The name of the dependencies of the module:


Module name and version: java.base@9

The service dependencies of the module:
java.util.spi.LocaleNameProvider, jdk.internal.logger.DefaultLoggerFinder, java.lang.System$LoggerFinder, sun.util.resources.LocaleData$SupplementaryResourceBundleProvider, java.text.spi.NumberFormatProvider, java.time.chrono.Chronology, java.util.spi.CalendarNameProvider, java.text.spi.DateFormatSymbolsProvider, java.time.zone.ZoneRulesProvider, sun.text.spi.JavaTimeDateTimePatternProvider, java.text.spi.DecimalFormatSymbolsProvider
..........................


The version of the module: 9
Note

The source code for the previous example can be found in the folder /ch09/moduleDescriptorJavaBase.

Summary

This chapter discussed the new module API introduced in Java 9, which gives us the means to access modules and the information inside modules.

You learned what kind of classes, interfaces, enums, and exceptions are contained inside the new module API. We talked about the java.lang.Module class together with its attributes, constructors, and methods. Next we showed how the java.lang.Class class has been enhanced with three useful methods. The new ModuleDescriptor class was also covered in detail. We explained its attributes and methods, but also its nested classes like ModuleDescriptor.Requires, ModuleDescriptor.Exports, ModuleDescriptor.Opens, ModuleDescriptor.Provides, and ModuleDescriptor.Version. We talked about the new ModuleReader and ModuleFinder interfaces and showed an example of how we can read the contents of a module. With the help of the ModuleFinder interface, we searched for all the implementation classes of the java.base module. Once we found them, we loaded them using the loadClass() method of the ClassLoader class. Then we displayed the names of the classes loaded together with the names of their packages.

Afterwards, we saw some examples of performing operations on modules, such as getting the module of a class, accessing the resources of a module, or searching for all the modules in the module path. The chapter concluded by discussing a Java class that reads all the properties from the module java.base and prints them at the system console.

Chapter 10 will cover some advanced topics related to Jigsaw, including layers, class loaders, multi-release JAR files, JMOD files and upgradeable modules. The layers are also part of the Module API.

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

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