1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.aether.internal.impl.collect;
20
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.HashSet;
25 import java.util.LinkedHashMap;
26 import java.util.List;
27 import java.util.Map;
28
29 import org.eclipse.aether.DefaultRepositorySystemSession;
30 import org.eclipse.aether.RepositoryException;
31 import org.eclipse.aether.RepositorySystemSession;
32 import org.eclipse.aether.RequestTrace;
33 import org.eclipse.aether.artifact.Artifact;
34 import org.eclipse.aether.artifact.ArtifactProperties;
35 import org.eclipse.aether.collection.CollectRequest;
36 import org.eclipse.aether.collection.CollectResult;
37 import org.eclipse.aether.collection.DependencyCollectionException;
38 import org.eclipse.aether.collection.DependencyGraphTransformer;
39 import org.eclipse.aether.collection.DependencyTraverser;
40 import org.eclipse.aether.collection.VersionFilter;
41 import org.eclipse.aether.graph.DefaultDependencyNode;
42 import org.eclipse.aether.graph.Dependency;
43 import org.eclipse.aether.graph.DependencyNode;
44 import org.eclipse.aether.impl.ArtifactDescriptorReader;
45 import org.eclipse.aether.impl.DependencyCollector;
46 import org.eclipse.aether.impl.RemoteRepositoryManager;
47 import org.eclipse.aether.impl.VersionRangeResolver;
48 import org.eclipse.aether.repository.ArtifactRepository;
49 import org.eclipse.aether.repository.RemoteRepository;
50 import org.eclipse.aether.resolution.ArtifactDescriptorException;
51 import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
52 import org.eclipse.aether.resolution.ArtifactDescriptorResult;
53 import org.eclipse.aether.resolution.VersionRangeRequest;
54 import org.eclipse.aether.resolution.VersionRangeResolutionException;
55 import org.eclipse.aether.resolution.VersionRangeResult;
56 import org.eclipse.aether.util.ConfigUtils;
57 import org.eclipse.aether.util.graph.transformer.TransformationContextKeys;
58 import org.eclipse.aether.version.Version;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62 import static java.util.Objects.requireNonNull;
63
64
65
66
67
68
69 public abstract class DependencyCollectorDelegate implements DependencyCollector {
70 protected static final String CONFIG_PROP_MAX_EXCEPTIONS = "aether.dependencyCollector.maxExceptions";
71
72 protected static final int CONFIG_PROP_MAX_EXCEPTIONS_DEFAULT = 50;
73
74 protected static final String CONFIG_PROP_MAX_CYCLES = "aether.dependencyCollector.maxCycles";
75
76 protected static final int CONFIG_PROP_MAX_CYCLES_DEFAULT = 10;
77
78 protected final Logger logger = LoggerFactory.getLogger(getClass());
79
80 protected final RemoteRepositoryManager remoteRepositoryManager;
81
82 protected final ArtifactDescriptorReader descriptorReader;
83
84 protected final VersionRangeResolver versionRangeResolver;
85
86 protected DependencyCollectorDelegate(
87 RemoteRepositoryManager remoteRepositoryManager,
88 ArtifactDescriptorReader artifactDescriptorReader,
89 VersionRangeResolver versionRangeResolver) {
90 this.remoteRepositoryManager =
91 requireNonNull(remoteRepositoryManager, "remote repository manager cannot be null");
92 this.descriptorReader = requireNonNull(artifactDescriptorReader, "artifact descriptor reader cannot be null");
93 this.versionRangeResolver = requireNonNull(versionRangeResolver, "version range resolver cannot be null");
94 }
95
96 @SuppressWarnings("checkstyle:methodlength")
97 @Override
98 public final CollectResult collectDependencies(RepositorySystemSession session, CollectRequest request)
99 throws DependencyCollectionException {
100 requireNonNull(session, "session cannot be null");
101 requireNonNull(request, "request cannot be null");
102 session = optimizeSession(session);
103
104 RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
105
106 CollectResult result = new CollectResult(request);
107
108 DependencyTraverser depTraverser = session.getDependencyTraverser();
109 VersionFilter verFilter = session.getVersionFilter();
110
111 Dependency root = request.getRoot();
112 List<RemoteRepository> repositories = request.getRepositories();
113 List<Dependency> dependencies = request.getDependencies();
114 List<Dependency> managedDependencies = request.getManagedDependencies();
115
116 Map<String, Object> stats = new LinkedHashMap<>();
117 long time1 = System.nanoTime();
118
119 DefaultDependencyNode node;
120 if (root != null) {
121 List<? extends Version> versions;
122 VersionRangeResult rangeResult;
123 try {
124 VersionRangeRequest rangeRequest = new VersionRangeRequest(
125 root.getArtifact(), request.getRepositories(), request.getRequestContext());
126 rangeRequest.setTrace(trace);
127 rangeResult = versionRangeResolver.resolveVersionRange(session, rangeRequest);
128 versions = filterVersions(root, rangeResult, verFilter, new DefaultVersionFilterContext(session));
129 } catch (VersionRangeResolutionException e) {
130 result.addException(e);
131 throw new DependencyCollectionException(result, e.getMessage());
132 }
133
134 Version version = versions.get(versions.size() - 1);
135 root = root.setArtifact(root.getArtifact().setVersion(version.toString()));
136
137 ArtifactDescriptorResult descriptorResult;
138 try {
139 ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
140 descriptorRequest.setArtifact(root.getArtifact());
141 descriptorRequest.setRepositories(request.getRepositories());
142 descriptorRequest.setRequestContext(request.getRequestContext());
143 descriptorRequest.setTrace(trace);
144 if (isLackingDescriptor(root.getArtifact())) {
145 descriptorResult = new ArtifactDescriptorResult(descriptorRequest);
146 } else {
147 descriptorResult = descriptorReader.readArtifactDescriptor(session, descriptorRequest);
148 }
149 } catch (ArtifactDescriptorException e) {
150 result.addException(e);
151 throw new DependencyCollectionException(result, e.getMessage());
152 }
153
154 root = root.setArtifact(descriptorResult.getArtifact());
155
156 if (!session.isIgnoreArtifactDescriptorRepositories()) {
157 repositories = remoteRepositoryManager.aggregateRepositories(
158 session, repositories, descriptorResult.getRepositories(), true);
159 }
160 dependencies = mergeDeps(dependencies, descriptorResult.getDependencies());
161 managedDependencies = mergeDeps(managedDependencies, descriptorResult.getManagedDependencies());
162
163 node = new DefaultDependencyNode(root);
164 node.setRequestContext(request.getRequestContext());
165 node.setRelocations(descriptorResult.getRelocations());
166 node.setVersionConstraint(rangeResult.getVersionConstraint());
167 node.setVersion(version);
168 node.setAliases(descriptorResult.getAliases());
169 node.setRepositories(request.getRepositories());
170 } else {
171 node = new DefaultDependencyNode(request.getRootArtifact());
172 node.setRequestContext(request.getRequestContext());
173 node.setRepositories(request.getRepositories());
174 }
175
176 result.setRoot(node);
177
178 boolean traverse = root == null || depTraverser == null || depTraverser.traverseDependency(root);
179 String errorPath = null;
180 if (traverse && !dependencies.isEmpty()) {
181 DataPool pool = new DataPool(session);
182
183 DefaultDependencyCollectionContext context = new DefaultDependencyCollectionContext(
184 session, request.getRootArtifact(), root, managedDependencies);
185
186 DefaultVersionFilterContext versionContext = new DefaultVersionFilterContext(session);
187
188 Results results = new Results(result, session);
189
190 doCollectDependencies(
191 session,
192 trace,
193 pool,
194 context,
195 versionContext,
196 request,
197 node,
198 repositories,
199 dependencies,
200 managedDependencies,
201 results);
202
203 errorPath = results.getErrorPath();
204 }
205
206 long time2 = System.nanoTime();
207
208 DependencyGraphTransformer transformer = session.getDependencyGraphTransformer();
209 if (transformer != null) {
210 try {
211 DefaultDependencyGraphTransformationContext context =
212 new DefaultDependencyGraphTransformationContext(session);
213 context.put(TransformationContextKeys.STATS, stats);
214 result.setRoot(transformer.transformGraph(node, context));
215 } catch (RepositoryException e) {
216 result.addException(e);
217 }
218 }
219
220 long time3 = System.nanoTime();
221 if (logger.isDebugEnabled()) {
222 stats.put(getClass().getSimpleName() + ".collectTime", time2 - time1);
223 stats.put(getClass().getSimpleName() + ".transformTime", time3 - time2);
224 logger.debug("Dependency collection stats {}", stats);
225 }
226
227 if (errorPath != null) {
228 throw new DependencyCollectionException(result, "Failed to collect dependencies at " + errorPath);
229 }
230 if (!result.getExceptions().isEmpty()) {
231 throw new DependencyCollectionException(result);
232 }
233
234 return result;
235 }
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251 protected RequestTrace collectStepTrace(
252 RequestTrace trace, String context, List<DependencyNode> path, Dependency node) {
253 return RequestTrace.newChild(trace, new CollectStepDataImpl(context, path, node));
254 }
255
256 @SuppressWarnings("checkstyle:parameternumber")
257 protected abstract void doCollectDependencies(
258 RepositorySystemSession session,
259 RequestTrace trace,
260 DataPool pool,
261 DefaultDependencyCollectionContext context,
262 DefaultVersionFilterContext versionContext,
263 CollectRequest request,
264 DependencyNode node,
265 List<RemoteRepository> repositories,
266 List<Dependency> dependencies,
267 List<Dependency> managedDependencies,
268 Results results);
269
270 protected RepositorySystemSession optimizeSession(RepositorySystemSession session) {
271 DefaultRepositorySystemSession optimized = new DefaultRepositorySystemSession(session);
272 optimized.setArtifactTypeRegistry(CachingArtifactTypeRegistry.newInstance(session));
273 return optimized;
274 }
275
276 protected List<Dependency> mergeDeps(List<Dependency> dominant, List<Dependency> recessive) {
277 List<Dependency> result;
278 if (dominant == null || dominant.isEmpty()) {
279 result = recessive;
280 } else if (recessive == null || recessive.isEmpty()) {
281 result = dominant;
282 } else {
283 int initialCapacity = dominant.size() + recessive.size();
284 result = new ArrayList<>(initialCapacity);
285 Collection<String> ids = new HashSet<>(initialCapacity, 1.0f);
286 for (Dependency dependency : dominant) {
287 ids.add(getId(dependency.getArtifact()));
288 result.add(dependency);
289 }
290 for (Dependency dependency : recessive) {
291 if (!ids.contains(getId(dependency.getArtifact()))) {
292 result.add(dependency);
293 }
294 }
295 }
296 return result;
297 }
298
299 protected static String getId(Artifact a) {
300 return a.getGroupId() + ':' + a.getArtifactId() + ':' + a.getClassifier() + ':' + a.getExtension();
301 }
302
303 @SuppressWarnings("checkstyle:parameternumber")
304 protected static DefaultDependencyNode createDependencyNode(
305 List<Artifact> relocations,
306 PremanagedDependency preManaged,
307 VersionRangeResult rangeResult,
308 Version version,
309 Dependency d,
310 Collection<Artifact> aliases,
311 List<RemoteRepository> repos,
312 String requestContext) {
313 DefaultDependencyNode child = new DefaultDependencyNode(d);
314 preManaged.applyTo(child);
315 child.setRelocations(relocations);
316 child.setVersionConstraint(rangeResult.getVersionConstraint());
317 child.setVersion(version);
318 child.setAliases(aliases);
319 child.setRepositories(repos);
320 child.setRequestContext(requestContext);
321 return child;
322 }
323
324 protected static DefaultDependencyNode createDependencyNode(
325 List<Artifact> relocations,
326 PremanagedDependency preManaged,
327 VersionRangeResult rangeResult,
328 Version version,
329 Dependency d,
330 ArtifactDescriptorResult descriptorResult,
331 DependencyNode cycleNode) {
332 DefaultDependencyNode child = createDependencyNode(
333 relocations,
334 preManaged,
335 rangeResult,
336 version,
337 d,
338 descriptorResult.getAliases(),
339 cycleNode.getRepositories(),
340 cycleNode.getRequestContext());
341 child.setChildren(cycleNode.getChildren());
342 return child;
343 }
344
345 protected static ArtifactDescriptorRequest createArtifactDescriptorRequest(
346 String requestContext, RequestTrace requestTrace, List<RemoteRepository> repositories, Dependency d) {
347 ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
348 descriptorRequest.setArtifact(d.getArtifact());
349 descriptorRequest.setRepositories(repositories);
350 descriptorRequest.setRequestContext(requestContext);
351 descriptorRequest.setTrace(requestTrace);
352 return descriptorRequest;
353 }
354
355 protected static VersionRangeRequest createVersionRangeRequest(
356 String requestContext,
357 RequestTrace requestTrace,
358 List<RemoteRepository> repositories,
359 Dependency dependency) {
360 VersionRangeRequest rangeRequest = new VersionRangeRequest();
361 rangeRequest.setArtifact(dependency.getArtifact());
362 rangeRequest.setRepositories(repositories);
363 rangeRequest.setRequestContext(requestContext);
364 rangeRequest.setTrace(requestTrace);
365 return rangeRequest;
366 }
367
368 protected VersionRangeResult cachedResolveRangeResult(
369 VersionRangeRequest rangeRequest, DataPool pool, RepositorySystemSession session)
370 throws VersionRangeResolutionException {
371 Object key = pool.toKey(rangeRequest);
372 VersionRangeResult rangeResult = pool.getConstraint(key, rangeRequest);
373 if (rangeResult == null) {
374 rangeResult = versionRangeResolver.resolveVersionRange(session, rangeRequest);
375 pool.putConstraint(key, rangeResult);
376 }
377 return rangeResult;
378 }
379
380 protected static boolean isLackingDescriptor(Artifact artifact) {
381 return artifact.getProperty(ArtifactProperties.LOCAL_PATH, null) != null;
382 }
383
384 protected static List<RemoteRepository> getRemoteRepositories(
385 ArtifactRepository repository, List<RemoteRepository> repositories) {
386 if (repository instanceof RemoteRepository) {
387 return Collections.singletonList((RemoteRepository) repository);
388 }
389 if (repository != null) {
390 return Collections.emptyList();
391 }
392 return repositories;
393 }
394
395 protected static List<? extends Version> filterVersions(
396 Dependency dependency,
397 VersionRangeResult rangeResult,
398 VersionFilter verFilter,
399 DefaultVersionFilterContext verContext)
400 throws VersionRangeResolutionException {
401 if (rangeResult.getVersions().isEmpty()) {
402 throw new VersionRangeResolutionException(
403 rangeResult, "No versions available for " + dependency.getArtifact() + " within specified range");
404 }
405
406 List<? extends Version> versions;
407 if (verFilter != null && rangeResult.getVersionConstraint().getRange() != null) {
408 verContext.set(dependency, rangeResult);
409 try {
410 verFilter.filterVersions(verContext);
411 } catch (RepositoryException e) {
412 throw new VersionRangeResolutionException(
413 rangeResult, "Failed to filter versions for " + dependency.getArtifact(), e);
414 }
415 versions = verContext.get();
416 if (versions.isEmpty()) {
417 throw new VersionRangeResolutionException(
418 rangeResult,
419 "No acceptable versions for " + dependency.getArtifact() + ": " + rangeResult.getVersions());
420 }
421 } else {
422 versions = rangeResult.getVersions();
423 }
424 return versions;
425 }
426
427
428
429
430 protected static class Results {
431
432 private final CollectResult result;
433
434 final int maxExceptions;
435
436 final int maxCycles;
437
438 String errorPath;
439
440 public Results(CollectResult result, RepositorySystemSession session) {
441 this.result = result;
442
443 maxExceptions =
444 ConfigUtils.getInteger(session, CONFIG_PROP_MAX_EXCEPTIONS_DEFAULT, CONFIG_PROP_MAX_EXCEPTIONS);
445
446 maxCycles = ConfigUtils.getInteger(session, CONFIG_PROP_MAX_CYCLES_DEFAULT, CONFIG_PROP_MAX_CYCLES);
447 }
448
449 public String getErrorPath() {
450 return errorPath;
451 }
452
453 public void addException(Dependency dependency, Exception e, List<DependencyNode> nodes) {
454 if (maxExceptions < 0 || result.getExceptions().size() < maxExceptions) {
455 result.addException(e);
456 if (errorPath == null) {
457 StringBuilder buffer = new StringBuilder(256);
458 for (DependencyNode node : nodes) {
459 if (buffer.length() > 0) {
460 buffer.append(" -> ");
461 }
462 Dependency dep = node.getDependency();
463 if (dep != null) {
464 buffer.append(dep.getArtifact());
465 }
466 }
467 if (buffer.length() > 0) {
468 buffer.append(" -> ");
469 }
470 buffer.append(dependency.getArtifact());
471 errorPath = buffer.toString();
472 }
473 }
474 }
475
476 public void addCycle(List<DependencyNode> nodes, int cycleEntry, Dependency dependency) {
477 if (maxCycles < 0 || result.getCycles().size() < maxCycles) {
478 result.addCycle(new DefaultDependencyCycle(nodes, cycleEntry, dependency));
479 }
480 }
481 }
482 }