Skip to content

Commit cd7e88a

Browse files
authored
[MRESOLVER-518] Version selector improvements (apache#450)
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
1 parent d823824 commit cd7e88a

File tree

9 files changed

+736
-5
lines changed

9 files changed

+736
-5
lines changed

maven-resolver-api/src/main/java/org/eclipse/aether/collection/UnsolvableVersionConflictException.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import org.eclipse.aether.graph.DependencyNode;
3030
import org.eclipse.aether.version.VersionConstraint;
3131

32+
import static java.util.Objects.requireNonNull;
33+
3234
/**
3335
* Thrown in case of an unsolvable conflict between different version constraints for a dependency.
3436
*/
@@ -42,9 +44,25 @@ public class UnsolvableVersionConflictException extends RepositoryException {
4244
* Creates a new exception with the specified paths to conflicting nodes in the dependency graph.
4345
*
4446
* @param paths The paths to the dependency nodes that participate in the version conflict, may be {@code null}.
47+
* @deprecated Use {@link #UnsolvableVersionConflictException(String, Collection)} instead.
4548
*/
49+
@Deprecated
4650
public UnsolvableVersionConflictException(Collection<? extends List<? extends DependencyNode>> paths) {
47-
super("Could not resolve version conflict among " + toPaths(paths));
51+
this("Unsolvable hard constraint combination", paths);
52+
}
53+
54+
/**
55+
* Creates a new exception with the specified paths to conflicting nodes in the dependency graph.
56+
*
57+
* @param message The strategy that throw the bucket in, must not be {@code null}. Should provide concise message
58+
* why this exception was thrown.
59+
* @param paths The paths to the dependency nodes that participate in the version conflict, may be {@code null}.
60+
*
61+
* @since 2.0.0
62+
*/
63+
public UnsolvableVersionConflictException(
64+
String message, Collection<? extends List<? extends DependencyNode>> paths) {
65+
super(requireNonNull(message, "message") + "; Could not resolve version conflict among " + toPaths(paths));
4866
if (paths == null) {
4967
this.paths = Collections.emptyList();
5068
this.versions = Collections.emptyList();
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.resolver.examples;
20+
21+
import org.apache.maven.resolver.examples.util.Booter;
22+
import org.eclipse.aether.RepositorySystem;
23+
import org.eclipse.aether.RepositorySystemSession.CloseableSession;
24+
import org.eclipse.aether.RepositorySystemSession.SessionBuilder;
25+
import org.eclipse.aether.artifact.Artifact;
26+
import org.eclipse.aether.artifact.DefaultArtifact;
27+
import org.eclipse.aether.collection.CollectRequest;
28+
import org.eclipse.aether.collection.UnsolvableVersionConflictException;
29+
import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
30+
import org.eclipse.aether.resolution.ArtifactDescriptorResult;
31+
import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
32+
import org.eclipse.aether.util.graph.transformer.ChainedDependencyGraphTransformer;
33+
import org.eclipse.aether.util.graph.transformer.ConfigurableVersionSelector;
34+
import org.eclipse.aether.util.graph.transformer.ConflictResolver;
35+
import org.eclipse.aether.util.graph.transformer.JavaDependencyContextRefiner;
36+
import org.eclipse.aether.util.graph.transformer.JavaScopeDeriver;
37+
import org.eclipse.aether.util.graph.transformer.JavaScopeSelector;
38+
import org.eclipse.aether.util.graph.transformer.SimpleOptionalitySelector;
39+
40+
/**
41+
* Visualizes the transitive dependencies of an artifact similar to m2e's dependency hierarchy view.
42+
*/
43+
public class GetDependencyHierarchyWithConflicts {
44+
45+
/**
46+
* Main.
47+
* @param args
48+
* @throws Exception
49+
*/
50+
public static void main(String[] args) throws Exception {
51+
System.out.println("------------------------------------------------------------");
52+
System.out.println(GetDependencyHierarchyWithConflicts.class.getSimpleName());
53+
54+
// incompatible versions: two incompatible versions present in graph
55+
try (RepositorySystem system = Booter.newRepositorySystem(Booter.selectFactory(args))) {
56+
SessionBuilder sessionBuilder = Booter.newRepositorySystemSession(system);
57+
sessionBuilder.setConfigProperty(ConflictResolver.CONFIG_PROP_VERBOSE, true);
58+
sessionBuilder.setConfigProperty(DependencyManagerUtils.CONFIG_PROP_VERBOSE, true);
59+
try (CloseableSession session = sessionBuilder
60+
.setDependencyGraphTransformer(new ChainedDependencyGraphTransformer(
61+
new ConflictResolver(
62+
new ConfigurableVersionSelector(
63+
new ConfigurableVersionSelector.MajorVersionConvergence(
64+
new ConfigurableVersionSelector.Nearest())),
65+
new JavaScopeSelector(),
66+
new SimpleOptionalitySelector(),
67+
new JavaScopeDeriver()),
68+
new JavaDependencyContextRefiner()))
69+
.build()) {
70+
Artifact artifact = new DefaultArtifact("org.apache.maven.shared:maven-dependency-tree:3.0.1");
71+
72+
ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
73+
descriptorRequest.setArtifact(artifact);
74+
descriptorRequest.setRepositories(Booter.newRepositories(system, session));
75+
ArtifactDescriptorResult descriptorResult = system.readArtifactDescriptor(session, descriptorRequest);
76+
77+
CollectRequest collectRequest = new CollectRequest();
78+
collectRequest.setRootArtifact(descriptorResult.getArtifact());
79+
collectRequest.setDependencies(descriptorResult.getDependencies());
80+
collectRequest.setManagedDependencies(descriptorResult.getManagedDependencies());
81+
collectRequest.setRepositories(descriptorRequest.getRepositories());
82+
83+
system.collectDependencies(session, collectRequest);
84+
throw new IllegalStateException("should fail");
85+
}
86+
} catch (Exception e) {
87+
e.printStackTrace();
88+
if (e.getCause() instanceof UnsolvableVersionConflictException) {
89+
String cause = e.getCause().getMessage();
90+
if (!cause.contains(
91+
"Incompatible versions for org.apache.maven:maven-core, incompatible versions: [2.0], all versions [2.0, 3.0.4]")) {
92+
throw new IllegalStateException("should fail due incompatible versions");
93+
}
94+
} else {
95+
throw new IllegalStateException("should fail due incompatible versions");
96+
}
97+
}
98+
99+
// dependency divergence: multiple versions of same GA present in graph
100+
try (RepositorySystem system = Booter.newRepositorySystem(Booter.selectFactory(args))) {
101+
SessionBuilder sessionBuilder = Booter.newRepositorySystemSession(system);
102+
sessionBuilder.setConfigProperty(ConflictResolver.CONFIG_PROP_VERBOSE, true);
103+
sessionBuilder.setConfigProperty(DependencyManagerUtils.CONFIG_PROP_VERBOSE, true);
104+
try (CloseableSession session = sessionBuilder
105+
.setDependencyGraphTransformer(new ChainedDependencyGraphTransformer(
106+
new ConflictResolver(
107+
new ConfigurableVersionSelector(new ConfigurableVersionSelector.VersionConvergence(
108+
new ConfigurableVersionSelector.Nearest())),
109+
new JavaScopeSelector(),
110+
new SimpleOptionalitySelector(),
111+
new JavaScopeDeriver()),
112+
new JavaDependencyContextRefiner()))
113+
.build()) {
114+
Artifact artifact = new DefaultArtifact("org.apache.maven.shared:maven-dependency-tree:3.1.0");
115+
116+
ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
117+
descriptorRequest.setArtifact(artifact);
118+
descriptorRequest.setRepositories(Booter.newRepositories(system, session));
119+
ArtifactDescriptorResult descriptorResult = system.readArtifactDescriptor(session, descriptorRequest);
120+
121+
CollectRequest collectRequest = new CollectRequest();
122+
collectRequest.setRootArtifact(descriptorResult.getArtifact());
123+
collectRequest.setDependencies(descriptorResult.getDependencies());
124+
collectRequest.setManagedDependencies(descriptorResult.getManagedDependencies());
125+
collectRequest.setRepositories(descriptorRequest.getRepositories());
126+
127+
system.collectDependencies(session, collectRequest);
128+
throw new IllegalStateException("should fail");
129+
}
130+
} catch (Exception e) {
131+
e.printStackTrace();
132+
if (e.getCause() instanceof UnsolvableVersionConflictException) {
133+
String cause = e.getCause().getMessage();
134+
if (!cause.contains(
135+
"Convergence violated for org.codehaus.plexus:plexus-utils, versions present: [2.1, 1.5.5, 2.0.6]")) {
136+
throw new IllegalStateException("should fail due convergence violation");
137+
}
138+
} else {
139+
throw new IllegalStateException("should fail due convergence violation");
140+
}
141+
}
142+
}
143+
}

maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/scope/ScopeManagerImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@
4949
import org.eclipse.aether.util.graph.selector.AndDependencySelector;
5050
import org.eclipse.aether.util.graph.selector.ExclusionDependencySelector;
5151
import org.eclipse.aether.util.graph.transformer.ChainedDependencyGraphTransformer;
52+
import org.eclipse.aether.util.graph.transformer.ConfigurableVersionSelector;
5253
import org.eclipse.aether.util.graph.transformer.ConflictResolver;
53-
import org.eclipse.aether.util.graph.transformer.NearestVersionSelector;
5454
import org.eclipse.aether.util.graph.transformer.SimpleOptionalitySelector;
5555
import org.eclipse.aether.util.graph.visitor.CloningDependencyVisitor;
5656
import org.eclipse.aether.util.graph.visitor.FilteringDependencyVisitor;
@@ -157,7 +157,7 @@ public DependencySelector getDependencySelector(ResolutionScope resolutionScope)
157157
public DependencyGraphTransformer getDependencyGraphTransformer(ResolutionScope resolutionScope) {
158158
return new ChainedDependencyGraphTransformer(
159159
new ConflictResolver(
160-
new NearestVersionSelector(), new ManagedScopeSelector(this),
160+
new ConfigurableVersionSelector(), new ManagedScopeSelector(this),
161161
new SimpleOptionalitySelector(), new ManagedScopeDeriver(this)),
162162
new ManagedDependencyContextRefiner(this));
163163
}

maven-resolver-supplier-mvn3/src/main/java/org/eclipse/aether/supplier/SessionBuilderSupplier.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@
3939
import org.eclipse.aether.util.graph.selector.AndDependencySelector;
4040
import org.eclipse.aether.util.graph.selector.ExclusionDependencySelector;
4141
import org.eclipse.aether.util.graph.transformer.ChainedDependencyGraphTransformer;
42+
import org.eclipse.aether.util.graph.transformer.ConfigurableVersionSelector;
4243
import org.eclipse.aether.util.graph.transformer.ConflictResolver;
4344
import org.eclipse.aether.util.graph.transformer.JavaDependencyContextRefiner;
4445
import org.eclipse.aether.util.graph.transformer.JavaScopeDeriver;
4546
import org.eclipse.aether.util.graph.transformer.JavaScopeSelector;
46-
import org.eclipse.aether.util.graph.transformer.NearestVersionSelector;
4747
import org.eclipse.aether.util.graph.transformer.SimpleOptionalitySelector;
4848
import org.eclipse.aether.util.graph.traverser.FatArtifactTraverser;
4949
import org.eclipse.aether.util.repository.SimpleArtifactDescriptorPolicy;
@@ -69,6 +69,7 @@ public SessionBuilderSupplier(RepositorySystem repositorySystem) {
6969
}
7070

7171
protected void configureSessionBuilder(SessionBuilder session) {
72+
session.setSystemProperties(System.getProperties());
7273
session.setDependencyTraverser(getDependencyTraverser());
7374
session.setDependencyManager(getDependencyManager());
7475
session.setDependencySelector(getDependencySelector());
@@ -95,7 +96,7 @@ protected DependencySelector getDependencySelector() {
9596
protected DependencyGraphTransformer getDependencyGraphTransformer() {
9697
return new ChainedDependencyGraphTransformer(
9798
new ConflictResolver(
98-
new NearestVersionSelector(), new JavaScopeSelector(),
99+
new ConfigurableVersionSelector(), new JavaScopeSelector(),
99100
new SimpleOptionalitySelector(), new JavaScopeDeriver()),
100101
new JavaDependencyContextRefiner());
101102
}

0 commit comments

Comments
 (0)