Java 9 – The Java Platform Module System (JPMS) Part 2 | Code Factory


Index Page : Link

Donate : Link

Medium Blog : Link

Applications : Link

JPMS vs NoClassDefFoundError:

In Java 9 Platform Modular System, JVM will check all dependencies at the beginning only. If any dependent module is missing then JVM won’t start its execution. Hence there is no chance of NoClassDefFoundError in the middle of Program execution.

Demo Program:

Components of moduleA:

A.java

package pack1;
public class A
{
  public void m1()
  {
    System.out.println("Module m1() of moduleA");
  }
}

module-info.java

module moduleA
{
  exports pack1;
}

Components of moduleB:

B.java

package pack2;
import pack1.A;

public class B
{
  public void m2()
  {
    System.out.println("moduleB accessing members of moduleA");
    A a = new A();
    a.m1();
  }
}

module-info.java

module moduleB
{
  requires moduleA;
  exports pack2;
}

Components of moduleC:

Test.java

package pack3;
import pack2.B;

public class Test
{
  public static void main (String... args)
  {
    System.out.println("main method");
    B b = new B();
    b.m2();
  }
}

module-info.java

module moduleC
{
  requires moduleB;
}

Compilation and Execution:

D:\CodeFactory>javac --module-source-path src -d out -m moduleA,moduleB,moduleC

D:\CodeFactory>java --module-path out -m moduleC/pack3.Test
main method
moduleB accessing members of moduleA
Module m1() of moduleA

If we delete compiled code of module (inside out folder), then JVM will raise error at the beginning only and JVM won’t start program execution.

D:\CodeFactory>java --module-path out -m moduleC/pack3.Test
Error occurred during initialization of boot layer
java.lang.module.FindException: Module moduleC not found

But in Non Modular programming, JVM will start execution and in the middle, it will raise NoClassDefFoundError.

Hence in Java Platform Module System, there is no chance of getting NoClassDefFoundError in the middle of program execution.

Transitive Dependencies (requires with transitive Keyword):

A -> B, B -> C ==> A -> C
This property in mathematics is called Transitive Property.

Student1 requires Material, only for himself, if any other person asking he won’t share.

module student1
{
  requires material;
}

Student1 requires material not only for himself, if any other person asking him, he will share it.

module student1
{
  requires transitive material;
}

Sometimes module requires the components of some other module not only for itself and for the modules that requires that module also. For this requirement we can use transitive keyword.

The transitive keyword says that “Whatever I have will be given to a module that asks me.”

Case 1:

module moduleA
{
  exports pack1;
}

module moduleB
{
  requires moduleA;
}

module moduleC
{
  requires moduleB;
}

In this case only moduleB is available to moduleC and moduleA is not available. Hence moduleC cannot use the members of moduleA directly.

Case 2:

module moduleA
{
  exports pack1;
}

module moduleB
{
  requires transitive moduleA;
}

module moduleC
{
  requires moduleB;
}

In this both moduleB and moduleA are available to moduleC. Now moduleC can use members of both modules directly.

Case Study:

Assume Modules C1, C2,….C10 requires Module B and Module B requires A.

If we write “requires transitive A” inside module B

module B
{
  requires transitive A;
}

Then module A is by default available to C1, C2,.., C10 automatically. Inside every module of C, we are not required to use “requires A” explicitly. Hence transitive keyword promotes code reusability.

Note: Transitive means implied readability i.e., Readability will be continues to the next level.

Demo Program for transitive keyword:

moduleA components:

A.java

package pack1;
public class A
{
  public void m1()
  {
    System.out.println("A.m1() of moduleA");
  }
}

module.info.java

module moduleA
{
  exports pack1;
}

moduleB components:

B.java

package pack2;
import pack1.A;

public class B
{
  public A m2()
  {
    System.out.println("B.m2() of moduleB");
    return new A();
  }
}

module-info.java

module moduleB
{
  requires transitive moduleA;
  exports pack2;
}

moduleC components:

Test.java

package pack3;
import pack2.B;

public class Test
{
  public static void main (String... args)
  {
    System.out.println("Test class main method");
    B b = new B();
    b.m2().m1();
  }
}

module-info.java

module moduleC
{
  requires moduleB;
}
D:\CodeFactory>javac --module-source-path src -d out -m moduleA,moduleB,moduleC

D:\CodeFactory>java --module-path out -m moduleC/pack3.Test
Test class main method
B.m2() of moduleB
A.m1() of moduleA

In the above program if we are not using transitive keyword then we will get compile time error
because moduleA is not available to moduleC.

D:\CodeFactory>javac --module-source-path src -d out -m moduleA,moduleB,moduleC
src\moduleC\pack3\Test.java:10: error: A.m1() in package pack1 is not accessible
    b.m2().m1();
          ^
  (package pack1 is declared in module moduleA, but module moduleC does not read it)
1 error

Optional Dependencies (Requires Directive with static keyword):

If Dependent Module should be available at compile time but optional at runtime, then such type of dependency is called Otional Dependency. We can specify optional dependency by using static keyword.

Syntax: requires static <modulename>

The static keyword is used to say that, “This dependency check is mandatory at compile time and optional at runtime.”

Eg1:

module moduleB
{
  requires moduleA;
}

moduleA should be available at the time of compilation and runtime. It is not optional dependency.

Eg2:

