JavaData & JavaExploring the Module APIs in Java 9

Exploring the Module APIs in Java 9

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Java 9 incorporated the API in a collection of modules. Therefore, modularity is the central theme; this affected program design from the top level. Programs can be built modular right from the start. It is no surprise that there will be APIs to deal specifically with the programming element called a module. The APIs provide a way to access modules programmatically. These APIs comes quite handy to get specific information about the modules or to read or manipulate them. This article explores the Module APIs classes and some of the methods, with examples to give you an idea of their overall functionality.

An Overview

Java 9 provides a set of classes and interfaces to deal with module programmatically. These APIs are particularly useful for:

  • Reading, loading, and searching modules
  • Reading and manipulating module descriptors

The list of APIs is encompassed mainly within packages: java.lang and java.lang.module. Although the java.lang.module package consists of most of the classes and interfaces to deal with module descriptors, the java.lang package contain classes Module, ModuleLayer, and an exception, LayerInstantiationException. Among these three, the Module class is of primary importance because an instance of this class provides all the methods connected to reading, loading, and searching modules. The most important class in the java.lang.module package is the ModuleDescriptor. This class provides the necessary methods to deal with module descriptors.

Modules API

According to Java API Documentation, the module class represents both named and unnamed run-time modules. Named modules have a name and are constructed by the Java Virtual Machine when a graph of modules is defined to the Java virtual machine to create a module layer. An unnamed module does not have a name. There is an unnamed module for each ClassLoader, obtained by invoking its getUnnamedModule method. All types that are not in a named module are members of their defining class loader’s unnamed module.

It is simple to find out the module of a class to which it belongs. For example, if we want to find out the module of a class, say, ArrayList, from the Collection API or, say, Application from JavaFX, we may do so in the following manner.

Class<ArrayList> c= ArrayList.class;
Module mod=c.getModule();
System.out.println(mod.getName());

Or, in a single statement, as follows:

System.out.println(Application.class
   .getModule().getName());

This prints the module name of the class, such as java.base, for Arraylist, and javafx.graphics for Application. Because a module can be either named of unnamed, we can find out by invoking the isNamed() method. This method returns true if the module is named or false if it is an unnamed module. Here is an example of unnamed modules.

package org.mano.java9.examples;
public class Main {
   public static void main(String[] args) {
      Class<Main> c= Main.class;
      Module mod=c.getModule();
      System.out.println(mod);
      System.out.println(mod.getName());
      System.out.println(mod.getName()+" is "
         +(mod.isNamed()?
         "Named Module":"Unnamed Module"));
      System.out.println(mod.getDescriptor());
   }
}

Output:

unnamed module @4c75cab9
null
null is Unnamed Module
null

And, for named modules, we may write as follows:

package org.mano.java9.examples;
import java.util.ArrayList;
public class Main {
   public static void main(String[] args) {
      Class<ArrayList> c= ArrayList.class;
      Module mod=c.getModule();<
      System.out.println(mod);
      System.out.println(mod.getName());
      System.out.println(mod.getName()+" is "
         +(mod.isNamed()?
         "Named Module":"Unnamed Module"));
      System.out.println(mod.getDescriptor());
   }
}

Output:

module java.base
java.base
java.base is Named Module
module { name: java.base@9.0.1, uses:
   [java.nio.file.spi.FileTypeDetector, ...}

A ModuleLayer contains only named modules. We can invoke the getLayer method to get the information about the layer it contains in the module. If it returns null, it means that the module is not in a layer, or it is an unnamed module. If we want to get a list of packages available in a module, we can invoke the getPackages method. The getClassLoader method returns the module class loader. Here is an example to illustrate the methods described above.

package org.app.module1;
import javafx.application.Application;
import java.util.Set;
public class Main {
   public static void main(String[] args) {
      Class<Application> c = Application.class;
      Module mod = c.getModule();
      System.out.println("Name :" 
         + mod.getName());
      System.out.println(mod.getName() + " is " 
         + (mod.isNamed() ? "Named Module" :
         "Unnamed Module"));
      System.out.println("Layer :" + mod.getLayer());
      System.out.println("ClassLoader :"
         + mod.getClassLoader());
      System.out.println("List of
         Packagesn.....................");
      Set<String> set = mod.getPackages();
      int i=1;
      for (String s : set) {
         System.out.println(i+++") "+s);
      }
   }
}

Output:

Name :javafx.graphics
javafx.graphics is Named Module
Layer :jdk.compiler, java.compiler, jdk.management.jfr,
   jdk.scripting.nashorn, ...
ClassLoader :jdk.internal.loader.ClassLoaders
   $PlatformClassLoader@880ec60
....................
List of Packages
.....................
1) com.sun.javafx.stage
2) com.sun.scenario.effect.impl.prism.ps
3) javafx.print
...
107) com.sun.prism.j2d
108) javafx.scene.image

