Conflict Resolvers
Synopsis
A dependency version conflict occurs when building a Maven project whose dependency graph pulls in two different versions of the same dependency. Maven must decide which version of the conflicting dependency to use before proceeding with the build. The behaviour thus far has always been to select the version in the dependency graph that is 'nearest' to the project being built, where the distance is defined to be the number of transitive steps between two nodes in the graph. This aim of this proposal is to allow alternative conflict resolution techniques to be used when building projects with Maven.
Design
The existing but unused ConflictResolver interface will become the basis for the conflict resolver API. The following method will be added to handle conflict resolutions:
/** * Determines which of the specified versions of an artifact to use when there are conflicting declarations. * * @param node1 the first artifact declaration * @param node2 the second artifact declaration * @return the artifact declaration to use: node1; node2; or null if this conflict cannot be resolved */ ResolutionNode resolveConflict( ResolutionNode node1, ResolutionNode node2 );
The following ConflictResolver
implementations will be provided:
Name |
Strategy |
---|---|
nearest |
Resolves conflicting artifacts by always selecting the |
farthest |
Resolves conflicting artifacts by always selecting the |
newest |
Resolves conflicting artifacts by always selecting the |
oldest |
Resolves conflicting artifacts by always selecting the |
ArtifactCollector will have an additional method added to accept a list of ConflictResolvers
:
ArtifactResolutionResult collect( Set artifacts, Artifact originatingArtifact, Map managedVersions, ArtifactRepository localRepository, List remoteRepositories, ArtifactMetadataSource source, ArtifactFilter filter, List listeners, List conflictResolvers ) throws ArtifactResolutionException
Correspondingly, DefaultArtifactCollector will be updated to process the chain of ConflictResolvers
to resolve dependency version conflicts. The chain will be processed until one ConflictResolver
can resolve the conflict. In the event that no ConflictResolvers
can resolve the conflict, an ArtifactResolutionException will be thrown to fail the build.
ArtifactResolver will also have a corresponding new method in order to delegate to the new ArtifactCollector
method:
ArtifactResolutionResult resolveTransitively( Set artifacts, Artifact originatingArtifact, Map managedVersions, ArtifactRepository localRepository, List remoteRepositories, ArtifactMetadataSource source, ArtifactFilter filter, List listeners, List conflictResolvers ) throws ArtifactResolutionException, ArtifactNotFoundException;
To ensure that the conflict resolution strategy is consistent throughout the build, all usages of ArtifactCollector
and ArtifactResolver
within Maven will be updated to use these new overloaded versions. The list of ConflictResolvers
to use will be obtained from the MavenProject
being built. In order to preserve the 2.0.x POM schema, a POM property will initially be used to configure the ConflictResolver
chain. The POM property will be named mavenConflictResolvers
and its value will be defined as a comma-separated list of conflict resolver names. For example:
<properties> <mavenConflictResolvers>newest,nearest</mavenConflictResolvers> </properties>
For 2.1.x, it is likely that the POM schema will be modified to introduce a more appropriate syntax for configuring conflict resolvers. For example:
<dependencies> <conflictResolvers> <conflictResolver>newest</conflictResolver> <conflictResolver>nearest</conflictResolver> </conflictResolvers> </dependencies>
For backwards compatibility, if a project does not declare any conflict resolvers then the 'nearest' conflict resolver will be used by default.
5 Comments
Reto Gmür
I'd suggest to add also a conflict-resolution method 'fail' which causes a build to fail id two different versions of an artifact are referenced. Generally this would enforce updating the project basing on an older version.
Ideally, I should be able to set the 'fail' mode only for dependencies of a set of projects, e.g. those with group-id matching org.example.*.
Unknown User (mihhon)
why do not allow the user to specify explicitly the compatibility of artifact versions so maven could decide to choose the latest version if it is backward-compatible ? and to fail the build process in the case of non-compatibility with a readable error message
Paul Gier
It would be helpful to also have a way to load a custom conflict resolvers as an extension. Maybe the custom resolver could have it's name defined internally, and then you could choose to use it by doing something like:
Paul Gier
Mark, do you have an example of when you would want to use more than one conflict resolver? It seems like only one would ever be needed for a single build.
Unknown User (irobertson)
How would ConflictResolvers interact with VersionRanges? The current behavior of maven when a one or more VersionRanges is introduced is to select the highest numbered version available in the repository which lives in the intersection of all VersionRanges. Would a ConflictResolver override this behavior, or be overridden by it?