module moduleB
{
  requires static moduleA;
}

At the time of compilation moduleA should be available, but at runtime it is optional. i.e., at runtime even moduleA is not available JVM will execute code.

Demo Program for Optional Dependency:

moduleA components:

A.java

package pack1;
public class A
{
  public void m1()
  {
    System.out.println("A.m1() of moduleA");
  }
}

module-info.java

module moduleA
{
  exports pack1;
}

moduleB components:

Test.java

package pack2;

public class Test
{
  public static void main (String... args)
  {
    System.out.println("Optional dependencies demo!");
  }
}

module-info.java

module moduleB
{
  requires static moduleA;
}

At the time of compilation both modules should be available. But at runtime, we can run moduleB Test class, even moduleA compiled classes are not available i.e., moduleB having optional dependency with moduleA.

D:\CodeFactory>javac --module-source-path src -d out -m moduleA,moduleB

D:\CodeFactory>java --module-path out -m moduleB/pack2.Test
Optional dependencies demo!

If we remove static keyword and at runtime if we delete compiled classes of moduleA, then we will get error.

// remove static from /src/moduleB/module-info.java

D:\CodeFactory>javac --module-source-path src -d out -m moduleA,moduleB

// delete /out/moduleA folder

D:\CodeFactory>java --module-path out -m moduleB/pack2.Test
Error occurred during initialization of boot layer
java.lang.module.FindException: Module moduleA not found, required by moduleB

Use cases of Optional Dependencies:

Usage of optional dependencies is very common in Programming world.

Sometimes we can develop library with optional dependencies.

  1. If apache http Client is available use it, otherwise use HttpURLConnection.
  2. : If oracle module is available use it, otherwise use mysql module.

Why we should do this? For various reasons –

  1. When distributing a library and we may not want to force a big dependency to the client.
  2. On the other hand, a more advanced library may have performance benefits, so whatever module client needs, he can use.
  3. We may want to allow easily pluggable implementations of some functionality. We may provide implementations using all of these, and pick the one whose dependency is found.

Cyclic Dependencies:

If moduleA depends on moduleB and moduleB depends on moduleA, such type of dependency is called cyclic dependency.

Cyclic Dependencies between the modules are not allowed in java 9.

Demo Program:

moduleA components:

module-info.java

module moduleA
{
  requires moduleB;
}

moduleB components:

module moduleB
{
  requires moduleA;
}
D:\CodeFactory>javac --module-source-path src -d out -m moduleA,moduleB
src\moduleA\module-info.java:3: error: cyclic dependence involving moduleB
  requires moduleB;
           ^
1 error

There may be a chance of cyclic dependency between more than 2 modules also.

moduleA requires moduleB
moduleB requires moduleC
moduleC requires moduleA

Note: In all predefined modules also, there is no chance of cyclic dependency

Qualified Exports:

Sometimes a module can export its package to specific module instead of every module. Then the specified module only can access. Such type of exports are called Qualified Exports.

Syntax:
exports <pack1> to <module1>,<module2>,…

Eg:

module moduleA
{
  exports pack1; // to export pack1 to all modules
  exports pack1 to moduleB; // to export pack1 only for moduleB
  exports pack1 to moduleB,moduleC; // to export pack1 for moduleB,moduleC
}

Demo Program for Qualified Exports:

Components of exportermodule:

A.java:

package pack1;

public class A
{
}

B.java

package pack2;

public class B
{
}

C.java

package pack3;

public class C
{
}

module-info.java

module exportermodule
{
  exports pack1;
  exports pack2 to moduleA;
  exports pack3 to moduleA,moduleB;
}

Components of moduleA:

Test.java

package packA;
import pack1.A;
import pack2.B;
import pack3.C;

public class Test
{
  public static void main(String... args)
  {
    System.out.println("Qualified exports demo");
  }
}

module-info.java

module moduleA
{
  requires exportermodule;
}

Explanation:

For moduleA, all 3 packages are available. Hence we can compile and run moduleA successfully.

D:\CodeFactory>javac --module-source-path src -d out -m exportermodule,moduleA

D:\CodeFactory>java --module-path out -m moduleA/packA.Test
Qualified exports demo

Components of moduleB:

Test.java

package packB;
import pack1.A;
import pack2.B;
import pack3.C;

public class Test
{
  public static void main (String... args)
  {
    System.out.println("Qualified exports demo");
  }
}

module-info.java

module moduleB
{
  requires exportermodule
}

Explanation:

For moduleB, only pack1 and pack3 are available. pack2 is not available. But in moduleB we are trying to access pack2 and hence we will get compile time error.


D:\CodeFactory>javac --module-source-path src -d out -m exportermodule,moduleB
src\moduleB\packB\Test.java:3: error: cannot find symbol
import pack2.B;
            ^
  symbol:   class B
  location: package pack2
1 error

Q. Which of the following directives are valid inside module-info.java:

  1. requires moduleA;
  2. requires moduleA,moduleB;
  3. requires moduleA.pack1;
  4. requires moduleA.pack1.A;
  5. requires static moduleA;
  6. requires transitive moduleA;
  7. exports pack1;
  8. exports pack1,pack2;
  9. exports moduleA;
  10. exports moduleA.pack1.A;
  11. exports pack1 to moduleA;
  12. exports pack1 to moduleA,moduleB;

Answers: 1,5,6,7,11,12

Leave a comment