Maven: Project A not respecting Project B's exclusion - java

I have two projects A, and B.
Project A depends on Project B. Project B depends on a library C which depends on a library D.
I own both A and B but they live in separate code bases and are independent. Project B is like an internal common helper library.
- A
-- B
--- C (version 1.1)
---- D (version 1.1)
I want to upgrade D to a newer minor version, let's say 1.5.
So in B's pom.xml file, I imported D (v1.5) directly. Then excluded D from C. Example:
<dependency>
<groupId>D</groupId>
<artifactId>foobar</artifactId>
<version>1.5</version>
</dependency>
<dependency>
<groupId>C</groupId>
<artifactId>fizzbuzz</artifactId>
<version>1.1</version>
<exclusions>
<exclusion>
<groupId>D</groupId>
<artifactId>foobar</artifactId>
</exclusion>
</exclusions>
</dependency>
Next if I run
[~/projectB] $ mvn dependency:tree
I confirm that only 1.5 is used, not 1.1. Next I build a new local snapshot of B. And specify Project A to use that one.
My problem is when I build Project A, it continues to use v1.1 and ignores the exclusion in B.
This always returns 1.1 instead of the 1.5 that I want.
[~/projectA] $ mvn dependency:tree
Project A never imports C or D directly, it only get library D from importing Project B.
I've tried:
Reloading the maven imports
Running mvn clean install numerous times.
Deleting the folders from my .m2/ directory to force reimports
I'm out of troubleshooting ideas. Does anyone else have any tips?

Related

Add maven project as dependency in maven project