Module Description

According to Java 9 API Documentation, “A module descriptor describes a named module and defines methods to obtain each of its components.” The module descriptor for a named module in the Java virtual machine is obtained by invoking the Module‘s getDescriptor method. Module descriptors also can be created by using the ModuleDescriptor.Builder class or by reading the binary form of a module declaration (module-info.class) using the read methods defined in this class.

Therefore, typically the ModuleDescriptor instance represents the module definition found in the binary form of the module descriptor file, called module-info.class. Apart from reading and manipulating module definition, we can use the ModuleDescriptor.Builder class to describe the module at run time.

A module descriptor describes three types of module, such as normal, open, and automatic modules.

A normal and an open module explicitly describe the services they provide or use, dependencies, exported packages and other components. The main difference between a normal module and an open module is that Normal modules may open specific packages. The module descriptor for an open module does not declare any open packages (its opens method returns an empty set), but when instantiated in the Java virtual machine, it is treated as if all packages are open.

The automatic module, however, does not declare any exported, open packages, or dependencies except implicit declaration of the java.base module. When an automatic module is instantiated in the Java virtual machine, it reads every unnamed module and is treated as if all packages are exported and open.

The getDescriptor method of the Module class returns an instance of the ModuleDescriptor class. The ModuleDescriptor class contains static nested classes whose instance represents the directive statement in the module declaration file. The class, however, does not contain uses statement which typically can be represented by a service instance String. Here are those other four:

  • ModuleDescriptor.Requires
  • ModuleDescriptor.Opens
  • ModuleDescriptor.Provides
  • ModuleDescriptor.Exports

A Quick Example

package org.mano.java9.examples;
import javax.sql.RowSet;
import java.lang.module.ModuleDescriptor;
import java.util.List;
public class Main {
   public static void main(String[] args) {
      System.out.println("Module Name: "
         + List.class.getModule().getName());
      show(List.class.getModule().getDescriptor());
      System.out.println("Module Name: "
         + RowSet.class.getModule().getName());
      show(RowSet.class.getModule().getDescriptor());
   }
   public static void show(ModuleDescriptor d) {
      System.out.println("Module
         Descriptionn-------------------------");
      System.out.println("Requires: " + d.requires());
      System.out.println("Exports: " + d.exports());
      System.out.println("Uses: " + d.uses());
      System.out.println("Provides: " + d.provides());
      System.out.println("Packages: " + d.packages());
   }
}

Output:

Module Name: java.base
Module Description
-------------------------
Requires: []
Exports: [jdk.internal.org.objectweb.asm.signature to
   [jdk.scripting.nashorn], ...]
Uses: [java.util.spi.LocaleNameProvider,
   java.nio.file.spi.FileSystemProvider, ...]
Provides: [java.nio.file.spi.FileSystemProvider with
   [jdk.internal.jrtfs.JrtFileSystemProvider]]
Packages: [java.nio.file, jdk.internal.org.objectweb.asm
   .tree.analysis, com.sun.security.ntlm, ...]
Module Name: java.sql
Module Description
-------------------------
Requires: [mandated java.base, transitive java.logging,
   transitive java.xml]
Exports: [java.sql, javax.transaction.xa, javax.sql]
Uses: [java.sql.Driver]
Provides: []
Packages: [javax.sql, javax.transaction.xa, java.sql]

The module descriptor binary file, called module-info.class, can be directly read in the following manner to create an instance of the ModuleDescriptor class:

try {
   ModuleDescriptor descriptor = ModuleDescriptor
      .read(new FileInputStream("module-info.class"));
} catch (IOException ex) { }

There are four versions of the overloaded static read method defined in the ModuleDescriptor class. They are used to read the binary form of the module description from an input stream or a byte buffer. Here is the extraction from Java 9 API documentation.

  • read(InputStream in): Reads the binary form of a module declaration from an input stream as a module descriptor.
  • read(InputStream in, Supplier <Set<String>> packageFinder): Reads the binary form of a module declaration from an input stream as a module descriptor.
  • read(ByteBuffer bb): Reads the binary form of a module declaration from a byte buffer as a module descriptor.
  • read(ByteBuffer bb, Supplier <Set<String>> packageFinder): Reads the binary form of a module declaration from a byte buffer as a module descriptor.

The set packages by packageFinder include all the packages that the module exports, opens, services provided, and the package of the main class that are not encoded by the descriptor supplied in the input stream or byte buffer.

Conclusion

Apart from reading basic information about the module, the Module class provides some key methods to enquire about the module status—whether it be read, open, exported, and so forth. The API also provides methods such as addOpens, addExport, addUses, and addReads to add open and export uses and read statements to the modules descriptor programmatically. In a nutshell, the Module API provides many other methods to specifically deal with modules programmatically. Here, we have just scratched the surface to give an initial idea of what’s it all about.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories