1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.impl;
20
21 import java.io.IOException;
22 import java.nio.file.Path;
23 import java.util.Collection;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Objects;
27 import java.util.Set;
28 import java.util.concurrent.ConcurrentHashMap;
29 import java.util.function.Predicate;
30 import java.util.stream.Collectors;
31
32 import org.apache.maven.api.Artifact;
33 import org.apache.maven.api.ArtifactCoordinates;
34 import org.apache.maven.api.DependencyCoordinates;
35 import org.apache.maven.api.DependencyScope;
36 import org.apache.maven.api.Node;
37 import org.apache.maven.api.PathScope;
38 import org.apache.maven.api.PathType;
39 import org.apache.maven.api.Project;
40 import org.apache.maven.api.RemoteRepository;
41 import org.apache.maven.api.Session;
42 import org.apache.maven.api.Version;
43 import org.apache.maven.api.annotations.Nonnull;
44 import org.apache.maven.api.annotations.Nullable;
45 import org.apache.maven.api.di.Named;
46 import org.apache.maven.api.di.Singleton;
47 import org.apache.maven.api.services.ArtifactResolver;
48 import org.apache.maven.api.services.ArtifactResolverException;
49 import org.apache.maven.api.services.ArtifactResolverResult;
50 import org.apache.maven.api.services.DependencyResolver;
51 import org.apache.maven.api.services.DependencyResolverException;
52 import org.apache.maven.api.services.DependencyResolverRequest;
53 import org.apache.maven.api.services.DependencyResolverResult;
54 import org.apache.maven.api.services.ProjectManager;
55 import org.eclipse.aether.DefaultRepositorySystemSession;
56 import org.eclipse.aether.RepositorySystemSession;
57 import org.eclipse.aether.collection.CollectRequest;
58 import org.eclipse.aether.collection.CollectResult;
59 import org.eclipse.aether.collection.DependencyCollectionException;
60 import org.eclipse.aether.graph.DependencyFilter;
61 import org.eclipse.aether.graph.DependencyNode;
62 import org.eclipse.aether.scope.ResolutionScope;
63 import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
64 import org.eclipse.aether.util.graph.transformer.ConflictResolver;
65
66 import static java.util.Objects.requireNonNull;
67 import static org.apache.maven.impl.ImplUtils.cast;
68 import static org.apache.maven.impl.ImplUtils.map;
69
70 @Named
71 @Singleton
72 public class DefaultDependencyResolver implements DependencyResolver {
73
74
75
76
77
78
79
80
81 private final Map<Runtime.Version, PathModularizationCache> moduleCaches;
82
83
84
85
86 public DefaultDependencyResolver() {
87
88 moduleCaches = new ConcurrentHashMap<>();
89 }
90
91
92
93
94
95
96
97 private PathModularizationCache moduleCache(DependencyResolverRequest request) {
98 return moduleCaches.computeIfAbsent(getTargetVersion(request), PathModularizationCache::new);
99 }
100
101
102
103
104
105
106
107
108 static Runtime.Version getTargetVersion(DependencyResolverRequest request) {
109 Version target = request.getTargetVersion();
110 return (target != null) ? Runtime.Version.parse(target.toString()) : Runtime.version();
111 }
112
113 @Nonnull
114 @Override
115 public DependencyResolverResult collect(@Nonnull DependencyResolverRequest request)
116 throws DependencyResolverException, IllegalArgumentException {
117 requireNonNull(request, "request");
118 InternalSession session = InternalSession.from(request.getSession());
119 RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(session, request);
120 try {
121 Artifact rootArtifact;
122 DependencyCoordinates root;
123 Collection<DependencyCoordinates> dependencies;
124 Collection<DependencyCoordinates> managedDependencies;
125 List<RemoteRepository> remoteRepositories;
126 if (request.getProject().isPresent()) {
127 Project project = request.getProject().get();
128 rootArtifact = project.getPomArtifact();
129 root = null;
130 dependencies = project.getDependencies();
131 managedDependencies = project.getManagedDependencies();
132 remoteRepositories = request.getRepositories() != null
133 ? request.getRepositories()
134 : session.getService(ProjectManager.class).getRemoteProjectRepositories(project);
135 } else {
136 rootArtifact = request.getRootArtifact().orElse(null);
137 root = request.getRoot().orElse(null);
138 dependencies = request.getDependencies();
139 managedDependencies = request.getManagedDependencies();
140 remoteRepositories =
141 request.getRepositories() != null ? request.getRepositories() : session.getRemoteRepositories();
142 }
143 ResolutionScope resolutionScope = null;
144 if (request.getPathScope() != null) {
145 resolutionScope = session.getSession()
146 .getScopeManager()
147 .getResolutionScope(request.getPathScope().id())
148 .orElseThrow();
149 }
150 CollectRequest collectRequest = new CollectRequest()
151 .setRootArtifact(rootArtifact != null ? session.toArtifact(rootArtifact) : null)
152 .setRoot(root != null ? session.toDependency(root, false) : null)
153 .setDependencies(session.toDependencies(dependencies, false))
154 .setManagedDependencies(session.toDependencies(managedDependencies, true))
155 .setRepositories(session.toResolvingRepositories(remoteRepositories))
156 .setRequestContext(trace.context())
157 .setTrace(trace.trace());
158 collectRequest.setResolutionScope(resolutionScope);
159
160 RepositorySystemSession systemSession = session.getSession();
161 if (request.getVerbose()) {
162 systemSession = new DefaultRepositorySystemSession(systemSession)
163 .setConfigProperty(ConflictResolver.CONFIG_PROP_VERBOSE, true)
164 .setConfigProperty(DependencyManagerUtils.CONFIG_PROP_VERBOSE, true);
165 }
166
167 try {
168 final CollectResult result =
169 session.getRepositorySystem().collectDependencies(systemSession, collectRequest);
170 return new DefaultDependencyResolverResult(
171 null,
172 moduleCache(request),
173 result.getExceptions(),
174 session.getNode(result.getRoot(), request.getVerbose()),
175 0);
176 } catch (DependencyCollectionException e) {
177 String enhancedMessage = enhanceCollectionError(e, collectRequest);
178 throw new DependencyResolverException(enhancedMessage, e);
179 }
180 } finally {
181 RequestTraceHelper.exit(trace);
182 }
183 }
184
185 @Nonnull
186 @Override
187 public List<Node> flatten(@Nonnull Session s, @Nonnull Node node, @Nullable PathScope scope)
188 throws DependencyResolverException {
189 InternalSession session = InternalSession.from(s);
190 DependencyNode root = cast(AbstractNode.class, node, "node").getDependencyNode();
191 List<DependencyNode> dependencies = session.getRepositorySystem()
192 .flattenDependencyNodes(session.getSession(), root, getScopeDependencyFilter(scope));
193 dependencies.remove(root);
194 return map(dependencies, session::getNode);
195 }
196
197 private static DependencyFilter getScopeDependencyFilter(PathScope scope) {
198 if (scope == null) {
199 return null;
200 }
201 Set<String> scopes =
202 scope.dependencyScopes().stream().map(DependencyScope::id).collect(Collectors.toSet());
203 return (n, p) -> {
204 org.eclipse.aether.graph.Dependency d = n.getDependency();
205 return d == null || scopes.contains(d.getScope());
206 };
207 }
208
209
210
211
212
213
214
215 @Override
216 public DependencyResolverResult resolve(DependencyResolverRequest request)
217 throws DependencyResolverException, ArtifactResolverException {
218 InternalSession session =
219 InternalSession.from(requireNonNull(request, "request").getSession());
220 RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(session, request);
221 DependencyResolverResult result;
222 try {
223 DependencyResolverResult collectorResult = collect(request);
224 List<RemoteRepository> repositories = request.getRepositories() != null
225 ? request.getRepositories()
226 : request.getProject().isPresent()
227 ? session.getService(ProjectManager.class)
228 .getRemoteProjectRepositories(
229 request.getProject().get())
230 : session.getRemoteRepositories();
231 if (request.getRequestType() == DependencyResolverRequest.RequestType.COLLECT) {
232 result = collectorResult;
233 } else {
234 List<Node> nodes = flatten(session, collectorResult.getRoot(), request.getPathScope());
235 List<ArtifactCoordinates> coordinates = nodes.stream()
236 .map(Node::getDependency)
237 .filter(Objects::nonNull)
238 .map(Artifact::toCoordinates)
239 .collect(Collectors.toList());
240 Predicate<PathType> filter = request.getPathTypeFilter();
241 DefaultDependencyResolverResult resolverResult = new DefaultDependencyResolverResult(
242 null,
243 moduleCache(request),
244 collectorResult.getExceptions(),
245 collectorResult.getRoot(),
246 nodes.size());
247 if (request.getRequestType() == DependencyResolverRequest.RequestType.FLATTEN) {
248 for (Node node : nodes) {
249 resolverResult.addNode(node);
250 }
251 } else {
252 ArtifactResolverResult artifactResolverResult =
253 session.getService(ArtifactResolver.class).resolve(session, coordinates, repositories);
254 for (Node node : nodes) {
255 Path path = (node.getArtifact() != null)
256 ? artifactResolverResult
257 .getResult(node.getArtifact().toCoordinates())
258 .getPath()
259 : null;
260 try {
261 resolverResult.addDependency(node, node.getDependency(), filter, path);
262 } catch (IOException e) {
263 throw cannotReadModuleInfo(path, e);
264 }
265 }
266 }
267 result = resolverResult;
268 }
269 } finally {
270 RequestTraceHelper.exit(trace);
271 }
272 return result;
273 }
274
275 private static DependencyResolverException cannotReadModuleInfo(final Path path, final IOException cause) {
276 return new DependencyResolverException("Cannot read module information of " + path, cause);
277 }
278
279 private static boolean containsUnresolvedExpression(String value) {
280 return value != null && value.contains("${") && value.contains("}");
281 }
282
283 private static String enhanceCollectionError(DependencyCollectionException e, CollectRequest request) {
284 if (e.getMessage() != null && e.getMessage().contains("Invalid Collect Request")) {
285 StringBuilder enhanced = new StringBuilder();
286 enhanced.append("Failed to collect dependencies");
287
288 org.eclipse.aether.graph.Dependency root = request.getRoot();
289 if (root != null && root.getArtifact() != null) {
290 org.eclipse.aether.artifact.Artifact artifact = root.getArtifact();
291 String groupId = artifact.getGroupId();
292 String artifactId = artifact.getArtifactId();
293 String version = artifact.getVersion();
294
295 if (containsUnresolvedExpression(groupId)
296 || containsUnresolvedExpression(artifactId)
297 || containsUnresolvedExpression(version)) {
298 enhanced.append(" due to unresolved expression(s) in dependency: ")
299 .append(groupId)
300 .append(":")
301 .append(artifactId)
302 .append(":")
303 .append(version)
304 .append(".\n")
305 .append("Please check that all properties are defined in your POM or settings.xml.");
306 return enhanced.toString();
307 }
308 }
309
310 for (org.eclipse.aether.graph.Dependency dep : request.getDependencies()) {
311 if (dep != null && dep.getArtifact() != null) {
312 org.eclipse.aether.artifact.Artifact artifact = dep.getArtifact();
313 String groupId = artifact.getGroupId();
314 String artifactId = artifact.getArtifactId();
315 String version = artifact.getVersion();
316
317 if (containsUnresolvedExpression(groupId)
318 || containsUnresolvedExpression(artifactId)
319 || containsUnresolvedExpression(version)) {
320 enhanced.append(" due to unresolved expression(s) in dependency: ")
321 .append(groupId)
322 .append(":")
323 .append(artifactId)
324 .append(":")
325 .append(version)
326 .append(".\n")
327 .append("Please check that all properties are defined in your POM or settings.xml.");
328 return enhanced.toString();
329 }
330 }
331 }
332 }
333 return e.getMessage();
334 }
335 }