001package org.apache.maven.artifact.resolver; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.io.File; 023import java.util.ArrayList; 024import java.util.Collections; 025import java.util.LinkedHashMap; 026import java.util.LinkedHashSet; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030import java.util.concurrent.CountDownLatch; 031import java.util.concurrent.Executor; 032import java.util.concurrent.ExecutorService; 033import java.util.concurrent.LinkedBlockingQueue; 034import java.util.concurrent.ThreadFactory; 035import java.util.concurrent.ThreadPoolExecutor; 036import java.util.concurrent.TimeUnit; 037import java.util.concurrent.atomic.AtomicInteger; 038import java.util.regex.Matcher; 039 040import org.apache.maven.RepositoryUtils; 041import org.apache.maven.artifact.Artifact; 042import org.apache.maven.artifact.factory.ArtifactFactory; 043import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException; 044import org.apache.maven.artifact.metadata.ArtifactMetadataSource; 045import org.apache.maven.artifact.metadata.ResolutionGroup; 046import org.apache.maven.artifact.repository.ArtifactRepository; 047import org.apache.maven.artifact.repository.LegacyLocalRepositoryManager; 048import org.apache.maven.artifact.repository.RepositoryRequest; 049import org.apache.maven.artifact.repository.metadata.Snapshot; 050import org.apache.maven.artifact.repository.metadata.SnapshotArtifactRepositoryMetadata; 051import org.apache.maven.artifact.resolver.filter.ArtifactFilter; 052import org.apache.maven.execution.MavenSession; 053import org.apache.maven.plugin.LegacySupport; 054import org.apache.maven.repository.legacy.metadata.DefaultMetadataResolutionRequest; 055import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest; 056import org.apache.maven.repository.legacy.resolver.conflict.ConflictResolver; 057import org.apache.maven.wagon.events.TransferListener; 058import org.codehaus.plexus.PlexusContainer; 059import org.codehaus.plexus.component.annotations.Component; 060import org.codehaus.plexus.component.annotations.Requirement; 061import org.codehaus.plexus.component.repository.exception.ComponentLookupException; 062import org.codehaus.plexus.logging.Logger; 063import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable; 064import org.eclipse.aether.RepositorySystem; 065import org.eclipse.aether.RepositorySystemSession; 066import org.eclipse.aether.repository.LocalRepositoryManager; 067import org.eclipse.aether.resolution.ArtifactRequest; 068import org.eclipse.aether.resolution.ArtifactResult; 069 070/** 071 * @author Jason van Zyl 072 */ 073@Component( role = ArtifactResolver.class ) 074public class DefaultArtifactResolver 075 implements ArtifactResolver, Disposable 076{ 077 @Requirement 078 private Logger logger; 079 080 @Requirement 081 protected ArtifactFactory artifactFactory; 082 083 @Requirement 084 private ArtifactCollector artifactCollector; 085 086 @Requirement 087 private ResolutionErrorHandler resolutionErrorHandler; 088 089 @Requirement 090 private ArtifactMetadataSource source; 091 092 @Requirement 093 private PlexusContainer container; 094 095 @Requirement 096 private LegacySupport legacySupport; 097 098 @Requirement 099 private RepositorySystem repoSystem; 100 101 private final Executor executor; 102 103 public DefaultArtifactResolver() 104 { 105 int threads = Integer.getInteger( "maven.artifact.threads", 5 ); 106 if ( threads <= 1 ) 107 { 108 executor = new Executor() 109 { 110 public void execute( Runnable command ) 111 { 112 command.run(); 113 } 114 }; 115 } 116 else 117 { 118 executor = new ThreadPoolExecutor( threads, threads, 3, TimeUnit.SECONDS, 119 new LinkedBlockingQueue<Runnable>(), new DaemonThreadCreator() ); 120 } 121 } 122 123 private RepositorySystemSession getSession( ArtifactRepository localRepository ) 124 { 125 return LegacyLocalRepositoryManager.overlay( localRepository, legacySupport.getRepositorySession(), 126 repoSystem ); 127 } 128 129 private void injectSession1( RepositoryRequest request, MavenSession session ) 130 { 131 if ( session != null ) 132 { 133 request.setOffline( session.isOffline() ); 134 request.setForceUpdate( session.getRequest().isUpdateSnapshots() ); 135 } 136 } 137 138 private void injectSession2( ArtifactResolutionRequest request, MavenSession session ) 139 { 140 injectSession1( request, session ); 141 142 if ( session != null ) 143 { 144 request.setServers( session.getRequest().getServers() ); 145 request.setMirrors( session.getRequest().getMirrors() ); 146 request.setProxies( session.getRequest().getProxies() ); 147 } 148 } 149 150 public void resolve( Artifact artifact, List<ArtifactRepository> remoteRepositories, 151 ArtifactRepository localRepository, TransferListener resolutionListener ) 152 throws ArtifactResolutionException, ArtifactNotFoundException 153 { 154 resolve( artifact, remoteRepositories, getSession( localRepository ) ); 155 } 156 157 public void resolveAlways( Artifact artifact, List<ArtifactRepository> remoteRepositories, 158 ArtifactRepository localRepository ) 159 throws ArtifactResolutionException, ArtifactNotFoundException 160 { 161 resolve( artifact, remoteRepositories, getSession( localRepository ) ); 162 } 163 164 private void resolve( Artifact artifact, List<ArtifactRepository> remoteRepositories, 165 RepositorySystemSession session ) 166 throws ArtifactResolutionException, ArtifactNotFoundException 167 { 168 if ( artifact == null ) 169 { 170 return; 171 } 172 173 if ( Artifact.SCOPE_SYSTEM.equals( artifact.getScope() ) ) 174 { 175 File systemFile = artifact.getFile(); 176 177 if ( systemFile == null ) 178 { 179 throw new ArtifactNotFoundException( "System artifact: " + artifact + " has no file attached", 180 artifact ); 181 } 182 183 if ( !systemFile.exists() ) 184 { 185 throw new ArtifactNotFoundException( "System artifact: " + artifact + " not found in path: " 186 + systemFile, artifact ); 187 } 188 189 if ( !systemFile.isFile() ) 190 { 191 throw new ArtifactNotFoundException( "System artifact: " + artifact + " is not a file: " + systemFile, 192 artifact ); 193 } 194 195 artifact.setResolved( true ); 196 197 return; 198 } 199 200 if ( !artifact.isResolved() ) 201 { 202 ArtifactResult result; 203 204 try 205 { 206 ArtifactRequest artifactRequest = new ArtifactRequest(); 207 artifactRequest.setArtifact( RepositoryUtils.toArtifact( artifact ) ); 208 artifactRequest.setRepositories( RepositoryUtils.toRepos( remoteRepositories ) ); 209 210 // Maven 2.x quirk: an artifact always points at the local repo, regardless whether resolved or not 211 LocalRepositoryManager lrm = session.getLocalRepositoryManager(); 212 String path = lrm.getPathForLocalArtifact( artifactRequest.getArtifact() ); 213 artifact.setFile( new File( lrm.getRepository().getBasedir(), path ) ); 214 215 result = repoSystem.resolveArtifact( session, artifactRequest ); 216 } 217 catch ( org.eclipse.aether.resolution.ArtifactResolutionException e ) 218 { 219 if ( e.getCause() instanceof org.eclipse.aether.transfer.ArtifactNotFoundException ) 220 { 221 throw new ArtifactNotFoundException( e.getMessage(), artifact, remoteRepositories, e ); 222 } 223 else 224 { 225 throw new ArtifactResolutionException( e.getMessage(), artifact, remoteRepositories, e ); 226 } 227 } 228 229 artifact.selectVersion( result.getArtifact().getVersion() ); 230 artifact.setFile( result.getArtifact().getFile() ); 231 artifact.setResolved( true ); 232 233 if ( artifact.isSnapshot() ) 234 { 235 Matcher matcher = Artifact.VERSION_FILE_PATTERN.matcher( artifact.getVersion() ); 236 if ( matcher.matches() ) 237 { 238 Snapshot snapshot = new Snapshot(); 239 snapshot.setTimestamp( matcher.group( 2 ) ); 240 try 241 { 242 snapshot.setBuildNumber( Integer.parseInt( matcher.group( 3 ) ) ); 243 artifact.addMetadata( new SnapshotArtifactRepositoryMetadata( artifact, snapshot ) ); 244 } 245 catch ( NumberFormatException e ) 246 { 247 logger.warn( "Invalid artifact version " + artifact.getVersion() + ": " + e.getMessage() ); 248 } 249 } 250 } 251 } 252 } 253 254 public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact, 255 ArtifactRepository localRepository, 256 List<ArtifactRepository> remoteRepositories, 257 ArtifactMetadataSource source, ArtifactFilter filter ) 258 throws ArtifactResolutionException, 259 ArtifactNotFoundException 260 { 261 return resolveTransitively( artifacts, originatingArtifact, Collections.EMPTY_MAP, localRepository, 262 remoteRepositories, source, filter ); 263 264 } 265 266 public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact, 267 Map<String, Artifact> managedVersions, 268 ArtifactRepository localRepository, 269 List<ArtifactRepository> remoteRepositories, 270 ArtifactMetadataSource source ) 271 throws ArtifactResolutionException, 272 ArtifactNotFoundException 273 { 274 return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository, 275 remoteRepositories, source, null ); 276 } 277 278 public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact, 279 Map<String, Artifact> managedVersions, 280 ArtifactRepository localRepository, 281 List<ArtifactRepository> remoteRepositories, 282 ArtifactMetadataSource source, ArtifactFilter filter ) 283 throws ArtifactResolutionException, 284 ArtifactNotFoundException 285 { 286 return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository, 287 remoteRepositories, source, filter, null ); 288 } 289 290 public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact, 291 List<ArtifactRepository> remoteRepositories, 292 ArtifactRepository localRepository, 293 ArtifactMetadataSource source ) 294 throws ArtifactResolutionException, 295 ArtifactNotFoundException 296 { 297 return resolveTransitively( artifacts, originatingArtifact, localRepository, remoteRepositories, source, null ); 298 } 299 300 public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact, 301 List<ArtifactRepository> remoteRepositories, 302 ArtifactRepository localRepository, 303 ArtifactMetadataSource source, 304 List<ResolutionListener> listeners ) 305 throws ArtifactResolutionException, 306 ArtifactNotFoundException 307 { 308 return resolveTransitively( artifacts, originatingArtifact, Collections.EMPTY_MAP, localRepository, 309 remoteRepositories, source, null, listeners ); 310 } 311 312 public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact, 313 Map<String, Artifact> managedVersions, 314 ArtifactRepository localRepository, 315 List<ArtifactRepository> remoteRepositories, 316 ArtifactMetadataSource source, ArtifactFilter filter, 317 List<ResolutionListener> listeners ) 318 throws ArtifactResolutionException, 319 ArtifactNotFoundException 320 { 321 return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository, 322 remoteRepositories, source, filter, listeners, null ); 323 } 324 325 public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact, 326 Map<String, Artifact> managedVersions, 327 ArtifactRepository localRepository, 328 List<ArtifactRepository> remoteRepositories, 329 ArtifactMetadataSource source, ArtifactFilter filter, 330 List<ResolutionListener> listeners, 331 List<ConflictResolver> conflictResolvers ) 332 throws ArtifactResolutionException, 333 ArtifactNotFoundException 334 { 335 ArtifactResolutionRequest request = 336 new ArtifactResolutionRequest().setArtifact( originatingArtifact ).setResolveRoot( false ) 337 // This is required by the surefire plugin 338 .setArtifactDependencies( artifacts ).setManagedVersionMap( managedVersions ).setLocalRepository( localRepository ).setRemoteRepositories( remoteRepositories ).setCollectionFilter( filter ).setListeners( listeners ); 339 340 injectSession2( request, legacySupport.getSession() ); 341 342 return resolveWithExceptions( request ); 343 } 344 345 public ArtifactResolutionResult resolveWithExceptions( ArtifactResolutionRequest request ) 346 throws ArtifactResolutionException, ArtifactNotFoundException 347 { 348 ArtifactResolutionResult result = resolve( request ); 349 350 // We have collected all the problems so let's mimic the way the old code worked and just blow up right here. 351 // That's right lets just let it rip right here and send a big incomprehensible blob of text at unsuspecting 352 // users. Bad dog! 353 354 resolutionErrorHandler.throwErrors( request, result ); 355 356 return result; 357 } 358 359 // ------------------------------------------------------------------------ 360 // 361 // ------------------------------------------------------------------------ 362 363 public ArtifactResolutionResult resolve( ArtifactResolutionRequest request ) 364 { 365 Artifact rootArtifact = request.getArtifact(); 366 Set<Artifact> artifacts = request.getArtifactDependencies(); 367 Map<String, Artifact> managedVersions = request.getManagedVersionMap(); 368 List<ResolutionListener> listeners = request.getListeners(); 369 ArtifactFilter collectionFilter = request.getCollectionFilter(); 370 ArtifactFilter resolutionFilter = request.getResolutionFilter(); 371 RepositorySystemSession session = getSession( request.getLocalRepository() ); 372 373 // TODO: hack because metadata isn't generated in m2e correctly and i want to run the maven i have in the 374 // workspace 375 if ( source == null ) 376 { 377 try 378 { 379 source = container.lookup( ArtifactMetadataSource.class ); 380 } 381 catch ( ComponentLookupException e ) 382 { 383 // won't happen 384 } 385 } 386 387 if ( listeners == null ) 388 { 389 listeners = new ArrayList<>(); 390 391 if ( logger.isDebugEnabled() ) 392 { 393 listeners.add( new DebugResolutionListener( logger ) ); 394 } 395 396 listeners.add( new WarningResolutionListener( logger ) ); 397 } 398 399 ArtifactResolutionResult result = new ArtifactResolutionResult(); 400 401 // The root artifact may, or may not be resolved so we need to check before we attempt to resolve. 402 // This is often an artifact like a POM that is taken from disk and we already have hold of the 403 // file reference. But this may be a Maven Plugin that we need to resolve from a remote repository 404 // as well as its dependencies. 405 406 if ( request.isResolveRoot() /* && rootArtifact.getFile() == null */ ) 407 { 408 try 409 { 410 resolve( rootArtifact, request.getRemoteRepositories(), session ); 411 } 412 catch ( ArtifactResolutionException e ) 413 { 414 result.addErrorArtifactException( e ); 415 return result; 416 } 417 catch ( ArtifactNotFoundException e ) 418 { 419 result.addMissingArtifact( request.getArtifact() ); 420 return result; 421 } 422 } 423 424 ArtifactResolutionRequest collectionRequest = request; 425 426 if ( request.isResolveTransitively() ) 427 { 428 MetadataResolutionRequest metadataRequest = new DefaultMetadataResolutionRequest( request ); 429 430 metadataRequest.setArtifact( rootArtifact ); 431 metadataRequest.setResolveManagedVersions( managedVersions == null ); 432 433 try 434 { 435 ResolutionGroup resolutionGroup = source.retrieve( metadataRequest ); 436 437 if ( managedVersions == null ) 438 { 439 managedVersions = resolutionGroup.getManagedVersions(); 440 } 441 442 Set<Artifact> directArtifacts = resolutionGroup.getArtifacts(); 443 444 if ( artifacts == null || artifacts.isEmpty() ) 445 { 446 artifacts = directArtifacts; 447 } 448 else 449 { 450 List<Artifact> allArtifacts = new ArrayList<>(); 451 allArtifacts.addAll( artifacts ); 452 allArtifacts.addAll( directArtifacts ); 453 454 Map<String, Artifact> mergedArtifacts = new LinkedHashMap<>(); 455 for ( Artifact artifact : allArtifacts ) 456 { 457 String conflictId = artifact.getDependencyConflictId(); 458 if ( !mergedArtifacts.containsKey( conflictId ) ) 459 { 460 mergedArtifacts.put( conflictId, artifact ); 461 } 462 } 463 464 artifacts = new LinkedHashSet<>( mergedArtifacts.values() ); 465 } 466 467 collectionRequest = new ArtifactResolutionRequest( request ); 468 collectionRequest.setServers( request.getServers() ); 469 collectionRequest.setMirrors( request.getMirrors() ); 470 collectionRequest.setProxies( request.getProxies() ); 471 collectionRequest.setRemoteRepositories( resolutionGroup.getResolutionRepositories() ); 472 } 473 catch ( ArtifactMetadataRetrievalException e ) 474 { 475 ArtifactResolutionException are = 476 new ArtifactResolutionException( "Unable to get dependency information for " + rootArtifact.getId() 477 + ": " + e.getMessage(), rootArtifact, metadataRequest.getRemoteRepositories(), e ); 478 result.addMetadataResolutionException( are ); 479 return result; 480 } 481 } 482 483 if ( artifacts == null || artifacts.isEmpty() ) 484 { 485 if ( request.isResolveRoot() ) 486 { 487 result.addArtifact( rootArtifact ); 488 } 489 return result; 490 } 491 492 // After the collection we will have the artifact object in the result but they will not be resolved yet. 493 result = artifactCollector.collect( artifacts, rootArtifact, managedVersions, collectionRequest, source, 494 collectionFilter, listeners, null ); 495 496 // We have metadata retrieval problems, or there are cycles that have been detected 497 // so we give this back to the calling code and let them deal with this information 498 // appropriately. 499 500 if ( result.hasMetadataResolutionExceptions() || result.hasVersionRangeViolations() 501 || result.hasCircularDependencyExceptions() ) 502 { 503 return result; 504 } 505 506 if ( result.getArtifactResolutionNodes() != null ) 507 { 508 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 509 510 CountDownLatch latch = new CountDownLatch( result.getArtifactResolutionNodes().size() ); 511 512 for ( ResolutionNode node : result.getArtifactResolutionNodes() ) 513 { 514 Artifact artifact = node.getArtifact(); 515 516 if ( resolutionFilter == null || resolutionFilter.include( artifact ) ) 517 { 518 executor.execute( new ResolveTask( classLoader, latch, artifact, session, 519 node.getRemoteRepositories(), result ) ); 520 } 521 else 522 { 523 latch.countDown(); 524 } 525 } 526 try 527 { 528 latch.await(); 529 } 530 catch ( InterruptedException e ) 531 { 532 result.addErrorArtifactException( new ArtifactResolutionException( "Resolution interrupted", 533 rootArtifact, e ) ); 534 } 535 } 536 537 // We want to send the root artifact back in the result but we need to do this after the other dependencies 538 // have been resolved. 539 if ( request.isResolveRoot() ) 540 { 541 // Add the root artifact (as the first artifact to retain logical order of class path!) 542 Set<Artifact> allArtifacts = new LinkedHashSet<>(); 543 allArtifacts.add( rootArtifact ); 544 allArtifacts.addAll( result.getArtifacts() ); 545 result.setArtifacts( allArtifacts ); 546 } 547 548 return result; 549 } 550 551 public void resolve( Artifact artifact, List<ArtifactRepository> remoteRepositories, 552 ArtifactRepository localRepository ) 553 throws ArtifactResolutionException, ArtifactNotFoundException 554 { 555 resolve( artifact, remoteRepositories, localRepository, null ); 556 } 557 558 /** 559 * ThreadCreator for creating daemon threads with fixed ThreadGroup-name. 560 */ 561 static final class DaemonThreadCreator 562 implements ThreadFactory 563 { 564 static final String THREADGROUP_NAME = "org.apache.maven.artifact.resolver.DefaultArtifactResolver"; 565 566 static final ThreadGroup GROUP = new ThreadGroup( THREADGROUP_NAME ); 567 568 static final AtomicInteger THREAD_NUMBER = new AtomicInteger( 1 ); 569 570 public Thread newThread( Runnable r ) 571 { 572 Thread newThread = new Thread( GROUP, r, "resolver-" + THREAD_NUMBER.getAndIncrement() ); 573 newThread.setDaemon( true ); 574 newThread.setContextClassLoader( null ); 575 return newThread; 576 } 577 } 578 579 private class ResolveTask 580 implements Runnable 581 { 582 583 private final ClassLoader classLoader; 584 585 private final CountDownLatch latch; 586 587 private final Artifact artifact; 588 589 private final RepositorySystemSession session; 590 591 private final List<ArtifactRepository> remoteRepositories; 592 593 private final ArtifactResolutionResult result; 594 595 public ResolveTask( ClassLoader classLoader, CountDownLatch latch, Artifact artifact, 596 RepositorySystemSession session, List<ArtifactRepository> remoteRepositories, 597 ArtifactResolutionResult result ) 598 { 599 this.classLoader = classLoader; 600 this.latch = latch; 601 this.artifact = artifact; 602 this.session = session; 603 this.remoteRepositories = remoteRepositories; 604 this.result = result; 605 } 606 607 public void run() 608 { 609 ClassLoader old = Thread.currentThread().getContextClassLoader(); 610 try 611 { 612 Thread.currentThread().setContextClassLoader( classLoader ); 613 resolve( artifact, remoteRepositories, session ); 614 } 615 catch ( ArtifactNotFoundException anfe ) 616 { 617 // These are cases where the artifact just isn't present in any of the remote repositories 618 // because it wasn't deployed, or it was deployed in the wrong place. 619 620 synchronized ( result ) 621 { 622 result.addMissingArtifact( artifact ); 623 } 624 } 625 catch ( ArtifactResolutionException e ) 626 { 627 // This is really a wagon TransferFailedException so something went wrong after we successfully 628 // retrieved the metadata. 629 630 synchronized ( result ) 631 { 632 result.addErrorArtifactException( e ); 633 } 634 } 635 finally 636 { 637 latch.countDown(); 638 Thread.currentThread().setContextClassLoader( old ); 639 640 } 641 } 642 643 } 644 645 @Override 646 public void dispose() 647 { 648 if ( executor instanceof ExecutorService ) 649 { 650 ( (ExecutorService) executor ).shutdownNow(); 651 } 652 } 653 654}