Duplicate direct dependencies in maven - java

I deliberately wanted to see if maven allows duplicate direct dependencies (i.e. non transitive dependencies) as it uses dependency mediation to resolve the conflicting transitive dependencies.
I wrote this is in my pom.xml
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies>
I was surprised to see, it is picking 4.10 version instead of 4.11. What could be reason for this ?
I will add more details that shows it violates dependency mediation principle too. Here is output of mvn dependency:tree for only standalone junit 4.10 version VS standalone junit 4.11 version:
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) # PracticeMaven ---
[INFO] org.example:PracticeMaven:jar:1.0-SNAPSHOT
[INFO] \- junit:junit:jar:4.10:test
[INFO] \- org.hamcrest:hamcrest-core:jar:1.1:test
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) # PracticeMaven ---
[INFO] org.example:PracticeMaven:jar:1.0-SNAPSHOT
[INFO] \- junit:junit:jar:4.11:test
[INFO] \- org.hamcrest:hamcrest-core:jar:1.3:test
If you see there is a conflict with org.hamcrest:hamcrest-core when we mention both junit in pom.xml. It should have chosen org.hamcrest:hamcrest-core:jar:1.3 as 4.11 is mentioned first. But it does not. It choses junit 4.10 and org.hamcrest:hamcrest-core:jar:1.1.
I am using latest Apache Maven 3.6.3.

Why were you surprised to see this?
It is probably always taking the last one. Avoid this if possible (the only use case I could see is to alter a dependency that was defined in a parent POM).

I'm not sure how accurate this information still is, but:
When declaring a "normal" version such as 3.8.2 for Junit, internally
this is represented as "allow any-thing, but prefer 3.8.2." This means
that when a conflict is detected, Maven is allowed to use the
conflict algorithms to choose the best version. If you specify
[3.8.2], it means that only 3.8.2 will be used andnothing else. If
somewhere else there is a dependency that specifies [3.8.1], you would
get a buildfailure telling you of the conflict. We point this out to
make you aware of the option, but use it sparingly and only when really
needed. The preferred way to resolve this is via dependencyManagement
Source: Maven: The Complete Reference, page 35
So, usually given a version without a range (using round or square brackets) is just hint what you like to use, not something Maven has to obey.

Related

Where are servlet and JAX-RS dependencies coming from?

If I include the following J2EE dependency in my application, I have access to servlet and JAX-RS classes and interfaces:
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
</dependency>
But where are they coming from? On Maven Central I see that javax:javaee-api:7.0 has as a dependency javax:javaee-web-api:7.0, which in turn has as dependencies javax.servlet:javax.servlet-api:3.1.0 and javax.ws.rs:javax.ws.rs-api:2.0, so on the face of it, that's the answer.
But all those dependencies are marked as optional, which means that as transitive dependencies they wouldn't show up for my project unless I explicitly include them. But yet my program compiles simply with a dependency to javax:javaee-api:7.0. Why?
Here is my dependency tree; I don't see where they are coming in:
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) # temp-server ---
[INFO] com.example:test-server:war:1.0.0-SNAPSHOT
[INFO] +- com.google.code.findbugs:jsr305:jar:3.0.1:provided
[INFO] \- javax:javaee-api:jar:7.0:compile
[INFO] \- com.sun.mail:javax.mail:jar:1.5.0:compile
[INFO] \- javax.activation:activation:jar:1.1:compile
(This is all basic stuff; I don't know why I'm confused. I must be missing something obvious.)
As JAX-RS 2.0 is part of Java EE 7, the classes defining its API are directly included into the artifact javax:javaee-api:7.0 so it is enough to compile your program as long as you use standard classes only.
Here, you need to understand what is meant by <scope>provided</scope>. The javax.servlet:javax.servlet-api:3.1.0 and javax.ws.rs:javax.ws.rs-api:2.0 will be included with scope as provided when including the javaee-api dependency. What this means is that, ONLY while compiling your classes, these jar files will be used and NOT at runtime. Since the scope is provided, it expects that these 2 jars will be given by the runtime environment. To re-iterate, when any jar is included with scope as provided, it means this jar should be used for compilation, but for runtime, it expects to be provided by the runtime container.
Now to answer your question, if you consider any runtime environment container such as JBOSS, WAS, etc, they all come bundled with these jars. So at the time of compiling your classes, it will make use of the jars that come as transitive dependencies to javaee-api, but at run time it will make use of the jars that came bundled with the container instead. Hence you don't get any error.

Compilation failure on unneeded dependency of dependency

