Handling interdependencies in maven multi-module project - java

I'm been going through the Maven Framework. While going through multi-module projects, I read that cyclic dependency is not acceptable among the modules.
So I thought of a scenario, something like...
root ----------
- pom.xml |
|
|--- moduleA
| - pom.xml (moduleB has been added as a dependancy)
|
|--- moduleB
- pom.xml
Assume that moduleA has a property class AppProperty and a Main class which invokes another class B available in moduleB
Main class available in moduleA :-
someValue = AppProperty.get(propKey);
//some logic
B mb = new B();
B.process(...);
Class B of moduleB :-
process(...) {
someOtherValue = AppProperty.get(someKey)
// some other logic
}
Now Main will not throw any compile-time errors as its dependancies have been resolved because moduleB has been added as a dependancy in moduleA'a pom.xml. But for class B that is not the case as its invoking AppProperty class which is available in moduleA only. I cannot add moduleA's dependancy in moduleB's pom as that would lead to cyclic dependancy (if I understand it correctly).
I understand that ideally it is advised to maintain codes in an acyclic manner, but what if because of some reason removing cyclic dependancy is just not feasible? In such a scenario, is there any way to handle cyclic dependancies without actively changing the existing code logic?

You cannot build a project with cyclic dependencies. You need to build B before A before B, which is kind of a contradiction.
But problems like yours are easy to solve:
If the problem is just the class AppProperty or a few others, just move them from A to B.
If you have some common classes for A and B, create a helper module C and use it as dependency in A and B.
If A and B call each other all of the time, they should probably be just one module. So merge them to one module.

Related

How to export dependencies whole?

I have a library B that depends on another library A.
A third library C depends on B and A.
I can satisfy C's dependencies with
dependencies {
implementation files('/path/A.jar')
implementation files('/path/B.jar')
}
but I would rather only declare B and build B.jar in such a way that it contains and exposes A as well.
I know that with api files('/path/A.jar') B can expose the parts of A that it uses in interfaces, but (my experience is that) it doesn't let consuming projects import anything from A explicitly.
How can B expose A completely?
files() is only for local flat file use, there are 2 mechanisms to share transitive dependencies ...
project(':other') (project dependency)
'group:artifact:version' (maven repository dependency)
https://docs.gradle.org/current/userguide/declaring_repositories.html#sec:repository-types
if the source for B & A is available locally & can be built along with C ... then declaring like below is possible
implementation project(':A')
implementation project(':B')
else you go with proper maven repository artefact.

Including dependency from imported maven library

A little fuzzy on Gradle/maven, but generally here is the idea.
I have a web application that uses a common library (A) as a dependency with source implemented under com.mydomain.utils package. There is another legacy package (B) written under a different namespace, com.mydomain.legacy, that I would like included within A, such that when I include A as a dependency in my primary application, library B's resources can be resolved as normal:
import com.mydomain.legacy.someutility
If B is a dependency of A, and A is a maven artifact, then B is already included in A, otherwise, it would not be possible to build A.
If B is not a dependency of A, then you need to list both A and B as dependencies of your project.

How to solve circular dependency in gradle multi-project build

Consider the following situation. I have two gradle (sub-)projects called "A" and "B". A defines some classes/interfaces that are being referenced by B. So B has a compile dependency to A. Now A is a web server that should be started with B on the classpath. How do you achieve that with gradle?
Of course it is not possible to add B as compile dependency to A because that would mean a circular dependency between A and B. Even adding B as runtime dependency to A did not work because then compile errors in B state that referenced classes from A do not exist. But why?
One solution would be to move code from B into A but I really would like to separate that code because there might be another implementation of B later that I want to swap easily in A (e.g. by exchanging the jar in runtime classpath).
Another solution I was thinking about is to separate classes from A referenced by B into a new module and make both A and B depend on that new module. This sounds valid but that would imply to move persistence layer from A to that new module which feels wrong.
Additional information: A is a Spring boot web application with persistence layer, web services etc, B produces a JAR.
Circular dependencies are a well-known problem when you try to get Dependency Injection. In this case, you have something similar but at a module level
The only way I see you can solve your issue is by creating a third module C with the common code (probably the A interfaces referenced by B)
This way you can compile C (it doesn't have any dependencies), A (it depends on C), and B (it depends on C) and launch A with B in its classpath
Everytime you end up with circular dependency you probably should introduce another entity to break the cycle.
Have a look at my explanation in this other QA article (it's dealing with packages and classes, but idea is the same): What does it mean and how to fix SonarQube Java issue "Cycles between packages should be removed" (squid:CycleBetweenPackages)

How to find java class dependency tree

I am trying to pull a bunch of classes and packages from a large project and create a separate standalone module out of it. Now when I try to compile these classes, due to dependency on other classes, I end up with very large number of compiled classes which I don't intend to have in the standalone module.
e.g. if this is class dependency A -> B -> C -> D. And I compile A, I will end with A.class, B.class, C.class and D.class. I want to break the dependency on class D and refactor the code such that class D doesn't become part of the module. But for this to happen, I would have to know the dependency path(s) for given class A and class D.
I tried searching SO but without success so far.
For future stumble-upons: At least as of IntelliJ 2020.1, you can find the list of classes a class would need (or depend upon), recursively, by:
Right click on the class name
Click Analyze > Dependencies
Choose File .java
You can also specify the depth required
(For finding which classes depend on the class in question, choose Analyze > "Backward Dependencies", instead)
For other cases or alternatives: How do I get a list of Java class dependencies for a main class?

Inheritence conflict between java ant projects

I have two Java projects, built with ANT, named Project A and B which I created in Luna Eclipse (the Java EE version). The package structure is as follows:
Project A
|
src
|
SomePackage
|
A.java
Project B
|
src
|
AnotherPackage
| |
| B.java
|
SomeOtherPackageInSrc
|
C.java
where A, B, and C are non-abstract POJOs. I also have the following inheritence structure:
C extends B, B extends A.
I added a public method to A, so that its children could have it. I then built project A, and added the resulting JAR to the project B's Build Path. I noticed that C could not access the new method. I then attached the source JAR to the build path, viewed the the source for A.java, and the newly added method was present. I tried a number of things, and adding project A to the Deployment Assembly of project B allowed C to see the new method from A. Why does simply extending the class and adding the jar in which the extended class lives not provide visibility to public methods in this case?
I suppose, only extending and adding the project to your classpath will make it compile, but it is not automatically in the resulting deployment unit (EAR/WAR). ANd this makes the classes unavailable at runtime.
I found the problem. I had multiple versions of the Project A JAR on my classpath, and the compiler picked up the old JAR instead of the new one. Rookie mistake.

Categories

Resources