Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is Maven including multiple versions of the same dependency?

I have a Maven java web app (.WAR) project that includes several libraries, including the Wicket libraries (but I don't think the problem is wicket itself, but rather with maven).

Here's the problem: even tho I only include Wicket 6.20.0, the resulting .WAR contains two copies of the Wicket libraries: 6.20.0 and 6.18.0, as you can see in this screenshot:

enter image description here

Thinking of some conflicting imports I printed the dependency tree using the:

mvn dependency:tree

commnad... but there is no mention of Wicket 6.18.0 in the dependency tree! I also double-checked using Eclipse's "dependency hierarchy" view and I can confirm there's no trace of that import.

I even did a search for string "6.18.0" across the entire workspace with Eclipse, but it's nowhere to be found!

How can I find out what is causing the inclusion of that duplicate version of the library?

like image 369
Master_T Avatar asked Jun 19 '17 13:06

Master_T


People also ask

How do I exclude a specific version of a dependency in Maven?

Multiple transitive dependencies can be excluded by using the <exclusion> tag for each of the dependency you want to exclude and placing all these exclusion tags inside the <exclusions> tag in pom. xml. You will need to mention the group id and artifact id of the dependency you wish to exclude in the exclusion tag.

How Maven handles and determines what version of dependency will be used when multiple version of an artifact are encountered?

Dependency mediation - this determines what version of an artifact will be chosen when multiple versions are encountered as dependencies. Maven picks the "nearest definition". That is, it uses the version of the closest dependency to your project in the tree of dependencies.

Does Maven override dependency version?

By taking advantage of Maven's nearest definition logic, developers can override the version of a dependency by declaring it on the root pom. xml file.

What is Maven dependency conflict?

Maven can automatically bring in these artifacts, also called transitive dependencies. Version collision happens when multiple dependencies link to the same artifact, but use different versions. As a result, there may be errors in our applications both in the compilation phase and also at runtime.


2 Answers

Maven doesn't work in this way.
The resolution of more than one dependency with the same artifactId and groupId but with a different version will result to a single dependency (the version used is no determinist).

The presence of two artifacts with the same artifactId and groupId but with two distinct versions in a same lib folder of the WAR is probably related to one of these :

  • you don't execute mvn clean package but only mvn package.

  • your use a bugged version of the Maven war plugin. Try to update it to check that.

  • you have a Maven plugin that copies Wicket jars 6.18.0 in the WEB-INF/lib folder of the target folder during the build of the component.

  • the maven WAR project you are building has as dependency an artifact of type WAR. In this case, the dependencies of the WAR dependency are so overlaid in the WAR project that you are building.


An interesting Maven issue about duplicated JAR because of WAR dependencies :

JARs with different versions can be in WEB-INF/lib with war as dependencies


Your answer and your comment indicate that actually you have a WAR dependency in your build.
Unfortunately, there is not really a good and long term effective solution to bypass this limitation.

As said in my comment, using the packagingExcludes property of the maven war plugin is a valid workaround for the actual issue :

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.4</version>
    <configuration>
        <!-- ... -->
        <packagingExcludes>WEB-INF/lib/wicket-*-6.18.0.jar</packagingExcludes>
    </configuration>
</plugin>

But beware, using that will do your build less robust through the time. The day where you update the version of the WAR dependency and that in its new version, it pulls again a different version of wicket, you have still a risk to have duplicate jars with two distinct versions in your built WAR.

Using the overlay feature by specifying the overlay element of the maven-war-plugin is generally better as it focuses on the overlay applied for the war dependency. It fixes the problem early. As a result, you could define to exclude any wicket JARs from the WAR dependency :

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <version>2.4</version>
    <artifactId>maven-war-plugin</artifactId>
    <configuration>     
        <overlays>
            <overlay>
                <groupId>com.whatever.youlike</groupId> 
                <artifactId>myArtifact</artifactId>
                <excludes>
                    <exclude>WEB-INF/lib/wicket-*.jar</exclude>                 
                </excludes>
            </overlay>
        </overlays>
    </configuration>
</plugin>

This way is better but this is still a workaround.
The day where the dependency WAR is updated and that it pulls new dependencies (other than Wicket) that are declared in your actual build but with different versions, you may finish with the same kind of issue.

I think that declaring a dependency on a WAR artifact should be done only as we don't have choice.
As poms and projects refactoring are possible, introducing a common JAR dependency which the two WARs depend on and that contains only common sources and resources for the two WARs makes really things simpler.

like image 89
davidxxx Avatar answered Sep 21 '22 01:09

davidxxx


Well, I figured it out while poking around.

I had a dependency of type "war" in the project:

<dependency>
    <groupId>com.whatever.youlike</groupId>
    <artifactId>myArtifact</artifactId>
    <version>1.0.7-SNAPSHOT</version>
    <type>war</type>
</dependency>

Apparently (I wasn't aware of this, my fault here) these type of dependencies will include themselves in the classpath by copying all libs to the main WAR /libs folder, but these will NOT show app in the dependency tree / dependency hierarchy.

I solved by configuring an explicit exclusion in the WAR plugin:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.4</version>
    <configuration>
        <!-- ... -->
        <packagingExcludes>WEB-INF/lib/wicket-*-6.18.0.jar</packagingExcludes>
    </configuration>
</plugin>
like image 43
Master_T Avatar answered Sep 19 '22 01:09

Master_T