How to create Java Modules?

Java 9 introduced the Module system under the project name Project Jigsaw.

This altered the existing structure of Java projects.

Java projects can be now encapsulated into modules and each module can be separately packaged into a jar or a run time (customized JRE).

Until the module system came out , every Java project has to be packaged with all the classes provided in JRE whether the application needed them or not. This bloated the final application size.

To reduce this Java itself internally organized its entire code base into modules. You can use only the modules what you need in your application . By default java.base module will be imported into your modules which covers the base classes needed to create a java program.

And you can also export the modules which third party applications can make use of. Modules by default are not available to the public. You need to explicitly export them.

At the same time you need to explicitly mention which third party modules you need.

Both these are specified in a file named module-info.java. This file is the crux of the modular system in java.

Before diving into it , let’s see all the modules in JRE.

To know this type java –list-modules from the command prompt.

Here is the list of modules in my JRE:

C:\Users\Vijay>java --list-modules
java.base@14
java.compiler@14
java.datatransfer@14
java.desktop@14
java.instrument@14
java.logging@14
java.management@14
java.management.rmi@14
java.naming@14
java.net.http@14
java.prefs@14
java.rmi@14
java.scripting@14
java.se@14
java.security.jgss@14
java.security.sasl@14
java.smartcardio@14
java.sql@14
java.sql.rowset@14
java.transaction.xa@14
java.xml@14
java.xml.crypto@14
jdk.accessibility@14
jdk.aot@14
jdk.attach@14
jdk.charsets@14
jdk.compiler@14
jdk.crypto.cryptoki@14
jdk.crypto.ec@14
jdk.crypto.mscapi@14
jdk.dynalink@14
jdk.editpad@14
jdk.hotspot.agent@14
jdk.httpserver@14
jdk.incubator.foreign@14
jdk.incubator.jpackage@14
jdk.internal.ed@14
jdk.internal.jvmstat@14
jdk.internal.le@14
jdk.internal.opt@14
jdk.internal.vm.ci@14
jdk.internal.vm.compiler@14
jdk.internal.vm.compiler.management@14
jdk.jartool@14
jdk.javadoc@14
jdk.jcmd@14
jdk.jconsole@14
jdk.jdeps@14
jdk.jdi@14
jdk.jdwp.agent@14
jdk.jfr@14
jdk.jlink@14
jdk.jshell@14
jdk.jsobject@14
jdk.jstatd@14
jdk.localedata@14
jdk.management@14
jdk.management.agent@14
jdk.management.jfr@14
jdk.naming.dns@14
jdk.naming.rmi@14
jdk.net@14
jdk.nio.mapmode@14
jdk.rmic@14
jdk.scripting.nashorn@14
jdk.scripting.nashorn.shell@14
jdk.sctp@14
jdk.security.auth@14
jdk.security.jgss@14
jdk.unsupported@14
jdk.unsupported.desktop@14
jdk.xml.dom@14
jdk.zipfs@14

As earlier mentioned the first module java.base@14 is by default included in all modules you create. If you need any other module you need to explicitly import them in your module-info.java file.

Let’s create two modules to understand this concept.

One module is used to sell coconuts.

Another module buys coconuts from the first module.

The first module exports its API which sells coconuts.

And the second module imports the first module’s API.

The Project Structure:

I created a folder named moduledemo and created two subfolders :

  • src – this is where all the modules and their source code go into
  • modules – this is where the compiled modules go into

Inside the src folder I created two folders:

  • com.seller – this represents the seller module
  • org.buyer – this represents the buyer module

Inside com.seller ,

I created a module-info.java file and a SellCoconuts.java class.

The module-info.java file contains meta data about the module. This is where you export your packages and import packages from other modules.

In our case we are going to export the package which sells coconuts.

So here is the module-info.java file contents:

module com.seller{

  exports com.seller;

}

As you see the package com.seller is exported.

Here is the SellCoconuts.java class:

package com.seller;

public class SellCoconuts{


