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