문제

The place where I work uses Maven and we have lots of internal libraries. We try to make changes in a backward-compatible manner, but sometimes one of our libraries requires a newer version of another library. That can cause problems if an end product doesn't end up pulling in the newer library version.

For each end product, we have a dependencyManagement section that declares which versions of transitive dependencies should be used for the project. We do this instead of letting Maven figure out which version to use because we want control of which versions of libraries are being used.

Even if we let maven figure out which versions of libraries should be used, it's possible that the older library may be used, which could cause ClassNotFoundExceptions, etc...

Is there a Maven plugin or way to determine if my project is using an older version of a dependency, when one of the project's dependencies requires a newer version?

There is mvn dependency:tree but we have lots of dependencies, and I don't want to have to look through the giant list by hand.

Thanks!

EDIT

Since we have lots of libraries, if the end product uses libraries A, B, and C, and both A and B use a different version of C, the latest version of C is not always used. From Introduction to the Dependency Mechanism:

Dependency mediation - this determines what version of a dependency will be used when multiple versions of an artifact are encountered. Currently, Maven 2.0 only supports using the "nearest definition" which means that it will use 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, until Maven 2.0.8 it was not defined which one would win, but since Maven 2.0.9 it's the order in the declaration that counts: the first declaration wins.

도움이 되었습니까?

해결책

Since we almost always make changes in a backward-compatible manner, it looks like the best solution for us is to ensure that the latest versions of dependencies are used. The Maven Enforcer plugin with the "Require Upper Bound Dependencies" feature accomplishes this.

From http://maven.apache.org/enforcer/enforcer-rules/requireUpperBoundDeps.html:

This rule requires that the version for each dependency resolved during a build, is equal to or higher than all transitive dependency declarations. The version of each dependency resolved during the build will normally be the version specified in the POM or the version with the least transitive steps (the "nearest" definition). For more information about Maven dependency resolution, see the Maven site.

Here is a concrete example. This will cause a build to fail:

  <dependencies>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.4.0</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>0.9.9</version>
      <!-- Depends on org.slf4j:slf4j-api:1.5.0 -->
    </dependency>
  </dependencies>

Because the project will run logback-classic 0.9.9 with slf4j-api 1.4.0 and slf4j-api 1.4.0 is probably not forwards compatible with slf4j-api 1.5.0.

This is the log message:

Failed while enforcing RequireUpperBoundDeps. The error(s) are [
RequireUpperBoundDeps error for org.slf4j:slf4j-api:1.4.0 paths to dependency are:
+-test:TestParent:1.0-SNAPSHOT
  +-org.slf4j:slf4j-api:1.4.0
and
+-test:TestParent:1.0-SNAPSHOT
  +-ch.qos.logback:logback-classic:0.9.9
    +-org.slf4j:slf4j-api:1.5.0
]

We're probably going to set up this plugin in a profile which we can turn on or off.

다른 팁

I'm not sure there's a plugin that will do as you ask. In general, a newer version will be the one you'll settle on, but that's not always a hard rule - sometimes you'll have a newer version pulled in but an older version is actually what you require.

In my experience, you have to treat each dependency problem in isolation.

The way we handle this is by combination of using the <dependencyManagement/> section as you suggest, along with the Maven Enforcer plugin with the <DependencyConvergence/> rule.

The dependencyConvergence rule:

...requires that dependency version numbers converge. If a project has two dependencies, A and B, both depending on the same artifact, C, this rule will fail the build if A depends on a different version of C then the version of C depended on by B.

Basically, if you've got 2 different versions of the same lib in your dependency tree, then the validate phase fails.

So:

The way I do it is - we usually have multi module Maven projects and I try to define common dependency versions in the <dependencyManagement/> section at the parent level - EG Spring, Jersey etc. Then at the child module levels, define specialist <dependencyManagement/> section as needed.

In all our poms we define the Maven Enforcer plugin configured with the <dependencyConvergence/> rule as follows:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-enforcer-plugin</artifactId>
    <version>1.3.1</version>
    <executions>
    <execution>
        <id>enforce-versions</id>
        <goals>
        <goal>enforce</goal>
        </goals>
    </execution>
    </executions>
    <configuration>
    <rules>
        <DependencyConvergence/>
    </rules>
    <fail>true</fail>
    </configuration>
</plugin>

Then on every Maven build, if someone's added a new dependency (or there's a new transitive dependency) that clashes with another, your build will fail, and you'll get a (slightly repetitive) log of the dependency issues for you to deal with.

As an end point, I don't see the Maven Enforcer plugin used much, but it's got a load of really useful rules as well as the ability to make custom rules.

If it really was the newest version of a dependency you always wanted, I'm sure you could write a simple rule to do it.

Hope this helps,

Will

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top