maven circular dependency + inheritance best practice - maven

I have a few projects in the following structure: Project A, B, C
They are all under a parent project D. in pom.xml under D folder:
<module>A</module>
<module>B</module>
<module>C</module>
The dependency among them is like : A<--B<--C.
And A,B,C are sharing lots of common dependencies.
1.What's the best practice to organize the dependencies in this case in order to make sub poms very light, also the duplicated dependencies are excluded?
2.What should be put into "dependencies" section and what should be put into "dependency-management" section of the parent pom?
3.common dependencies are more like:
log4j, slf4j, thrift, jetty, javax.servlet etc...
most of them are transitive dependencies other than directly required by my projects. Does it make sense put them in the parent D's pom.xml as dependencies, let the sub projects(A,B,C) to inherit them from D? I noticed that if you do so, mvn dependency:tree is going to give you warning like.
[WARNING] Unused declared dependencies found: log4j, slf4j ....
If that gives me warning, I assume it's something maven doesn't recommend u to do?

Most important: Only declare dependencies directly required by a module. It is difficult to suggest anything specific without knowing more about your modules and their dependencies. What are examples for common dependencies in your case? Maybe reorganizing your modules helps in reducing common dependencies. Depending on your project it might be reasonable to put all classes from your modules A, B and C that depend on the same artifact into a new module E.
In my opinion nothing should be put into the dependencies section in the parent pom (and seeing the warning, it seems maven feels the same ;-)). At least put all the versions of dependencies used in more than one module into the dependency-management section of your parent. This helps to always stay at the same version of a specific dependency over your whole project.
As in 2. you should only specify the version of these artifacts in your dependencies-management section in the parent and repeat them in each module that needs it without specifying version. As stated in 1. you should only specify the direct dependencies. For example if you are using slf4j you should only need log4j in the module containing your main class.
To sum up:
Declare only direct dependencies of a module, transitive dependencies are handled automatically by maven (this is one of the main reasons to use maven at all).
Only declare needed dependencies: For example in the case of slf4j you only need one module depending on slf4j-log4j12, all the other modules need only to depend on slf4j-api.
Define versions in your parent pom so all submodules use the same version. Do this in the dependencyManagement section like this:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.4</version>
</dependency>
</dependencies>
</dependencyManagement>
For each module specify all direct dependencies in the modules pom but without version like this:
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
</dependencies>
To handle a lot of dependencies that are shared by a lot of modules you can also use this method: Create a new "dependency collection module" (let's call it X) that does nothing but depend on each needed artifact. Now each module only needs to depend on X to automatically depend on its dependencies too. I don't recommend this method though because (among other reasons) now each module relies on the dependencies in X and you cannot be sure what happens if you remove a dependency from X and you need to look at more than one pom to find a modules direct dependencies.
Hope this helps a bit :-)

Related

Should you include those dependencies in your pom that are already the dependencies of some of your dependencies?

Say there are two dependencies you need: A and B. And at the same time A is already a dependency of B. So do you still want/need to add A along with B as dependencies in your pom?
I believe this may be needed when A and B are external libraries where the version of A needed may be different than the version of A that B is depending on.
But how about when both your module and A and B are modules in the same project? i.e. knowing their versions are all going to be in sync.
If your module uses APIs from B it's best practice to add it explicitly to your pom, even though it's not strictly necessary. If you upgrade A, it could well be that it doesn't use B anymore and then you'll get a build failure without any changes to your module code.
Regarding versions, you should manage those with dependencyManagement in a parent pom. You can then skip the version for the managed dependencies in the child poms. The version in the dependencyManagement overrides the version in transitive dependencies, ensuring you use the same version everywhere.
If all modules are in the same project, they should also share the same project version. Typically, this will be a snapshot version, e.g. 1-SNAPSHOT
Each module will use something like:
<project>
<artifactId>A</artifactId>
<version>1-SNAPSHOT</version>
And refer to A and B like this in other modules:
<dependency>
<groupId>com.yourcompany</groupId>
<artifactId>A</artifactId>
<version>${project.version}</version>
</dependency>
To set a non-SNAPSHOT version before you build a release, you can for example use the maven-dependency-plugin's versions:set goal.

Maven: dependency appears in effective pom but not in dependency tree

