Janaka Bandara
Janaka Bandara

Reputation: 1072

Getting Maven to resolve missing dependencies with closest available matches

When I look at my local Maven cache (~/.m2/repository/) I see 10-20 versions of certain artifacts, some of which are being used for only a single specific build (project). I would like to get rid of this duplication (true, they are different versions, but I'd still think a depending project would be able to tolerate a micro or minor version difference) and to somehow ask Maven to resort to the closest available version during dependency resolution, if a specific artifact version is missing in the local repository.

For example, if I have versions 1.0.0, 1.1.2, 1.4.0 and 2.0.0 of a foo:bar artifact in my local cache, I would like Maven to:

without having to manually change the pom of the specific build(s).

I am fairly aware of the risks associated with switching dependency versions without proper analysis, and I'm only asking for a mechanism to be utilized for non-critical builds (such as a tool/library that I just cloned off a VCS, and would like to run and try out), preferably activated only when a particular flag is provided.

Is there something out there, like a Maven extension or plugin (that can be applied on a system-wide scale, and activated on demand with a flag), that can help me achieve my goal?

P.S.: Since the definition of "closest" could be ambiguous (given the fact that Maven may not know which of 1.4.0 and 2.0.0 is closer to 1.5.0 depending on the actual release versions lying between them), it would even be sufficient if I can specify the version on the build command (e.g. mvn package -Dfoo:bar=1.4.0), still without making any manual pom changes. (While this may already be possible for versions that have been specified as <properties> entries, I would like a generic solution where even hard-coded versions in transitive dependencies could be overridden.)

P.P.S.: Please note tht the project(s) that would be built would not have been authored/composed by me, so I don't really have control/authority over their actual pom files. What I'm looking for is a way to override dependency versions in their pom files without doing any manual modifications at source level.

Upvotes: 0

Views: 754

Answers (2)

Janaka Bandara
Janaka Bandara

Reputation: 1072

Closest thing I found so far: https://github.com/jboss/maven-dependency-management-extension/blob/master/README.md

It can be dropped into ${MAVEN_HOME}/lib/ext and utilized for overriding versions of specific dependencies, e.g. mvn install -Dversion:junit:junit=4.10. While it doesn't offer the suggested "intelligent version derivation" approach, it's a good-enough solution.

Upvotes: 1

Paul Hicks
Paul Hicks

Reputation: 13999

In order to change a transitive dependency, you need to exclude the transitive dependency in your direct dependency, then add a direct dependency in your pom.

For example, if you have a dependency on foo.jar (which depends on xyz.jar version 1.3) and on bar.jar (which depends on xyz.jar version 1.4), you can have these two sections in your pom:

<!-- Define the version(s) that you allow your dependencies to depend on. -->
<dependencyManagement>
  <dependency>
    <groupId>projectXyz</groupId>
    <artifactId>xyz</artifactId>
    <version>[1.0,2.0)</version>
  </dependency>
  <dependency>
    <groupId>projectFoo</groupId>
    <artifactId>foo</artifactId>
    <version>1</version>
    <exclusions>
      <exclusion>
        <groupId>projectXyz</groupId>
        <artifactId>xyz</artifactId>
      </exclusion>
    </exclusions>
  </dependency>
  <dependency>
    <groupId>projectBar</groupId>
    <artifactId>bar</artifactId>
    <version>5</version>
    <exclusions>
      <exclusion>
        <groupId>projectXyz</groupId>
        <artifactId>xyz</artifactId>
      </exclusion>
    </exclusions>
  </dependency>
</dependencyManagement>
...
<!-- Declare your dependencies but don't allow them to suck in their transitive dependencies. -->
<dependencies>
  <dependency>
    <groupId>projectXyz</groupId>
    <artifactId>xyz</artifactId>
  </dependency>
  <dependency>
    <groupId>projectFoo</groupId>
    <artifactId>foo</artifactId>
  </dependency>
  <dependency>
    <groupId>projectBar</groupId>
    <artifactId>bar</artifactId>
  </dependency>
</dependencies>
...

This will pick up the most recent version of xyz.jar that it can, and that will be the only version used. When foo and bar use xyz, they'll have the version that you've allowed into your project.

The best thing to do is to use a parent pom (or bom: bill of materials) with a well-defined and well-maintained dependencyManagement section. Stick with a single version of everything, just share that "everything" between all projects. You can override versions in projects if you need to.

If you'd rather define versions in each project, then version ranges will work. For the three examples you gave, you would use things like:

  • [1.1.0, 1.2)
  • [1.4.0, 1.5)
  • [2.0.0,)

(Open to correction here.. I haven't used version ranges in almost 10 years.)

Finally, to get it to use versions that are already available, rather than download the best ones, all you can do is to use a local artifact repository as your central maven repository, and turn off access to maven central and bintray.

Upvotes: 2

Related Questions