Multiple versions of the same dependency in Maven

Maven

Maven Problem Overview


Is it possible to declare multiple versions of the same dependency in a Maven repo?

I need these dependencies all at once:

    <dependency>
        <groupId>org.bukkit</groupId>
        <artifactId>craftbukkit</artifactId>
        <version>1.7.9-R0.2</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.bukkit</groupId>
        <artifactId>craftbukkit</artifactId>
        <version>1.7.2-R0.3</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.bukkit</groupId>
        <artifactId>craftbukkit</artifactId>
        <version>1.6.4-R2.0</version>
        <scope>compile</scope>
    </dependency>

Because each of them contains a different package I care about:

> org.bukkit.craftbukkit.v1_6_R3 > > org.bukkit.craftbukkit.v1_7_R1 > > org.bukkit.craftbukkit.v1_7_R3

If I declare dependencies as shown in the first snippet, only the last one will take effect. Is there any way to achieve this in Maven?

@Edit Any workaround, maybe?

Maven Solutions


Solution 1 - Maven

No. Maven will only resolve one dependency in your module and will omit the other versions to avoid any conflict. Even if multiple versions of the same dependency are used in the whole dependency hierarchy, Maven will pick one version using the "nearest in the dependency tree" strategy.

It is possible to specify different dependency versions using different profiles. For each version of Bukkit a profile can be defined and activated. Still if you activate more than one profile, only one version would be used.

<profiles>
	<profile>
		<id>Bukkit_1_7_9_R02</id>
		<activation>
			...
		</activation>
		<dependencies>
			<dependency>
				<groupId>org.bukkit</groupId>
                <artifactId>craftbukkit</artifactId>
                <version>1.7.9-R0.2</version>
                <scope>compile</scope>
			</dependency>
		</dependencies>
	</profile>
	<profile>
		<id>Bukkit_1_7_2_R03</id>
		<activation>
			...
		</activation>
		<dependencies>
			<dependency>
				<groupId>org.bukkit</groupId>
                <artifactId>craftbukkit</artifactId>
                <version>1.7.2-R0.3</version>
                <scope>compile</scope>
			</dependency>
		</dependencies>
	</profile>
    ...
</profiles>

Solution 2 - Maven

Try to cheat maven:

<dependency>
    <groupId>org.bukkit</groupId>
    <artifactId>craftbukkit</artifactId>
    <version>1.7.9-R0.2</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>org.bukkit.</groupId>
    <artifactId>craftbukkit</artifactId>
    <version>1.7.2-R0.3</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>org.bukkit..</groupId>
    <artifactId>craftbukkit</artifactId>
    <version>1.6.4-R2.0</version>
    <scope>compile</scope>
</dependency>

Solution 3 - Maven

Here is the relevant part of the Maven documentation, which explains how Maven chooses the version of a dependency when there is more than one possibility:

> 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. > You can always guarantee a version by declaring it explicitly in your > project's POM. Note that if two dependency versions are at the same > depth in the dependency tree, the first declaration wins. "nearest > definition" means that the version used will be the closest one to > your project in the tree of dependencies. Consider this tree of > dependencies:

A
├── B
│   └── C
│       └── D 2.0
└── E
    └── D 1.0

> In text, dependencies for A, B, and C are defined as A -> B -> C -> D 2.0 and A -> E -> D 1.0, then D 1.0 will be used when building A because the path from A to D through E is shorter. You > could explicitly add a dependency to D 2.0 in A to force the use of D > 2.0, as shown here:

A
├── B
│   └── C
│       └── D 2.0
├── E
│   └── D 1.0
│
└── D 2.0      

Solution 4 - Maven

No, you can't depend 2 versions of the same artifact, normally.
But you can include them so they end up in the resulting application.

That requirement is sometimes valid - for instance, when the maintenance of that library is poor, and they rename some packages and release that as a minor version of the same artifact. Then, other projects have it as a 3rd party dependency and need the same classes under different FQCN.

For such cases, you can for instance use the maven-shade-plugin.

  • Create a maven project with a single dependency, one of the versions you need.
  • Add the shade plugin and let it create a shaded jar. It will basically re-wrap the classes under a different artifact G:A:V.
  • Do this for all versions you need.
  • You may use classifier to differentiate the shaded versions.
  • In your project, depend on these artifacts.
  • Finally, exclude the original dependencies, give them the scope "provided".

You can use different variations of the same, which, in the end, will put those classes to your classpath. For instance, use the dependency:copy-dependency plugin/goal, and install that jar to your local repo during the build. Or unzip the classes right into your ${project.build.outputDirectory} (target/classes).

Solution 5 - Maven

I'm still pretty new, but something I've been running into with axis2 is that the individual modules sometimes require an earlier version because of a change they made to the classes, so the top level dependency only catches about half of them. The rest I've been having to individually correct the poms for for explicit dependencies to a different version.

Maybe this approach would work for you as well? Having the plugin have modular components for their specific dependencies.

Solution 6 - Maven

This is how I got around it. FYI: In my case I was building a RPM.

I used the maven-dependency-plugin to copy the older dependency that gets ignored to a folder in the build directory and then copied that file to the staging area so that it's included in the RPM.

Here's the code to copy the older dependency:

  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <version>3.1.1</version>
    <executions>
      <execution>
        <id>copy-dependencies</id>
        <phase>prepare-package</phase>
        <goals>
          <goal>copy</goal>
        </goals>
        <configuration>
          <artifactItems>
            <artifactItem>
              <groupId>some.package.group.id</groupId>
              <artifactId>someartifact-id</artifactId>
              <version>1.2.3</version>
              <overWrite>false</overWrite>
              <outputDirectory>${project.build.directory}/older-dependencies</outputDirectory>
            </artifactItem>
          </artifactItems>
        </configuration>
      </execution>
    </executions>
  </plugin>

And then later on during the building of my RPM I included this scriptlet in the configuration section of my rpm-maven-plugin. This will copy the file to the staging area for the RPM:

<installScriptlet>
  <script>cp ${project.build.directory}/older-dependencies/* ${project.build.directory}/rpm/${artifactId}/buildroot${installBase}/</script>
</installScriptlet>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionwassupView Question on Stackoverflow
Solution 1 - MavenM AView Answer on Stackoverflow
Solution 2 - MavenDmitriView Answer on Stackoverflow
Solution 3 - MavenSteve ChambersView Answer on Stackoverflow
Solution 4 - MavenOndra ŽižkaView Answer on Stackoverflow
Solution 5 - MavenRyan WoodView Answer on Stackoverflow
Solution 6 - MavenschmuduView Answer on Stackoverflow