I am attempting to compile my maven project, and I am depending on another project (which is a jar file). For some reason, when I attempt to compile I get the following error which seems to be that my project can not access the required dependencies of the project I'm depending on, even though I don't use any of those dependencies.
Here is the error http://hastebin.com/pebolozuxi.sql
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] /C:/Users/cneth/Desktop/Developing/PrimeMC/PrimeMobWhacker/src/main/java/org/primemc/MobWhackerManager.java:[315,34] cannot access tech.rayline.core.plugin.RedemptivePlugin
class file for tech.rayline.core.plugin.RedemptivePlugin not found
[INFO] 1 error
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.892 s
[INFO] Finished at: 2016-10-07T14:56:20+03:00
[INFO] Final Memory: 25M/298M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.5.1:compile (default-compile) on project MobWhacker: Compilation failure
[ERROR] /C:/Users/cneth/Desktop/Developing/PrimeMC/PrimeMobWhacker/src/main/java/org/primemc/MobWhackerManager.java:[315,34] cannot access tech.rayline.core.plugin.RedemptivePlugin
[ERROR] class file for tech.rayline.core.plugin.RedemptivePlugin not found
I know that I can easily depend on that required dependency to solve the issue, however I don't see why that would be necessary so I wish to solve this issue without taking that route, if possible.
Thanks!
This is nothing to do with Maven. The java compiler wants that class. MobWhackerManager.java:[315,34], in fact, wants that class. The line number is right there. Your code, at line 315, references that class somehow.
Try excluding not needed transitive dependecy
https://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html
Your error says that the compiler misses the class tech.rayline.core.plugin.RedemptivePlugin.
So you probably need to add the jar with this class to your Maven dependencies.
From your last comment I understand that you need the jar containing RedemptivePlugin so you must keep it. In the worst case, download it manually with the right version and put it in your .m2 with the right path
previous answear
Juste to be able to help:
Have you checked the presence of your needed jar in your .m2 ?
Have you checked the full dependencies of your project to state for sure that you don't need the jar containing the class tech.rayline.core.plugin.RedemptivePlugin.
If you are sure of that, try:
<dependencies>
<dependency>
<groupId>The Project Jar That Links You To RedemptivePlugin Jar</groupId>
<artifactId>Project</artifactId>
<version>TheVersion</version>
<exclusions>
<exclusion>
<groupId>The Jar containing RedemptivePlugin</groupId>
<artifactId>theJar</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

mvn dependency causing NoSuchMethod errors with htmlunit

I have dependency:
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.10</version>
</dependency>
And run a test that includes testing a website for form submission. Works great. I package this up into my local repo mvn install and include it in another wider project.
Doing a mvn dependency:tree -Dverbose -Dincludes=net.sourceforge.htmlunit doesn't seem to reveal anything untoward
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) # myproject ---
[INFO] com.myproject:myproject:war:1.0-SNAPSHOT
[INFO] \- com.myproject:myproject:mp:jar:1.0:compile
[INFO] \- net.sourceforge.htmlunit:htmlunit:jar:2.10:compile
[INFO] \- net.sourceforge.htmlunit:htmlunit-core-js:jar:2.10:compile
So I don't think any of the other deps are causing an issue. Yet if I copy/paste a test from the base project into the wider one, I always get errors:
java.lang.NoSuchMethodError: com.gargoylesoftware.htmlunit.html.HtmlPage.getElementById(Ljava/lang/String;)Lcom/gargoylesoftware/htmlunit/html/HtmlElement;
I've made sure my IDE isn't caching some dodgy library somewhere so as far as I can tell it's clean. Any ideas?
Thanks
Problem is you have this class coming from some other artifact, since you verified it is not directly coming from htmlunit with different version, it must be shaded in some other jar
for jvm to get pick up correct class please put htmlunit dependency on top of dependency so it will be first in classpath taking priority

Maven automatically removes trailing zeroes in dependency version (javax.servlet-api)

I am trying to add the javax.servlet-api project to my Maven Java project (see this link for more information about versions for this dependency). I want to add version 3.1.0 to my project. So naturally, I add this dependency to my pom.xml file:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
However, when Maven tries to download the jar/pom files for this artifact, it gets an error because it automatically converts version 3.1.0 to 3.1, which is not its actual version, and it cannot find that version of the artifact in the central Maven repo:
Downloading: http://repo.maven.apache.org/maven2/javax/servlet/javax.servlet-api/3.1/javax.servlet-api-3.1.pom
[WARNING] The POM for javax.servlet:javax.servlet-api:jar:3.1 is missing, no dependency information available
Downloading: http://repo.maven.apache.org/maven2/javax/servlet/javax.servlet-api/3.1/javax.servlet-api-3.1.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
...
[ERROR] Failed to execute goal on project my-java-project: Could not resolve dependencies for project com.example.myproject:my-project:war:1.0-SNAPSHOT: Could not find artifact javax.servlet:javax.servlet-api:jar:3.1 in central (http://repo.maven.apache.org/maven2) -> [Help 1]
Does anyone know how I can tell Maven to not strip off trailing zeroes in the version tag of a dependency in my pom.xml file? Any other workaround known?
NOTE: I did check out this SOF post (Maven dependency for Servlet 3.0 API?), but this author's solution was to use a <scope>provided</scope> dependency for servlet-api, which I do not have interest in doing becuase I want to avoid needing to use the provided scope.
it gets an error because it automatically converts version 3.1.0 to 3.1
That is something impossible. Maven will never convert version numbers ( with the exception of snapshots ). It will always download the version that you specify in the pom.xml regardless the version actually exists or not. You can test this by writing a bogus number in the version area of the tag and then fire the mvn install command, you will see that it will try to download that version and then fail.
To debug your problem, first verify that you have the correct maven entry in the pom
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
Secondly , I would advise you to delete the folder ~/.m2/repository/javax/servlet/ to remove any bad jar and thus force maven to re-download all the javax.servlet jars

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.

Categories