Working on a multi-module project.
GrandParent
\
Parent
\
Child
GrandParent has rogue_1 module in its <dependencyManagement> section as provided !
Parent does NOT have rogue_1 in its pom.xml at all.
Child does NOT have rogue_1 as a direct dependency in its pom.xml.
However it includes several other projects some of which may include rogue_1. (at least one does depend on rogue_1)
To be on the safe side, on ALL Child's dependencies I have added exclusions as follows:
<dependency> <!-- a direct dependency of Child -->
<groupId>erso</groupId>
<artifactId>galen</artifactId>
<exclusions>
<exclusion>
<groupId>resistance</groupId>
<artifactId>rogue_1</artifactId>
</exclusion>
</exclusions>
</dependency>
(in case for example galen.erso is bringing in the resistance.rogue_1)
However: rogue_1 DOES end up being displayed as a dependency in the outcome of
mvn help:effective-pom
(as provided !)
It is NOT in the ouctome of
mvn dependency:tree
Any suggestions?
mvn help:effective-pom
is basically a merger between the super POM (grand /+parent) dependencies and the simple POM that you defined at the project level. Hence you do see the rogue_1 under the XML created by effective-pom, of course your grandparent pom's dependencyManagement being the source. Here is a detailed read over the same.
mvn dependency:tree
on the other hand displays the tre of the dependencies used in your project. As you mentioned you've excluded this out of all your mentioned dependencies, so you shouldn't find the artifact listed here.
By the way, in both the cases the chances of having the rogue_1 in the classpath of your child module is zero.
You may get this sort of behaviour, if you have a profile either in your project, or one that you're inheriting (and that is somehow getting activated). As far as I recall, dependencies defined in a <profile/> do not show up on the dependency tree.

List only used jars

I have Maven projects which I can build with these dependencies:
<dependency>
<groupId>org.wildfly.arquillian</groupId>
<artifactId>wildfly-arquillian-container-embedded</artifactId>
<version>1.0.2.Final</version>
</dependency>
<dependency>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-embedded</artifactId>
<version>9.0.2.Final</version>
</dependency>
There are a lot of jars into these dependencies and I would like to use on the necessary. How I can list which jars are only needed for the build?
The simple answer to you question is that there is no easy way to do what you are asking. In maven you declare for your package all the dependencies that the code inside your package will ever need in all scenarios. In this case if your using only a specific part of the wildfly-embedded package, and that part that you are using only depends on a subset of the package's declared dependencies then Maven has no way of knowing this.
One approach you could take is to simply look at the declared dependencies of those artifacts, exclude one of them, and then run your test suite. Assuming you have a comprehensive test suite if the tests past then you can reasonably assume that the dependency you excluded was not required by any of the parts of the library you utilized. You can do this for each of the declared dependencies.

Does Maven need to explicitly specify the dependency that Spring/Hibernate dependented?

I'm new to Maven, I try to use Maven with Spring, Hibernate in my project. After go though the Spring and Hibernate reference, I found that "there is no need to explicitly specify the dependent liberaries in POM.xml file for such Apache commons liberaries".
My questions is that : If my other parts of project refer to Apache commons liberary, such as commons-io, SHOULD I explicit specify this dependency in POM.xml file?
You should define those dependencies in Maven which your project is using. For example, even though some library depends on commons-io but if your code needs this then you should directly define commons-io in your pom.xml
You should not worry about the dependencies of the libraries you have defined in your pom.xml. Maven will do that for you.
Maven is used to avoid the issue of having to run down JAR files that are dependent on other JAR files. Of course you do not HAVE to use maven to do this, but you should. Maven will automatically download the dependent JAR files of the JAR file you require. THe hibernate-entity manager JAR file, for example, has over 100 dependencies and maven does the work for you.
Anyway,even if you do add the commons-io file to the build path/classpath of the maven project,and then update the project configuration, maven will kick it out.
You can provide a lib name on a site like mvnrepository.com to see what it depends on (e.g. take a look at a section called "This artifact depends on ..." in case of spring-webmvc library). Those dependencies (which your artifact depends on) are called transitive dependencies. You don't have to specify these in your pom.xml as maven will resolve them for you.
For the sake of readability you should only state those dependencies in your module that you rely on directly. You want JUnit to test your software, only declare JUnit; you need hibernate to use ORM, declare hibernate, and so on. Leave the rest to Maven.
And most of the time you should state what you intend to use in the very module you want to use it in. So if you want to use a dependency in more than one module, consider moving it into a dependencyManagement block in a parent pom and referencing it from there in the module you want it in.
parent pom.xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
child pom.xml
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
This guarantees you version-stability and still allows you to find out what a module uses by only looking in it's pom (and not all over the place).

Maven: Include POM with used dependencies in assembly

We deliver our package with many external dependencies to customers. Now customers can use you libraries to develop stuff on top. For those who are also using Maven we would like to include a pom.xml file in the assembly which contains all dependencies, so they can simply use it in their Maven build:
It should contain all dependencies used by us as follows:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math</artifactId>
<version>2.1</version>
<scope>compile</scope>
</dependency>
Is there a way to achieve that in Maven?
The pom.xml for your jar/war is by default placed inside your jar/war in the location META-INF\maven\<groupId>\<artifacdId>
You have two choices:
The first one which is the most common and preferred way is relying on Maven's transitive dependency resolution.
Have a POM (assume called foo-api:1.0) in your project that declares the dependency (e.g. A:1.0:jar, B:1.0:jar).
Developer's own project (bar:2.0) should then simply depends on foo-api:1.0. Maven will simply include A:1.0:jar and B:1.0:jar as dependency of bar:2.0, through the transitive dependency resolution.
Another way is similar to the above approach, but use foo-api:1.0 as parent POM of bar:2.0.
Either way should work but which one is suitable depends on your design.

Resources