Debugging Java 9 module system | Cyclic Dependency : The need and an alternate approach

 The inception of the module system as a part of project Jigsaw has brought in lot of emotions amongst software developers and technology enthusiasts alike. In a way, Java's programming constructs have a fresh feel altogether. A new direction, which if excercised efficiently, will help in having a better view and understanding of the application. Modules pack together all dependencies, which a code would require throughout it's life cycle.

Java 9 enhances the jars to modular jars, wherein each jar file comes with its own package [module] information. By this, we can have a clearer picture of all dependencies and entire object graph at an early stage. 

Cyclic Dependency

Here we discuss the cyclic dependency and the concerns of use cases where we cannot avoid it at all. 
Referring to the below object graph, lets assume we have a module Mod1 (AllTimeRef) with interface Mention & class TechNotes & Mod2 (SecondConcern) with classes ColorNotes[which accesses Mention interface from Mod1] & SamePackageClass. Also, Mod1 accesses ClsOne(SamePackageClass) from Mod2 as well, causing the cyclic dependency to take place.

 

As the general opinion for modules with cyclic dependency expresses: that the modules reflecting cyclic dependency are effectively the same module and should be maintained likewise, because they display a tightly integrated nature. 

Sometimes, due to some unavoidable circumstances, owing to the categorization of business objects, larger concerns etc.; the classes might have to be maintained in separate modules 1 & 2. The primary dependency going in the direction from 1 -> 2. Yet, there might be an object in 1 which would require 2's ClsOne to complete its definition, causing the cycle.



Refactor & add a separate independent smaller module

We might not be able to conjoin the modules and a both-way dependency has to exist. We can strategically tweak the modular design by cutting out ClsOne and maintaining it in a separate module 3. Subsequently, we can have both 1 & 2 depend upon module 3, alongside there current set of dependencies. This breaks the cycle!
Now the step of cutting out ClsOne is very dynamic and will totally depend upon the application design and object layout. In some of the application designs, with a need of cyclic dependency, it might not be possible to fetchout the "cycle causing object" out in a separate module. Whereas, in others, we might be able to achieve this.


We can incorporate this approach for all the plausible cases. 


Configuration

We can conveniently configure the above approach in our projects. Please note the below configuration has been done in Eclipse Oxygen with JDK 9 using maven as build tool.


Assumptions
Lets name our modules as per below:

Mod 1: AllTimeRef(module: com.jvb.self.modone), Mod 2: SecondConcern(module: com.jvb.self.modtwo), Mod 3: ThirdConcern(module: com.jvb.self.modthree). 

The main class is in SecondConcern (com.jvb.self.modtwo.BlackBookMain). The main class will be calling artifacts from all three modules. 


The class which breaks the cyclic dependency: SamePackageClass is moved to the module ThirdConcern (com.jvb.self.modthree). Its accessed(required) by both AllTimeRef and SecondConcern[of which it was originally a part, hence dependent]



  •  Make sure SecondConcern's pom has AllTimeRef & ThirdConcern dependencies. Also true for AllTimeRef's pom.xml to have ThirdConcern dependency.
  • Ensure that maven-compiler-plugin has both goals(compile, testCompile) mentioned in the plugin management.                                              
  • To refer ThirdConcern in AllTimeRef, we should have a 'requires' for modthree in AllTimeRef's modone:              
  • To refer ThirdConcern & AllTimeRef in SecondConcern, we should have a 'requires' for both in SecondConcern's modtwo:                                      

















  • For the dependencies to be available at run time, its imperative that they should be present already. To achieve this with maven, we do the routine mvn clean install against the dependencies first, then for the referring module.

      modthree.SamePackageClass in AllTimeRef 's modone 
        modone & modthree in SecondConcern's modtwo

    • One we have dependencies in place, the following modules will run successfully and will get all relevant imports from different modules as well.
    • Running the maven command mvn exec:java -Dexec.mainClass="com.jvb.self.modtwo.BlackBookMain" will run our artifacts, import resources from all dependencies, break the cycle without really disturbing the entire object graph.