001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.eclipse.aether.internal.impl.collect; 020 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.Collections; 024import java.util.HashSet; 025import java.util.LinkedHashMap; 026import java.util.List; 027import java.util.Map; 028 029import org.eclipse.aether.DefaultRepositorySystemSession; 030import org.eclipse.aether.RepositoryException; 031import org.eclipse.aether.RepositorySystemSession; 032import org.eclipse.aether.RequestTrace; 033import org.eclipse.aether.artifact.Artifact; 034import org.eclipse.aether.collection.CollectRequest; 035import org.eclipse.aether.collection.CollectResult; 036import org.eclipse.aether.collection.DependencyCollectionException; 037import org.eclipse.aether.collection.DependencyGraphTransformer; 038import org.eclipse.aether.collection.DependencyTraverser; 039import org.eclipse.aether.collection.VersionFilter; 040import org.eclipse.aether.graph.DefaultDependencyNode; 041import org.eclipse.aether.graph.Dependency; 042import org.eclipse.aether.graph.DependencyNode; 043import org.eclipse.aether.impl.ArtifactDescriptorReader; 044import org.eclipse.aether.impl.DependencyCollector; 045import org.eclipse.aether.impl.RemoteRepositoryManager; 046import org.eclipse.aether.impl.VersionRangeResolver; 047import org.eclipse.aether.impl.scope.InternalScopeManager; 048import org.eclipse.aether.repository.ArtifactRepository; 049import org.eclipse.aether.repository.RemoteRepository; 050import org.eclipse.aether.resolution.ArtifactDescriptorException; 051import org.eclipse.aether.resolution.ArtifactDescriptorRequest; 052import org.eclipse.aether.resolution.ArtifactDescriptorResult; 053import org.eclipse.aether.resolution.VersionRangeRequest; 054import org.eclipse.aether.resolution.VersionRangeResolutionException; 055import org.eclipse.aether.resolution.VersionRangeResult; 056import org.eclipse.aether.scope.ResolutionScope; 057import org.eclipse.aether.scope.SystemDependencyScope; 058import org.eclipse.aether.util.ConfigUtils; 059import org.eclipse.aether.util.graph.transformer.TransformationContextKeys; 060import org.eclipse.aether.version.Version; 061import org.slf4j.Logger; 062import org.slf4j.LoggerFactory; 063 064import static java.util.Objects.requireNonNull; 065 066/** 067 * Helper class for delegate implementations, they MUST subclass this class. 068 * 069 * @since 1.8.0 070 */ 071public abstract class DependencyCollectorDelegate implements DependencyCollector { 072 /** 073 * Only exceptions up to the number given in this configuration property are emitted. Exceptions which exceed 074 * that number are swallowed. 075 * 076 * @configurationSource {@link RepositorySystemSession#getConfigProperties()} 077 * @configurationType {@link java.lang.Integer} 078 * @configurationDefaultValue {@link #DEFAULT_MAX_EXCEPTIONS} 079 */ 080 public static final String CONFIG_PROP_MAX_EXCEPTIONS = 081 DefaultDependencyCollector.CONFIG_PROPS_PREFIX + "maxExceptions"; 082 083 public static final int DEFAULT_MAX_EXCEPTIONS = 50; 084 085 /** 086 * Only up to the given amount cyclic dependencies are emitted. 087 * 088 * @configurationSource {@link RepositorySystemSession#getConfigProperties()} 089 * @configurationType {@link java.lang.Integer} 090 * @configurationDefaultValue {@link #DEFAULT_MAX_CYCLES} 091 */ 092 public static final String CONFIG_PROP_MAX_CYCLES = DefaultDependencyCollector.CONFIG_PROPS_PREFIX + "maxCycles"; 093 094 public static final int DEFAULT_MAX_CYCLES = 10; 095 096 protected final Logger logger = LoggerFactory.getLogger(getClass()); 097 098 protected final RemoteRepositoryManager remoteRepositoryManager; 099 100 protected final ArtifactDescriptorReader descriptorReader; 101 102 protected final VersionRangeResolver versionRangeResolver; 103 104 protected DependencyCollectorDelegate( 105 RemoteRepositoryManager remoteRepositoryManager, 106 ArtifactDescriptorReader artifactDescriptorReader, 107 VersionRangeResolver versionRangeResolver) { 108 this.remoteRepositoryManager = 109 requireNonNull(remoteRepositoryManager, "remote repository manager cannot be null"); 110 this.descriptorReader = requireNonNull(artifactDescriptorReader, "artifact descriptor reader cannot be null"); 111 this.versionRangeResolver = requireNonNull(versionRangeResolver, "version range resolver cannot be null"); 112 } 113 114 @SuppressWarnings("checkstyle:methodlength") 115 @Override 116 public final CollectResult collectDependencies(RepositorySystemSession session, CollectRequest request) 117 throws DependencyCollectionException { 118 requireNonNull(session, "session cannot be null"); 119 requireNonNull(request, "request cannot be null"); 120 121 InternalScopeManager scopeManager = (InternalScopeManager) session.getScopeManager(); 122 session = setUpSession(session, request, scopeManager); 123 124 RequestTrace trace = RequestTrace.newChild(request.getTrace(), request); 125 126 CollectResult result = new CollectResult(request); 127 128 DependencyTraverser depTraverser = session.getDependencyTraverser(); 129 VersionFilter verFilter = session.getVersionFilter(); 130 131 Dependency root = request.getRoot(); 132 List<RemoteRepository> repositories = request.getRepositories(); 133 List<Dependency> dependencies = request.getDependencies(); 134 List<Dependency> managedDependencies = request.getManagedDependencies(); 135 136 Map<String, Object> stats = new LinkedHashMap<>(); 137 long time1 = System.nanoTime(); 138 139 DefaultDependencyNode node; 140 if (root != null) { 141 List<? extends Version> versions; 142 VersionRangeResult rangeResult; 143 try { 144 VersionRangeRequest rangeRequest = new VersionRangeRequest( 145 root.getArtifact(), request.getRepositories(), request.getRequestContext()); 146 rangeRequest.setTrace(trace); 147 rangeResult = versionRangeResolver.resolveVersionRange(session, rangeRequest); 148 versions = filterVersions(root, rangeResult, verFilter, new DefaultVersionFilterContext(session)); 149 } catch (VersionRangeResolutionException e) { 150 result.addException(e); 151 throw new DependencyCollectionException(result, e.getMessage()); 152 } 153 154 Version version = versions.get(versions.size() - 1); 155 root = root.setArtifact(root.getArtifact().setVersion(version.toString())); 156 157 ArtifactDescriptorResult descriptorResult; 158 try { 159 ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest(); 160 descriptorRequest.setArtifact(root.getArtifact()); 161 descriptorRequest.setRepositories(request.getRepositories()); 162 descriptorRequest.setRequestContext(request.getRequestContext()); 163 descriptorRequest.setTrace(trace); 164 if (isLackingDescriptor(session, root.getArtifact())) { 165 descriptorResult = new ArtifactDescriptorResult(descriptorRequest); 166 } else { 167 descriptorResult = descriptorReader.readArtifactDescriptor(session, descriptorRequest); 168 } 169 } catch (ArtifactDescriptorException e) { 170 result.addException(e); 171 throw new DependencyCollectionException(result, e.getMessage()); 172 } 173 174 root = root.setArtifact(descriptorResult.getArtifact()); 175 176 if (!session.isIgnoreArtifactDescriptorRepositories()) { 177 repositories = remoteRepositoryManager.aggregateRepositories( 178 session, repositories, descriptorResult.getRepositories(), true); 179 } 180 dependencies = mergeDeps(dependencies, descriptorResult.getDependencies()); 181 managedDependencies = mergeDeps(managedDependencies, descriptorResult.getManagedDependencies()); 182 183 node = new DefaultDependencyNode(root); 184 node.setRequestContext(request.getRequestContext()); 185 node.setRelocations(descriptorResult.getRelocations()); 186 node.setVersionConstraint(rangeResult.getVersionConstraint()); 187 node.setVersion(version); 188 node.setAliases(descriptorResult.getAliases()); 189 node.setRepositories(request.getRepositories()); 190 } else { 191 node = new DefaultDependencyNode(request.getRootArtifact()); 192 node.setRequestContext(request.getRequestContext()); 193 node.setRepositories(request.getRepositories()); 194 } 195 196 result.setRoot(node); 197 198 boolean traverse = root == null || depTraverser == null || depTraverser.traverseDependency(root); 199 String errorPath = null; 200 if (traverse && !dependencies.isEmpty()) { 201 DataPool pool = new DataPool(session); 202 203 DefaultDependencyCollectionContext context = new DefaultDependencyCollectionContext( 204 session, request.getRootArtifact(), root, managedDependencies); 205 206 DefaultVersionFilterContext versionContext = new DefaultVersionFilterContext(session); 207 208 Results results = new Results(result, session); 209 210 doCollectDependencies( 211 session, 212 trace, 213 pool, 214 context, 215 versionContext, 216 request, 217 node, 218 repositories, 219 dependencies, 220 managedDependencies, 221 results); 222 223 errorPath = results.getErrorPath(); 224 } 225 226 long time2 = System.nanoTime(); 227 228 DependencyGraphTransformer transformer = session.getDependencyGraphTransformer(); 229 if (transformer != null) { 230 try { 231 DefaultDependencyGraphTransformationContext context = 232 new DefaultDependencyGraphTransformationContext(session); 233 context.put(TransformationContextKeys.STATS, stats); 234 result.setRoot(transformer.transformGraph(node, context)); 235 } catch (RepositoryException e) { 236 result.addException(e); 237 } 238 } 239 240 long time3 = System.nanoTime(); 241 if (logger.isDebugEnabled()) { 242 stats.put(getClass().getSimpleName() + ".collectTime", time2 - time1); 243 stats.put(getClass().getSimpleName() + ".transformTime", time3 - time2); 244 logger.debug("Dependency collection stats {}", stats); 245 } 246 247 if (errorPath != null) { 248 throw new DependencyCollectionException(result, "Failed to collect dependencies at " + errorPath); 249 } 250 if (!result.getExceptions().isEmpty()) { 251 throw new DependencyCollectionException(result); 252 } 253 254 if (request.getResolutionScope() != null) { 255 return scopeManager.postProcess(request.getResolutionScope(), result); 256 } else { 257 return result; 258 } 259 } 260 261 /** 262 * Creates child {@link RequestTrace} instance from passed in {@link RequestTrace} and parameters by creating 263 * {@link CollectStepDataImpl} instance out of passed in data. Caller must ensure that passed in parameters are 264 * NOT affected by threading (or that there is no multi threading involved). In other words, the passed in values 265 * should be immutable. 266 * 267 * @param trace The current trace instance. 268 * @param context The context from {@link CollectRequest#getRequestContext()}, never {@code null}. 269 * @param path List representing the path of dependency nodes, never {@code null}. Caller must ensure, that this 270 * list does not change during the lifetime of the requested {@link RequestTrace} instance. If it may 271 * change, simplest is to pass here a copy of used list. 272 * @param node Currently collected node, that collector came by following the passed in path. 273 * @return A child request trance instance, never {@code null}. 274 */ 275 protected RequestTrace collectStepTrace( 276 RequestTrace trace, String context, List<DependencyNode> path, Dependency node) { 277 return RequestTrace.newChild(trace, new CollectStepDataImpl(context, path, node)); 278 } 279 280 @SuppressWarnings("checkstyle:parameternumber") 281 protected abstract void doCollectDependencies( 282 RepositorySystemSession session, 283 RequestTrace trace, 284 DataPool pool, 285 DefaultDependencyCollectionContext context, 286 DefaultVersionFilterContext versionContext, 287 CollectRequest request, 288 DependencyNode node, 289 List<RemoteRepository> repositories, 290 List<Dependency> dependencies, 291 List<Dependency> managedDependencies, 292 Results results) 293 throws DependencyCollectionException; 294 295 protected RepositorySystemSession setUpSession( 296 RepositorySystemSession session, CollectRequest collectRequest, InternalScopeManager scopeManager) { 297 DefaultRepositorySystemSession optimized = new DefaultRepositorySystemSession(session); 298 optimized.setArtifactTypeRegistry(CachingArtifactTypeRegistry.newInstance(session)); 299 300 ResolutionScope resolutionScope = collectRequest.getResolutionScope(); 301 if (resolutionScope != null) { 302 requireNonNull(scopeManager, "ScopeManager is not set on session"); 303 optimized.setDependencySelector(scopeManager.getDependencySelector(resolutionScope)); 304 optimized.setDependencyGraphTransformer(scopeManager.getDependencyGraphTransformer(resolutionScope)); 305 } 306 return optimized; 307 } 308 309 protected List<Dependency> mergeDeps(List<Dependency> dominant, List<Dependency> recessive) { 310 List<Dependency> result; 311 if (dominant == null || dominant.isEmpty()) { 312 result = recessive; 313 } else if (recessive == null || recessive.isEmpty()) { 314 result = dominant; 315 } else { 316 int initialCapacity = dominant.size() + recessive.size(); 317 result = new ArrayList<>(initialCapacity); 318 Collection<String> ids = new HashSet<>(initialCapacity, 1.0f); 319 for (Dependency dependency : dominant) { 320 ids.add(getId(dependency.getArtifact())); 321 result.add(dependency); 322 } 323 for (Dependency dependency : recessive) { 324 if (!ids.contains(getId(dependency.getArtifact()))) { 325 result.add(dependency); 326 } 327 } 328 } 329 return result; 330 } 331 332 protected static String getId(Artifact a) { 333 return a.getGroupId() + ':' + a.getArtifactId() + ':' + a.getClassifier() + ':' + a.getExtension(); 334 } 335 336 @SuppressWarnings("checkstyle:parameternumber") 337 protected static DefaultDependencyNode createDependencyNode( 338 List<Artifact> relocations, 339 PremanagedDependency preManaged, 340 VersionRangeResult rangeResult, 341 Version version, 342 Dependency d, 343 Collection<Artifact> aliases, 344 List<RemoteRepository> repos, 345 String requestContext) { 346 DefaultDependencyNode child = new DefaultDependencyNode(d); 347 preManaged.applyTo(child); 348 child.setRelocations(relocations); 349 child.setVersionConstraint(rangeResult.getVersionConstraint()); 350 child.setVersion(version); 351 child.setAliases(aliases); 352 child.setRepositories(repos); 353 child.setRequestContext(requestContext); 354 return child; 355 } 356 357 protected static DefaultDependencyNode createDependencyNode( 358 List<Artifact> relocations, 359 PremanagedDependency preManaged, 360 VersionRangeResult rangeResult, 361 Version version, 362 Dependency d, 363 ArtifactDescriptorResult descriptorResult, 364 DependencyNode cycleNode) { 365 DefaultDependencyNode child = createDependencyNode( 366 relocations, 367 preManaged, 368 rangeResult, 369 version, 370 d, 371 descriptorResult.getAliases(), 372 cycleNode.getRepositories(), 373 cycleNode.getRequestContext()); 374 child.setChildren(cycleNode.getChildren()); 375 return child; 376 } 377 378 protected static ArtifactDescriptorRequest createArtifactDescriptorRequest( 379 String requestContext, RequestTrace requestTrace, List<RemoteRepository> repositories, Dependency d) { 380 ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest(); 381 descriptorRequest.setArtifact(d.getArtifact()); 382 descriptorRequest.setRepositories(repositories); 383 descriptorRequest.setRequestContext(requestContext); 384 descriptorRequest.setTrace(requestTrace); 385 return descriptorRequest; 386 } 387 388 protected static VersionRangeRequest createVersionRangeRequest( 389 String requestContext, 390 RequestTrace requestTrace, 391 List<RemoteRepository> repositories, 392 Dependency dependency) { 393 VersionRangeRequest rangeRequest = new VersionRangeRequest(); 394 rangeRequest.setArtifact(dependency.getArtifact()); 395 rangeRequest.setRepositories(repositories); 396 rangeRequest.setRequestContext(requestContext); 397 rangeRequest.setTrace(requestTrace); 398 return rangeRequest; 399 } 400 401 protected VersionRangeResult cachedResolveRangeResult( 402 VersionRangeRequest rangeRequest, DataPool pool, RepositorySystemSession session) 403 throws VersionRangeResolutionException { 404 Object key = pool.toKey(rangeRequest); 405 VersionRangeResult rangeResult = pool.getConstraint(key, rangeRequest); 406 if (rangeResult == null) { 407 rangeResult = versionRangeResolver.resolveVersionRange(session, rangeRequest); 408 pool.putConstraint(key, rangeResult); 409 } 410 return rangeResult; 411 } 412 413 protected static boolean isLackingDescriptor(RepositorySystemSession session, Artifact artifact) { 414 SystemDependencyScope systemDependencyScope = session.getSystemDependencyScope(); 415 return systemDependencyScope != null && systemDependencyScope.getSystemPath(artifact) != null; 416 } 417 418 protected static List<RemoteRepository> getRemoteRepositories( 419 ArtifactRepository repository, List<RemoteRepository> repositories) { 420 if (repository instanceof RemoteRepository) { 421 return Collections.singletonList((RemoteRepository) repository); 422 } 423 if (repository != null) { 424 return Collections.emptyList(); 425 } 426 return repositories; 427 } 428 429 protected static List<? extends Version> filterVersions( 430 Dependency dependency, 431 VersionRangeResult rangeResult, 432 VersionFilter verFilter, 433 DefaultVersionFilterContext verContext) 434 throws VersionRangeResolutionException { 435 if (rangeResult.getVersions().isEmpty()) { 436 throw new VersionRangeResolutionException( 437 rangeResult, "No versions available for " + dependency.getArtifact() + " within specified range"); 438 } 439 440 List<? extends Version> versions; 441 if (verFilter != null && rangeResult.getVersionConstraint().getRange() != null) { 442 verContext.set(dependency, rangeResult); 443 try { 444 verFilter.filterVersions(verContext); 445 } catch (RepositoryException e) { 446 throw new VersionRangeResolutionException( 447 rangeResult, "Failed to filter versions for " + dependency.getArtifact(), e); 448 } 449 versions = verContext.get(); 450 if (versions.isEmpty()) { 451 throw new VersionRangeResolutionException( 452 rangeResult, 453 "No acceptable versions for " + dependency.getArtifact() + ": " + rangeResult.getVersions()); 454 } 455 } else { 456 versions = rangeResult.getVersions(); 457 } 458 return versions; 459 } 460 461 /** 462 * Helper class used during collection. 463 */ 464 protected static class Results { 465 466 private final CollectResult result; 467 468 final int maxExceptions; 469 470 final int maxCycles; 471 472 String errorPath; 473 474 public Results(CollectResult result, RepositorySystemSession session) { 475 this.result = result; 476 477 maxExceptions = ConfigUtils.getInteger(session, DEFAULT_MAX_EXCEPTIONS, CONFIG_PROP_MAX_EXCEPTIONS); 478 479 maxCycles = ConfigUtils.getInteger(session, DEFAULT_MAX_CYCLES, CONFIG_PROP_MAX_CYCLES); 480 } 481 482 public CollectResult getResult() { 483 return result; 484 } 485 486 public String getErrorPath() { 487 return errorPath; 488 } 489 490 public void addException(Dependency dependency, Exception e, List<DependencyNode> nodes) { 491 if (maxExceptions < 0 || result.getExceptions().size() < maxExceptions) { 492 result.addException(e); 493 if (errorPath == null) { 494 StringBuilder buffer = new StringBuilder(256); 495 for (DependencyNode node : nodes) { 496 if (buffer.length() > 0) { 497 buffer.append(" -> "); 498 } 499 Dependency dep = node.getDependency(); 500 if (dep != null) { 501 buffer.append(dep.getArtifact()); 502 } 503 } 504 if (buffer.length() > 0) { 505 buffer.append(" -> "); 506 } 507 buffer.append(dependency.getArtifact()); 508 errorPath = buffer.toString(); 509 } 510 } 511 } 512 513 public void addCycle(List<DependencyNode> nodes, int cycleEntry, Dependency dependency) { 514 if (maxCycles < 0 || result.getCycles().size() < maxCycles) { 515 result.addCycle(new DefaultDependencyCycle(nodes, cycleEntry, dependency)); 516 } 517 } 518 } 519}