Skip to content

Commit

Permalink
[MRESOLVER-518] Version selector improvements (apache#450)
Browse files Browse the repository at this point in the history
Version selector improvements:
* introduce strategy how to select "winner" (so far it was cemented "nearest") and optionally perform some enforcements
* supplier retains same behaviour as before

Big fat note: this is NOT to alter Maven4 behaviour, but to make Resolver 2 more versatile for example for "diagnostic purposes" (while Maven may still benefit from these changes as well, by having some "strict version strategy" mode for example).

---

https://issues.apache.org/jira/browse/MRESOLVER-518
  • Loading branch information
cstamas committed Apr 12, 2024
1 parent d823824 commit cd7e88a
Show file tree
Hide file tree
Showing 9 changed files with 736 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import org.eclipse.aether.graph.DependencyNode;
import org.eclipse.aether.version.VersionConstraint;

import static java.util.Objects.requireNonNull;

/**
* Thrown in case of an unsolvable conflict between different version constraints for a dependency.
*/
Expand All @@ -42,9 +44,25 @@ public class UnsolvableVersionConflictException extends RepositoryException {
* Creates a new exception with the specified paths to conflicting nodes in the dependency graph.
*
* @param paths The paths to the dependency nodes that participate in the version conflict, may be {@code null}.
* @deprecated Use {@link #UnsolvableVersionConflictException(String, Collection)} instead.
*/
@Deprecated
public UnsolvableVersionConflictException(Collection<? extends List<? extends DependencyNode>> paths) {
super("Could not resolve version conflict among " + toPaths(paths));
this("Unsolvable hard constraint combination", paths);
}

/**
* Creates a new exception with the specified paths to conflicting nodes in the dependency graph.
*
* @param message The strategy that throw the bucket in, must not be {@code null}. Should provide concise message
* why this exception was thrown.
* @param paths The paths to the dependency nodes that participate in the version conflict, may be {@code null}.
*
* @since 2.0.0
*/
public UnsolvableVersionConflictException(
String message, Collection<? extends List<? extends DependencyNode>> paths) {
super(requireNonNull(message, "message") + "; Could not resolve version conflict among " + toPaths(paths));
if (paths == null) {
this.paths = Collections.emptyList();
this.versions = Collections.emptyList();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.resolver.examples;

import org.apache.maven.resolver.examples.util.Booter;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession.CloseableSession;
import org.eclipse.aether.RepositorySystemSession.SessionBuilder;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.collection.UnsolvableVersionConflictException;
import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
import org.eclipse.aether.resolution.ArtifactDescriptorResult;
import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
import org.eclipse.aether.util.graph.transformer.ChainedDependencyGraphTransformer;
import org.eclipse.aether.util.graph.transformer.ConfigurableVersionSelector;
import org.eclipse.aether.util.graph.transformer.ConflictResolver;
import org.eclipse.aether.util.graph.transformer.JavaDependencyContextRefiner;
import org.eclipse.aether.util.graph.transformer.JavaScopeDeriver;
import org.eclipse.aether.util.graph.transformer.JavaScopeSelector;
import org.eclipse.aether.util.graph.transformer.SimpleOptionalitySelector;

/**
* Visualizes the transitive dependencies of an artifact similar to m2e's dependency hierarchy view.
*/
public class GetDependencyHierarchyWithConflicts {

/**
* Main.
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
System.out.println("------------------------------------------------------------");
System.out.println(GetDependencyHierarchyWithConflicts.class.getSimpleName());

// incompatible versions: two incompatible versions present in graph
try (RepositorySystem system = Booter.newRepositorySystem(Booter.selectFactory(args))) {
SessionBuilder sessionBuilder = Booter.newRepositorySystemSession(system);
sessionBuilder.setConfigProperty(ConflictResolver.CONFIG_PROP_VERBOSE, true);
sessionBuilder.setConfigProperty(DependencyManagerUtils.CONFIG_PROP_VERBOSE, true);
try (CloseableSession session = sessionBuilder
.setDependencyGraphTransformer(new ChainedDependencyGraphTransformer(
new ConflictResolver(
new ConfigurableVersionSelector(
new ConfigurableVersionSelector.MajorVersionConvergence(
new ConfigurableVersionSelector.Nearest())),
new JavaScopeSelector(),
new SimpleOptionalitySelector(),
new JavaScopeDeriver()),
new JavaDependencyContextRefiner()))
.build()) {
Artifact artifact = new DefaultArtifact("org.apache.maven.shared:maven-dependency-tree:3.0.1");

ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
descriptorRequest.setArtifact(artifact);
descriptorRequest.setRepositories(Booter.newRepositories(system, session));
ArtifactDescriptorResult descriptorResult = system.readArtifactDescriptor(session, descriptorRequest);

CollectRequest collectRequest = new CollectRequest();
collectRequest.setRootArtifact(descriptorResult.getArtifact());
collectRequest.setDependencies(descriptorResult.getDependencies());
collectRequest.setManagedDependencies(descriptorResult.getManagedDependencies());
collectRequest.setRepositories(descriptorRequest.getRepositories());

system.collectDependencies(session, collectRequest);
throw new IllegalStateException("should fail");
}
} catch (Exception e) {
e.printStackTrace();
if (e.getCause() instanceof UnsolvableVersionConflictException) {
String cause = e.getCause().getMessage();
if (!cause.contains(
"Incompatible versions for org.apache.maven:maven-core, incompatible versions: [2.0], all versions [2.0, 3.0.4]")) {
throw new IllegalStateException("should fail due incompatible versions");
}
} else {
throw new IllegalStateException("should fail due incompatible versions");
}
}

// dependency divergence: multiple versions of same GA present in graph
try (RepositorySystem system = Booter.newRepositorySystem(Booter.selectFactory(args))) {
SessionBuilder sessionBuilder = Booter.newRepositorySystemSession(system);
sessionBuilder.setConfigProperty(ConflictResolver.CONFIG_PROP_VERBOSE, true);
sessionBuilder.setConfigProperty(DependencyManagerUtils.CONFIG_PROP_VERBOSE, true);
try (CloseableSession session = sessionBuilder
.setDependencyGraphTransformer(new ChainedDependencyGraphTransformer(
new ConflictResolver(
new ConfigurableVersionSelector(new ConfigurableVersionSelector.VersionConvergence(
new ConfigurableVersionSelector.Nearest())),
new JavaScopeSelector(),
new SimpleOptionalitySelector(),
new JavaScopeDeriver()),
new JavaDependencyContextRefiner()))
.build()) {
Artifact artifact = new DefaultArtifact("org.apache.maven.shared:maven-dependency-tree:3.1.0");

ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
descriptorRequest.setArtifact(artifact);
descriptorRequest.setRepositories(Booter.newRepositories(system, session));
ArtifactDescriptorResult descriptorResult = system.readArtifactDescriptor(session, descriptorRequest);

CollectRequest collectRequest = new CollectRequest();
collectRequest.setRootArtifact(descriptorResult.getArtifact());
collectRequest.setDependencies(descriptorResult.getDependencies());
collectRequest.setManagedDependencies(descriptorResult.getManagedDependencies());
collectRequest.setRepositories(descriptorRequest.getRepositories());

system.collectDependencies(session, collectRequest);
throw new IllegalStateException("should fail");
}
} catch (Exception e) {
e.printStackTrace();
if (e.getCause() instanceof UnsolvableVersionConflictException) {
String cause = e.getCause().getMessage();
if (!cause.contains(
"Convergence violated for org.codehaus.plexus:plexus-utils, versions present: [2.1, 1.5.5, 2.0.6]")) {
throw new IllegalStateException("should fail due convergence violation");
}
} else {
throw new IllegalStateException("should fail due convergence violation");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@
import org.eclipse.aether.util.graph.selector.AndDependencySelector;
import org.eclipse.aether.util.graph.selector.ExclusionDependencySelector;
import org.eclipse.aether.util.graph.transformer.ChainedDependencyGraphTransformer;
import org.eclipse.aether.util.graph.transformer.ConfigurableVersionSelector;
import org.eclipse.aether.util.graph.transformer.ConflictResolver;
import org.eclipse.aether.util.graph.transformer.NearestVersionSelector;
import org.eclipse.aether.util.graph.transformer.SimpleOptionalitySelector;
import org.eclipse.aether.util.graph.visitor.CloningDependencyVisitor;
import org.eclipse.aether.util.graph.visitor.FilteringDependencyVisitor;
Expand Down Expand Up @@ -157,7 +157,7 @@ public DependencySelector getDependencySelector(ResolutionScope resolutionScope)
public DependencyGraphTransformer getDependencyGraphTransformer(ResolutionScope resolutionScope) {
return new ChainedDependencyGraphTransformer(
new ConflictResolver(
new NearestVersionSelector(), new ManagedScopeSelector(this),
new ConfigurableVersionSelector(), new ManagedScopeSelector(this),
new SimpleOptionalitySelector(), new ManagedScopeDeriver(this)),
new ManagedDependencyContextRefiner(this));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@
import org.eclipse.aether.util.graph.selector.AndDependencySelector;
import org.eclipse.aether.util.graph.selector.ExclusionDependencySelector;
import org.eclipse.aether.util.graph.transformer.ChainedDependencyGraphTransformer;
import org.eclipse.aether.util.graph.transformer.ConfigurableVersionSelector;
import org.eclipse.aether.util.graph.transformer.ConflictResolver;
import org.eclipse.aether.util.graph.transformer.JavaDependencyContextRefiner;
import org.eclipse.aether.util.graph.transformer.JavaScopeDeriver;
import org.eclipse.aether.util.graph.transformer.JavaScopeSelector;
import org.eclipse.aether.util.graph.transformer.NearestVersionSelector;
import org.eclipse.aether.util.graph.transformer.SimpleOptionalitySelector;
import org.eclipse.aether.util.graph.traverser.FatArtifactTraverser;
import org.eclipse.aether.util.repository.SimpleArtifactDescriptorPolicy;
Expand All @@ -69,6 +69,7 @@ public SessionBuilderSupplier(RepositorySystem repositorySystem) {
}

protected void configureSessionBuilder(SessionBuilder session) {
session.setSystemProperties(System.getProperties());
session.setDependencyTraverser(getDependencyTraverser());
session.setDependencyManager(getDependencyManager());
session.setDependencySelector(getDependencySelector());
Expand All @@ -95,7 +96,7 @@ protected DependencySelector getDependencySelector() {
protected DependencyGraphTransformer getDependencyGraphTransformer() {
return new ChainedDependencyGraphTransformer(
new ConflictResolver(
new NearestVersionSelector(), new JavaScopeSelector(),
new ConfigurableVersionSelector(), new JavaScopeSelector(),
new SimpleOptionalitySelector(), new JavaScopeDeriver()),
new JavaDependencyContextRefiner());
}
Expand Down
Loading

0 comments on commit cd7e88a

Please sign in to comment.