I have two Maven projects, both created from the default Maven structure in Eclipse.
Project A is dependant on Project B. In order to utilise classes from A in B, I can add B to A's build path.
How can I achieve the same effect using Maven?
Currently, I have both of the projects on my file system, but I would like to add this dependency in as similar a way as possible to, for example, adding GSON from a remote repository (Fig. 1), as that is how I would like to handle my own repositories in future.
Currently, when I try this:
<dependency>
<groupId>vision.voltsofdoom</groupId>
<artifactId>voltsofdoom</artifactId>
<version>0.0.1</version>
<type>pom</type>
</dependency>
... and remove any other projects from the build path, Eclipse is unable to resolve any of my imports (everything works fine when I add Project B (voltsofdoom) to A's (casketofazamgarath) build path.
B's (voltsofdoom) basic pom looks like this.
<groupId>vision.voltsofdoom</groupId>
<artifactId>voltsofdoom</artifactId>
<version>0.0.1</version>
<packaging>pom</packaging>
B (voltsofdoom) does not appear in the "Local Repository" tab in the "Maven Repositories" View, instead appearing in "Workspace Projects".
Figure 1 (Adding GSON):
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.3.1</version>
</dependency>
==
Answer:
Follow the correct marked answer.
The amended snippet is:
<dependency>
<groupId>vision.voltsofdoom</groupId>
<artifactId>voltsofdoom</artifactId>
<version>0.0.1</version>
</dependency>
You can run the maven clean install goal on the project that has no dependency on the other (i.e. the "dependency"), which will "export" your project to your local .m2 directory in your users directory.
After that, you should be able to access it from the depending project with the code snippet you mentioned in your question (although I'm not quite sure what the <type> tag does).

How to use different version dependencies for each JAR

I have a maven project A that uses some Java library B, which is dependency stated in POM. That library uses some other library C that uses library D. The problem arises in compiling where both A and C are using that library D but different versions. It looks like this:
A ----- B
| |
D(v1) C
|
D(v2)
A can not change its dependency to D(v2), but also if I exclude D(v2) in A's POM:
<dependency>
<groupId>...</groupId>
<artifactId> C </artifactId>
<exclusions>
<exclusion>
<groupId>...</groupId>
<artifactId> D </artifactId>
<version> v2 </version>
</exclusion>
</exclusions>
</dependency>
I will get compilation error that some class MissingClass is not resolved in class C. The most annoying thing is that only D(v2) has that Missing Class.
I am not somewhat skilled with Maven so is there some way to make Maven is using D(v1) dependency in A's case, and D(v2) in B's case?
This is more a problem of Java classpath.
Could your scenario work if you run things via cmd line, without Maven? (i.e. run your app, and manually specify your classpath).
If the answer is yes, then you probably can do it in Maven; but my understanding from your description is that no, you couldn't run via cmd line.

Can I have partial maven workspace resolution in eclipse?

I have 3 projects - A, B and C.
A has dependencies on B and C, and I deploy A to Tomcat in eclipse.
I want A to deploy with the workspace versions of A and B. I don't want my workspace copy of project C to be picked up (instead I want the installed version in ~/.m2/ to be picked up).
I can achieve this by closing project C in Eclipse. However, I want to keep project C open.
Is this possible?
You can execute mvn install on your project C with a closed version in your pom.xml file, let's say 1.0. This will leave a 1.0 version of your project C deliverable in your ~/.m2.
Make sure project A pom.xml and project B pom.xml depend on version 1.0 of project C.
For example, Project A and B pom.xml:
...
<dependency>
<groupId>myproject</groupId>
<artifactId>C</artifactId>
<version>1.0</version>
</dependency>
...
Then you can just change the version in your project C pom.xml to, let's say 2.0, and then tell Eclipse to update all your maven projects.
Project C pom.xml:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>myproject</groupId>
<artifactId>C</artifactId>
<version>2.0</version>
...
The situation will be:
Eclipse A project depending on Eclipse B project and ~/.m2/.../C/1.0/C.jar.
Eclipse B project depending on ~/.m2/.../C/1.0/C.jar.
Eclipse C project open but out of the tomcat deploy.
This way you can always adjust maven dependencies back in A and B in order to depend on your development version of C, or just keep them depending on your closed version of C.jar

Can I overwrite a maven dependency version through my local repository?

I have a bit of a dependency hell situation here that I'm trying to resolve: I have three projects, A, B and C. A and B both depend on C. Now A is my own module that I have direct control over, B is a library I'm using, C is a library that is used both directly from my module A and by my dependency B.
To visualize this:
C
^
/ B
| ^
\ /
A
For various reasons I now needed to make a small change to C that I need locally but don't want to (or can't) deploy to the global repository from which C is downloaded normally.
I tried to do this by tagging my modified version of C installed in my local repository with a classifier and changing the dependency to it in A's POM to include the classifier like this:
<dependency>
<groupId>foo</groupId>
<artifactId>C</artifactId>
<version>0.7.16</version>
<classifier>myclassifier</classifier>
</dependency>
But mvn dependency:tree now shows that I have both the version with the classifier and the version without it in my classpath because of the transitive dependency:
[INFO] --- maven-dependency-plugin:2.1:tree (default-cli) # A ---
[INFO] org.example:A:jar:0.1-SNAPSHOT
...
[INFO] +- foo:C:jar:myclassifier:0.7.16:compile
[INFO] +- bar:B:jar:3.2.5:compile
[INFO] | +- foo:C:jar:0.7.16:compile
Is there any way I can force my project / maven to just use my modified version in this context?
Edit: Solution
For now, I've solved it using exclusions like this in A's pom.xml thanks to the answers by Filipe and Samuel.
<dependency>
<groupId>foo</groupId>
<artifactId>C</artifactId>
<version>0.7.16</version>
<classifier>myclassifier</classifier>
</dependency>
<dependency>
<groupId>bar</groupId>
<artifactId>B</artifactId>
<version>3.2.5</version>
<exclusions>
<exclusion>
<groupId>foo</groupId>
<artifactId>C</artifactId>
</exclusion>
<exclusions>
</dependency>
Note: This of course only works as long as the edits in C are really minor and don't change the API used by B, which for me is the case.
You're saying that C is the official version of the library and that your minor change (Let's call it C2) would only be used by A. You're also saying that by declaring in A the direct dependency on C2, than it's unfortunately expected that you end up with C2 and C. C will be a transitive dependency from B. Unfortunately too, the Maven reactor treats libraries with the same version but with a different classifier as different libraries, giving you exactly this result (of having both C and C2).
If you only need C2 in project A, you could explicitly add exclusions to C into A's POM.
Obviously this new version of C is for you not a new artifact, but a new version of this artifact.
So you should not use here a classifier but a version number increase instead.
Your project will be built with the last version (and even if you have a problem to resolve the last version, you can exclude the transitive dependency in your pom)
However, you must be very carefull when doing this, because your B project will have been compiled with the old version of C. So if you modify method erasure, or remove methods or classes, you will have runtime trouble.
If you only add classes or methods in your C library, then it should work.

