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 import java.util.concurrent.atomic.AtomicInteger;
29
30 import org.eclipse.aether.DefaultRepositorySystemSession;
31 import org.eclipse.aether.RepositoryException;
32 import org.eclipse.aether.RepositorySystemSession;
33 import org.eclipse.aether.RequestTrace;
34 import org.eclipse.aether.artifact.Artifact;
35 import org.eclipse.aether.collection.CollectRequest;
36 import org.eclipse.aether.collection.CollectResult;
37 import org.eclipse.aether.collection.DependencyCollectionChecker;
38 import org.eclipse.aether.collection.DependencyCollectionException;
39 import org.eclipse.aether.collection.DependencyGraphTransformer;
40 import org.eclipse.aether.collection.DependencyTraverser;
41 import org.eclipse.aether.collection.VersionFilter;
42 import org.eclipse.aether.graph.DefaultDependencyNode;
43 import org.eclipse.aether.graph.Dependency;
44 import org.eclipse.aether.graph.DependencyNode;
45 import org.eclipse.aether.impl.ArtifactDescriptorReader;
46 import org.eclipse.aether.impl.DependencyCollector;
47 import org.eclipse.aether.impl.RemoteRepositoryManager;
48 import org.eclipse.aether.impl.VersionRangeResolver;
49 import org.eclipse.aether.impl.scope.InternalScopeManager;
50 import org.eclipse.aether.internal.impl.Utils;
51 import org.eclipse.aether.repository.ArtifactRepository;
52 import org.eclipse.aether.repository.RemoteRepository;
53 import org.eclipse.aether.resolution.ArtifactDescriptorException;
54 import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
55 import org.eclipse.aether.resolution.ArtifactDescriptorResult;
56 import org.eclipse.aether.resolution.VersionRangeRequest;
57 import org.eclipse.aether.resolution.VersionRangeResolutionException;
58 import org.eclipse.aether.resolution.VersionRangeResult;
59 import org.eclipse.aether.scope.ResolutionScope;
60 import org.eclipse.aether.scope.SystemDependencyScope;
61 import org.eclipse.aether.spi.artifact.decorator.ArtifactDecorator;
62 import org.eclipse.aether.spi.artifact.decorator.ArtifactDecoratorFactory;
63 import org.eclipse.aether.util.ConfigUtils;
64 import org.eclipse.aether.util.graph.transformer.TransformationContextKeys;
65 import org.eclipse.aether.version.Version;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68
69 import static java.util.Objects.requireNonNull;
70
71
72
73
74
75
76 public abstract class DependencyCollectorDelegate implements DependencyCollector {
77
78
79
80
81
82
83
84
85 public static final String CONFIG_PROP_MAX_EXCEPTIONS =
86 DefaultDependencyCollector.CONFIG_PROPS_PREFIX + "maxExceptions";
87
88 public static final int DEFAULT_MAX_EXCEPTIONS = 50;
89
90
91
92
93
94
95
96
97 public static final String CONFIG_PROP_MAX_CYCLES = DefaultDependencyCollector.CONFIG_PROPS_PREFIX + "maxCycles";
98
99 public static final int DEFAULT_MAX_CYCLES = 10;
100
101
102
103
104
105
106
107
108
109
110 public static final String CONFIG_PROP_MAX_RUNS = DefaultDependencyCollector.CONFIG_PROPS_PREFIX + "maxRuns";
111
112 public static final int DEFAULT_MAX_RUNS = 5;
113
114 protected final Logger logger = LoggerFactory.getLogger(getClass());
115
116 protected final RemoteRepositoryManager remoteRepositoryManager;
117
118 protected final ArtifactDescriptorReader descriptorReader;
119
120 protected final VersionRangeResolver versionRangeResolver;
121
122 protected final Map<String, ArtifactDecoratorFactory> artifactDecoratorFactories;
123
124 protected DependencyCollectorDelegate(
125 RemoteRepositoryManager remoteRepositoryManager,
126 ArtifactDescriptorReader artifactDescriptorReader,
127 VersionRangeResolver versionRangeResolver,
128 Map<String, ArtifactDecoratorFactory> artifactDecoratorFactories) {
129 this.remoteRepositoryManager =
130 requireNonNull(remoteRepositoryManager, "remote repository manager cannot be null");
131 this.descriptorReader = requireNonNull(artifactDescriptorReader, "artifact descriptor reader cannot be null");
132 this.versionRangeResolver = requireNonNull(versionRangeResolver, "version range resolver cannot be null");
133 this.artifactDecoratorFactories =
134 requireNonNull(artifactDecoratorFactories, "artifact decorator factories cannot be null");
135 }
136
137 @SuppressWarnings("checkstyle:methodlength")
138 @Override
139 public final CollectResult collectDependencies(
140 final RepositorySystemSession originalSession, final CollectRequest request)
141 throws DependencyCollectionException {
142 requireNonNull(originalSession, "session cannot be null");
143 requireNonNull(request, "request cannot be null");
144
145 final InternalScopeManager scopeManager = (InternalScopeManager) originalSession.getScopeManager();
146 final RepositorySystemSession setUpSession = setUpSession(originalSession, request, scopeManager);
147 final DependencyCollectionChecker dependencyCollectionChecker =
148 originalSession.getDependencyCollectionChecker() == null
149 ? DependencyCollectionChecker.NOOP
150 : originalSession.getDependencyCollectionChecker();
151
152 final RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
153
154 final Map<String, Object> stats = new LinkedHashMap<>();
155 final AtomicInteger runs = new AtomicInteger(0);
156 final int maxRuns = ConfigUtils.getInteger(originalSession, DEFAULT_MAX_RUNS, CONFIG_PROP_MAX_RUNS);
157 if (maxRuns < 1) {
158 throw new DependencyCollectionException(
159 new CollectResult(request),
160 "Invalid configuration: '" + CONFIG_PROP_MAX_RUNS
161 + "' configuration must be equal or grater than 1");
162 }
163
164 CollectResult result = null;
165
166 boolean finished = false;
167 while (!finished) {
168 final long time1 = System.nanoTime();
169 if (runs.incrementAndGet() > maxRuns) {
170 throw new DependencyCollectionException(
171 new CollectResult(request),
172 "Too many collection attempts (bug of used DependencyCollectionChecker?)");
173 }
174
175 RepositorySystemSession session = dependencyCollectionChecker.prepare(setUpSession, request);
176 final DependencyTraverser depTraverser = session.getDependencyTraverser();
177 final VersionFilter verFilter = session.getVersionFilter();
178
179 Dependency root = request.getRoot();
180 List<RemoteRepository> repositories = request.getRepositories();
181 List<Dependency> dependencies = request.getDependencies();
182 List<Dependency> managedDependencies = request.getManagedDependencies();
183
184 result = new CollectResult(request);
185 DefaultDependencyNode node;
186 if (root != null) {
187 List<? extends Version> versions;
188 VersionRangeResult rangeResult;
189 try {
190 VersionRangeRequest rangeRequest = new VersionRangeRequest(
191 root.getArtifact(), request.getRepositories(), request.getRequestContext());
192 rangeRequest.setTrace(trace);
193 rangeResult = versionRangeResolver.resolveVersionRange(session, rangeRequest);
194 versions = filterVersions(root, rangeResult, verFilter, new DefaultVersionFilterContext(session));
195 } catch (VersionRangeResolutionException e) {
196 result.addException(e);
197 throw new DependencyCollectionException(result, e.getMessage());
198 }
199
200 Version version = versions.get(versions.size() - 1);
201 root = root.setArtifact(root.getArtifact().setVersion(version.toString()));
202
203 ArtifactDescriptorResult descriptorResult;
204 try {
205 ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
206 descriptorRequest.setArtifact(root.getArtifact());
207 descriptorRequest.setRepositories(request.getRepositories());
208 descriptorRequest.setRequestContext(request.getRequestContext());
209 descriptorRequest.setTrace(trace);
210 if (isLackingDescriptor(session, root.getArtifact())) {
211 descriptorResult = new ArtifactDescriptorResult(descriptorRequest);
212 } else {
213 descriptorResult = descriptorReader.readArtifactDescriptor(session, descriptorRequest);
214 for (ArtifactDecorator decorator :
215 Utils.getArtifactDecorators(session, artifactDecoratorFactories)) {
216 descriptorResult.setArtifact(decorator.decorateArtifact(descriptorResult));
217 }
218 }
219 } catch (ArtifactDescriptorException e) {
220 result.addException(e);
221 throw new DependencyCollectionException(result, e.getMessage());
222 }
223
224 root = root.setArtifact(descriptorResult.getArtifact());
225
226 if (!session.isIgnoreArtifactDescriptorRepositories()) {
227 repositories = remoteRepositoryManager.aggregateRepositories(
228 session, repositories, descriptorResult.getRepositories(), true);
229 }
230 dependencies = mergeDeps(dependencies, descriptorResult.getDependencies());
231 managedDependencies = mergeDeps(managedDependencies, descriptorResult.getManagedDependencies());
232
233 node = new DefaultDependencyNode(root);
234 node.setRequestContext(request.getRequestContext());
235 node.setRelocations(descriptorResult.getRelocations());
236 node.setVersionConstraint(rangeResult.getVersionConstraint());
237 node.setVersion(version);
238 node.setAliases(descriptorResult.getAliases());
239 node.setRepositories(request.getRepositories());
240 } else {
241 node = new DefaultDependencyNode(request.getRootArtifact());
242 node.setRequestContext(request.getRequestContext());
243 node.setRepositories(request.getRepositories());
244 }
245
246 result.setRoot(node);
247
248 boolean traverse = root == null || depTraverser == null || depTraverser.traverseDependency(root);
249 String errorPath = null;
250 if (traverse && !dependencies.isEmpty()) {
251 DataPool pool = new DataPool(session);
252
253 DefaultDependencyCollectionContext context = new DefaultDependencyCollectionContext(
254 session, request.getRootArtifact(), root, managedDependencies);
255
256 DefaultVersionFilterContext versionContext = new DefaultVersionFilterContext(session);
257
258 Results results = new Results(result, session);
259
260 doCollectDependencies(
261 session,
262 trace,
263 pool,
264 context,
265 versionContext,
266 request,
267 node,
268 repositories,
269 dependencies,
270 managedDependencies,
271 results);
272
273 errorPath = results.getErrorPath();
274 }
275
276 final long time2 = System.nanoTime();
277
278 DependencyGraphTransformer transformer = session.getDependencyGraphTransformer();
279 if (transformer != null) {
280 try {
281 DefaultDependencyGraphTransformationContext context =
282 new DefaultDependencyGraphTransformationContext(session);
283 context.put(TransformationContextKeys.STATS, stats);
284 result.setRoot(transformer.transformGraph(node, context));
285 } catch (RepositoryException e) {
286 result.addException(e);
287 }
288 }
289
290 if (errorPath != null) {
291 throw new DependencyCollectionException(result, "Failed to collect dependencies at " + errorPath);
292 }
293 if (!result.getExceptions().isEmpty()) {
294 throw new DependencyCollectionException(result);
295 }
296
297 if (request.getResolutionScope() != null) {
298 result = scopeManager.postProcess(session, request.getResolutionScope(), result);
299 }
300
301 long time3 = System.nanoTime();
302 stats.put(getClass().getSimpleName() + ".collectTime", time2 - time1);
303 stats.put(getClass().getSimpleName() + ".transformTime", time3 - time2);
304
305 finished = dependencyCollectionChecker.isSatisfactory(session, request, result);
306 }
307
308 stats.put(getClass().getSimpleName() + ".runs", runs.get());
309 if (logger.isDebugEnabled()) {
310 logger.debug("Dependency collection stats {}", stats);
311 }
312
313 return result;
314 }
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330 protected RequestTrace collectStepTrace(
331 RequestTrace trace, String context, List<DependencyNode> path, Dependency node) {
332 return RequestTrace.newChild(trace, new CollectStepDataImpl(context, path, node));
333 }
334
335 @SuppressWarnings("checkstyle:parameternumber")
336 protected abstract void doCollectDependencies(
337 RepositorySystemSession session,
338 RequestTrace trace,
339 DataPool pool,
340 DefaultDependencyCollectionContext context,
341 DefaultVersionFilterContext versionContext,
342 CollectRequest request,
343 DependencyNode node,
344 List<RemoteRepository> repositories,
345 List<Dependency> dependencies,
346 List<Dependency> managedDependencies,
347 Results results)
348 throws DependencyCollectionException;
349
350 protected RepositorySystemSession setUpSession(
351 RepositorySystemSession session, CollectRequest collectRequest, InternalScopeManager scopeManager) {
352 DefaultRepositorySystemSession optimized = new DefaultRepositorySystemSession(session);
353 optimized.setArtifactTypeRegistry(CachingArtifactTypeRegistry.newInstance(session));
354
355 ResolutionScope resolutionScope = collectRequest.getResolutionScope();
356 if (resolutionScope != null) {
357 requireNonNull(scopeManager, "ScopeManager is not set on session");
358 optimized.setDependencySelector(scopeManager.getDependencySelector(session, resolutionScope));
359 }
360 return optimized;
361 }
362
363 protected List<Dependency> mergeDeps(List<Dependency> dominant, List<Dependency> recessive) {
364 List<Dependency> result;
365 if (dominant == null || dominant.isEmpty()) {
366 result = recessive;
367 } else if (recessive == null || recessive.isEmpty()) {
368 result = dominant;
369 } else {
370 int initialCapacity = dominant.size() + recessive.size();
371 result = new ArrayList<>(initialCapacity);
372 Collection<String> ids = new HashSet<>(initialCapacity, 1.0f);
373 for (Dependency dependency : dominant) {
374 ids.add(getId(dependency.getArtifact()));
375 result.add(dependency);
376 }
377 for (Dependency dependency : recessive) {
378 if (!ids.contains(getId(dependency.getArtifact()))) {
379 result.add(dependency);
380 }
381 }
382 }
383 return result;
384 }
385
386 protected static String getId(Artifact a) {
387 return a.getGroupId() + ':' + a.getArtifactId() + ':' + a.getClassifier() + ':' + a.getExtension();
388 }
389
390 @SuppressWarnings("checkstyle:parameternumber")
391 protected static DefaultDependencyNode createDependencyNode(
392 List<Artifact> relocations,
393 PremanagedDependency preManaged,
394 VersionRangeResult rangeResult,
395 Version version,
396 Dependency d,
397 Collection<Artifact> aliases,
398 List<RemoteRepository> repos,
399 String requestContext) {
400 DefaultDependencyNode child = new DefaultDependencyNode(d);
401 preManaged.applyTo(child);
402 child.setRelocations(relocations);
403 child.setVersionConstraint(rangeResult.getVersionConstraint());
404 child.setVersion(version);
405 child.setAliases(aliases);
406 child.setRepositories(repos);
407 child.setRequestContext(requestContext);
408 return child;
409 }
410
411 protected static DefaultDependencyNode createDependencyNode(
412 List<Artifact> relocations,
413 PremanagedDependency preManaged,
414 VersionRangeResult rangeResult,
415 Version version,
416 Dependency d,
417 ArtifactDescriptorResult descriptorResult,
418 DependencyNode cycleNode) {
419 DefaultDependencyNode child = createDependencyNode(
420 relocations,
421 preManaged,
422 rangeResult,
423 version,
424 d,
425 descriptorResult.getAliases(),
426 cycleNode.getRepositories(),
427 cycleNode.getRequestContext());
428 child.setChildren(cycleNode.getChildren());
429 return child;
430 }
431
432 protected static ArtifactDescriptorRequest createArtifactDescriptorRequest(
433 String requestContext, RequestTrace requestTrace, List<RemoteRepository> repositories, Dependency d) {
434 ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
435 descriptorRequest.setArtifact(d.getArtifact());
436 descriptorRequest.setRepositories(repositories);
437 descriptorRequest.setRequestContext(requestContext);
438 descriptorRequest.setTrace(requestTrace);
439 return descriptorRequest;
440 }
441
442 protected static VersionRangeRequest createVersionRangeRequest(
443 String requestContext,
444 RequestTrace requestTrace,
445 List<RemoteRepository> repositories,
446 Dependency dependency) {
447 VersionRangeRequest rangeRequest = new VersionRangeRequest();
448 rangeRequest.setArtifact(dependency.getArtifact());
449 rangeRequest.setRepositories(repositories);
450 rangeRequest.setRequestContext(requestContext);
451 rangeRequest.setTrace(requestTrace);
452 return rangeRequest;
453 }
454
455 protected VersionRangeResult cachedResolveRangeResult(
456 VersionRangeRequest rangeRequest, DataPool pool, RepositorySystemSession session)
457 throws VersionRangeResolutionException {
458 Object key = pool.toKey(rangeRequest);
459 VersionRangeResult rangeResult = pool.getConstraint(key, rangeRequest);
460 if (rangeResult == null) {
461 rangeResult = versionRangeResolver.resolveVersionRange(session, rangeRequest);
462 pool.putConstraint(key, rangeResult);
463 }
464 return rangeResult;
465 }
466
467 protected static boolean isLackingDescriptor(RepositorySystemSession session, Artifact artifact) {
468 SystemDependencyScope systemDependencyScope = session.getSystemDependencyScope();
469 return systemDependencyScope != null && systemDependencyScope.getSystemPath(artifact) != null;
470 }
471
472 protected static List<RemoteRepository> getRemoteRepositories(
473 ArtifactRepository repository, List<RemoteRepository> repositories) {
474 if (repository instanceof RemoteRepository) {
475 return Collections.singletonList((RemoteRepository) repository);
476 }
477 if (repository != null) {
478 return Collections.emptyList();
479 }
480 return repositories;
481 }
482
483 protected static List<? extends Version> filterVersions(
484 Dependency dependency,
485 VersionRangeResult rangeResult,
486 VersionFilter verFilter,
487 DefaultVersionFilterContext verContext)
488 throws VersionRangeResolutionException {
489 if (rangeResult.getVersions().isEmpty()) {
490 throw new VersionRangeResolutionException(
491 rangeResult, "No versions available for " + dependency.getArtifact() + " within specified range");
492 }
493
494 List<? extends Version> versions;
495 if (verFilter != null && rangeResult.getVersionConstraint().getRange() != null) {
496 verContext = verContext.initialize(dependency, rangeResult);
497 try {
498 verFilter.filterVersions(verContext);
499 } catch (RepositoryException e) {
500 throw new VersionRangeResolutionException(
501 rangeResult, "Failed to filter versions for " + dependency.getArtifact(), e);
502 }
503 versions = verContext.get();
504 if (versions.isEmpty()) {
505 throw new VersionRangeResolutionException(
506 rangeResult,
507 "No acceptable versions for " + dependency.getArtifact() + ": " + rangeResult.getVersions());
508 }
509 } else {
510 versions = rangeResult.getVersions();
511 }
512 return versions;
513 }
514
515 protected ArtifactDescriptorResult resolveCachedArtifactDescriptor(
516 DataPool pool,
517 ArtifactDescriptorRequest descriptorRequest,
518 RepositorySystemSession session,
519 Dependency d,
520 Results results,
521 List<DependencyNode> nodes) {
522 DataPool.DescriptorKey key = pool.toKey(descriptorRequest);
523 ArtifactDescriptorResult descriptorResult = pool.getDescriptor(key, descriptorRequest);
524 if (descriptorResult == null) {
525 try {
526 descriptorResult = descriptorReader.readArtifactDescriptor(session, descriptorRequest);
527 for (ArtifactDecorator decorator : Utils.getArtifactDecorators(session, artifactDecoratorFactories)) {
528 descriptorResult.setArtifact(decorator.decorateArtifact(descriptorResult));
529 }
530 pool.putDescriptor(key, descriptorResult);
531 } catch (ArtifactDescriptorException e) {
532 results.addException(d, e, nodes);
533 pool.putDescriptor(key, e);
534 return null;
535 }
536 } else if (descriptorResult == DataPool.NO_DESCRIPTOR) {
537 return null;
538 }
539 return descriptorResult;
540 }
541
542
543
544
545 protected static class Results {
546
547 private final CollectResult result;
548
549 final int maxExceptions;
550
551 final int maxCycles;
552
553 volatile String errorPath;
554
555 public Results(CollectResult result, RepositorySystemSession session) {
556 this.result = result;
557
558 maxExceptions = ConfigUtils.getInteger(session, DEFAULT_MAX_EXCEPTIONS, CONFIG_PROP_MAX_EXCEPTIONS);
559
560 maxCycles = ConfigUtils.getInteger(session, DEFAULT_MAX_CYCLES, CONFIG_PROP_MAX_CYCLES);
561 }
562
563 public synchronized CollectResult getResult() {
564 return result;
565 }
566
567 public synchronized String getErrorPath() {
568 return errorPath;
569 }
570
571 public synchronized void addException(Dependency dependency, Exception e, List<DependencyNode> nodes) {
572 if (maxExceptions < 0 || result.getExceptions().size() < maxExceptions) {
573 result.addException(e);
574 if (errorPath == null) {
575 StringBuilder buffer = new StringBuilder(256);
576 for (DependencyNode node : nodes) {
577 if (buffer.length() > 0) {
578 buffer.append(" -> ");
579 }
580 Dependency dep = node.getDependency();
581 if (dep != null) {
582 buffer.append(dep.getArtifact());
583 }
584 }
585 if (buffer.length() > 0) {
586 buffer.append(" -> ");
587 }
588 buffer.append(dependency.getArtifact());
589 errorPath = buffer.toString();
590 }
591 }
592 }
593
594 public synchronized void addCycle(List<DependencyNode> nodes, int cycleEntry, Dependency dependency) {
595 if (maxCycles < 0 || result.getCycles().size() < maxCycles) {
596 result.addCycle(new DefaultDependencyCycle(nodes, cycleEntry, dependency));
597 }
598 }
599 }
600 }