    public static String sellCoconuts(){


       return "Here they are , the coconuts you asked for (o o o o o o)";
	}

}

It has a static method which returns a string .

Now let’s look into the second module.

Inside org.buyer folder I created two files a module-info.java file and a BuyCoconuts.java file.

Here are the contents of module-info.java :

module org.buyer{

   requires com.seller;

}

As you see this module requires the package com.seller of the first module which includes the class SellCoconuts.java which sells coconuts.

Here are the contents of BuyCoconuts.java:

package org.buyer;
import com.seller.SellCoconuts;

public class BuyCoconuts{



public static void main(String a[]){


   System.out.println(SellCoconuts.sellCoconuts());
}

}

As you see we are calling the static method sellCoconuts() which belongs to the class in the first module.

Now let’s compile the modules.

First let’s compile the first module com.seller.

Here is the javac command:

javac -d modules\com.seller src\com\seller\module-info.java src\com\seller\SellCoconuts.java

In the above command,

modules\com.seller – destination path of build files for the first module

And the following arguments represent the two files in the first module.

I executed this from the root folder (moduledemo)

A new module build got created under modules folder

Now let’s compile the second module.

Since the second module has a dependency on another module , you need to specify the dependent module path while compiling. This can be specified using –module-path flag.

Here is the command:

javac --module-path modules -d modules\org.buyer src\org\buyer\module-info.java src\org\buyer\BuyCoconuts.java

The second module got built into modules folder.

Here are the two modules created in two folders:

Let’s run the second module now and buy coconuts from the first module.

Here is the command to be used:

java --module-path modules -m org.buyer/org.buyer.BuyCoconuts

You need to specify the path of the modules using –module-path flag and the main class which is to be executed using -m flag.

On executing the above command , I got the below output:

That’s it!

Now let’s see what happens if we don’t export the seller module and use it in buyer module.

I removed the export com.seller line of code from module-info.java of seller module:

module com.seller{



}

I compiled it and it compiled fine.

But when I compiled the second module , it threw the below error :

src\org\buyer\BuyCoconuts.java:3: error: package com.seller is not visible
import com.seller.SellCoconuts;
^
(package com.seller is declared in module com.seller, which does not export it)
1 error

seller module need to export itself for buyer module to import it.

Let me add the export statement in module-info.java of the seller module back.

And remove the requires com.seller statement from module-info.java of buyer module:

module com.seller{

  exports com.seller;

}
module org.buyer{



}

Again I compiled the first module and it compiled fine.

But when I compiled the second module it threw the below error:

src\org\buyer\BuyCoconuts.java:3: error: package com.seller is not visible
import com.seller.SellCoconuts;
^
(package com.seller is declared in module com.seller, but module org.buyer does not read it)
1 error

You need to explicitly import the seller module to use it!

That’s it.

We explored the basic principle of modular system in java.

Now let’s go one step further .

Let’s package each module into a jar . It is called a modular JAR since it has the file module-info.class at its root path.

I created a new folder named jars under the root path (moduledemo).

I put back the original contents of module-info.java of both the modules and compiled them.

And then I ran the below command from the root directory:

jar --create --file=jars\seller.jar -C modules\com.seller .

–create – command to create a file specified by –file flag

-C – change to the directory following this flag and include all the files following the directory (in our case “.” is specified following the directory modules\com.seller to include all files under it)

On running the command a new jar named sellers.jar got created under jars directory.

The same command can be used to create a jar file for the second module except that you need to specify the main class which needs to be run using –main-class flag :

jar --create --file=jars\buyer.jar --main-class=org.buyer.BuyCoconuts -C modules\org.buyer .

On running the above command , a new jar named buyer.jar got created in jars directory.

Now let’s run the second module jar using the below command:

java -p jars -m org.buyer

-p specifies the path of the module

-m specifies the module to be executed.

On running the above command the below output got displayed:

Here is the github url for the project:

https://github.com/vijaysrj/javamoduledemo

Modules have made java code shorter and more secure.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s