How to handle sub projects dependencies in Maven

My project is made of 5 sub projects. One is a War, and the other 4 are jars. Basically the war project needs all 4 jar projects, and their dependencies.
I can strip down the dependencies to have something like war->A->B->C->D. Every sub project add their share of external dependencies (spring, struts, hibernate) so that in the end the war gets everything needed to run.
This looks pretty well organised and square, but then I ask myself if this is very practical to make changes.
Imagine I have to change one line of code in project D, without changing anything to its Maven dependencies. I would have to re-release project D obviously, but then I have to re-release projects C, B, A, and the war just to reflect this change in their pom files. This can be long and annoying, especially if you have to quickly release a new version to fix something in production.
I could make the war depend on all 4 projects, so then I just have to change project D version number in the war pom file. But then I have project A depending indirectly on project D 1.0 and the war specifying project D 1.1. I think that the war direct dependency would win in that case wouldn't it ?
This would make the new war release quicker, but it would also mess my sub projects dependencies, as they would be outdated.
What would be an acceptable way to handle this situation ?
There is no simple answer to your problem.
If you indeed do have a chain of transitive dependencies (A->B->C->D), then releasing each modules up the chain independently is not a bad option. Although it is tedious, there is a good chance your nested dependencies are simple lib jars and will not see changes too often. Hopefully you will not be forced to go through that process frequently. Pretend it would be the same situation as if log4j was updated and all of your modules needed to be updated as well.
Another thing to consider is your WAR's dependencies. Yes, Maven will pull dependencies in automatically for you but it is often a good practice to declare your known dependencies explicitly so you can specify a version number yourself for each module. This would mean A depends on D and the others directly. Unfortunately, if you have conflicting version numbers, as you've described, then you are looking for trouble on your classpath. If you really need to do this though, maven does allow you exclude transitive dependencies explicitly:
<project>
...
<dependencies>
<dependency>
<groupId>my.project</groupId>
<artifactId>module-B</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>my.project</groupId>
<artifactId>module-C</artifactId>
</exclusion>
<exclusion>
<groupId>my.project</groupId>
<artifactId>module-D</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>my.project</groupId>
<artifactId>module-C</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>my.project</groupId>
<artifactId>module-D</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>my.project</groupId>
<artifactId>module-D</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
...
</project>
Here is the documentation describing these optional dependencies and exclusions.
Do you actually need to release B, C, and D independently? If not, consider using an Aggregator pom.xml file at the root of your modules. This will allow you to use SNAPSHOT versions throughout your modules and then release the bunch at once. This is the way our team manages our multi-module project. Using SNAPSHOT dependencies ensures you use the version that was JUST built when those artifacts are needed.
Do you actually release any of projects A to D independently, without the WAR? If not, I don't see any problems with your current setup. You should absolutely use the same version of any module throughout the project. Otherwise you open the door to classloader hell - believe me, you don't want to get there :-(
To make releases easier, the maven-release-plugin may help you.
The best answer these days is now use gradle which is best of ant and maven. I never really liked maven but gradle took alot of the common concepts but made it more like ant in that it is flexible so that there is no an easy answer to your question in gradle ;).

Categories