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 = 119 new ThreadPoolExecutor( threads, threads, 3, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), 120 new DaemonThreadCreator() ); 121 } 122 } 123 124 private RepositorySystemSession getSession( ArtifactRepository localRepository ) 125 { 126 return LegacyLocalRepositoryManager.overlay( localRepository, legacySupport.getRepositorySession(), 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", artifact ); 180 } 181 182 if ( !systemFile.exists() ) 183 { 184 throw new ArtifactNotFoundException( "System artifact: " + artifact + " not found in path: " + systemFile, artifact ); 185 } 186 187 if ( !systemFile.isFile() ) 188 { 189 throw new ArtifactNotFoundException( "System artifact: " + artifact + " is not a file: " + systemFile, artifact ); 190 } 191 192 artifact.setResolved( true ); 193 194 return; 195 } 196 197 if ( !artifact.isResolved() ) 198 { 199 ArtifactResult result; 200 201 try 202 { 203 ArtifactRequest artifactRequest = new ArtifactRequest(); 204 artifactRequest.setArtifact( RepositoryUtils.toArtifact( artifact ) ); 205 artifactRequest.setRepositories( RepositoryUtils.toRepos( remoteRepositories ) ); 206 207 // Maven 2.x quirk: an artifact always points at the local repo, regardless whether resolved or not 208 LocalRepositoryManager lrm = session.getLocalRepositoryManager(); 209 String path = lrm.getPathForLocalArtifact( artifactRequest.getArtifact() ); 210 artifact.setFile( new File( lrm.getRepository().getBasedir(), path ) ); 211 212 result = repoSystem.resolveArtifact( session, artifactRequest ); 213 } 214 catch ( org.eclipse.aether.resolution.ArtifactResolutionException e ) 215 { 216 if ( e.getCause() instanceof org.eclipse.aether.transfer.ArtifactNotFoundException ) 217 { 218 throw new ArtifactNotFoundException( e.getMessage(), artifact, remoteRepositories, e ); 219 } 220 else 221 { 222 throw new ArtifactResolutionException( e.getMessage(), artifact, remoteRepositories, e ); 223 } 224 } 225 226 artifact.selectVersion( result.getArtifact().getVersion() ); 227 artifact.setFile( result.getArtifact().getFile() ); 228 artifact.setResolved( true ); 229 230 if ( artifact.isSnapshot() ) 231 { 232 Matcher matcher = Artifact.VERSION_FILE_PATTERN.matcher( artifact.getVersion() ); 233 if ( matcher.matches() ) 234 { 235 Snapshot snapshot = new Snapshot(); 236 snapshot.setTimestamp( matcher.group( 2 ) ); 237 try 238 { 239 snapshot.setBuildNumber( Integer.parseInt( matcher.group( 3 ) ) ); 240 artifact.addMetadata( new SnapshotArtifactRepositoryMetadata( artifact, snapshot ) ); 241 } 242 catch ( NumberFormatException e ) 243 { 244 logger.warn( "Invalid artifact version " + artifact.getVersion() + ": " + e.getMessage() ); 245 } 246 } 247 } 248 } 249 } 250 251 public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact, 252 ArtifactRepository localRepository, 253 List<ArtifactRepository> remoteRepositories, 254 ArtifactMetadataSource source, ArtifactFilter filter ) 255 throws ArtifactResolutionException, ArtifactNotFoundException 256 { 257 return resolveTransitively( artifacts, originatingArtifact, Collections.EMPTY_MAP, localRepository, 258 remoteRepositories, source, filter ); 259 260 } 261 262 public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact, 263 Map managedVersions, ArtifactRepository localRepository, 264 List<ArtifactRepository> remoteRepositories, 265 ArtifactMetadataSource source ) 266 throws ArtifactResolutionException, ArtifactNotFoundException 267 { 268 return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository, 269 remoteRepositories, source, null ); 270 } 271 272 public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact, 273 Map managedVersions, ArtifactRepository localRepository, 274 List<ArtifactRepository> remoteRepositories, 275 ArtifactMetadataSource source, ArtifactFilter filter ) 276 throws ArtifactResolutionException, ArtifactNotFoundException 277 { 278 return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository, 279 remoteRepositories, source, filter, null ); 280 } 281 282 public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact, 283 List<ArtifactRepository> remoteRepositories, 284 ArtifactRepository localRepository, 285 ArtifactMetadataSource source ) 286 throws ArtifactResolutionException, ArtifactNotFoundException 287 { 288 return resolveTransitively( artifacts, originatingArtifact, localRepository, remoteRepositories, source, null ); 289 } 290 291 public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact, 292 List<ArtifactRepository> remoteRepositories, 293 ArtifactRepository localRepository, 294 ArtifactMetadataSource source, 295 List<ResolutionListener> listeners ) 296 throws ArtifactResolutionException, ArtifactNotFoundException 297 { 298 return resolveTransitively( artifacts, originatingArtifact, Collections.EMPTY_MAP, localRepository, 299 remoteRepositories, source, null, listeners ); 300 } 301 302 public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact, 303 Map managedVersions, ArtifactRepository localRepository, 304 List<ArtifactRepository> remoteRepositories, 305 ArtifactMetadataSource source, ArtifactFilter filter, 306 List<ResolutionListener> listeners ) 307 throws ArtifactResolutionException, ArtifactNotFoundException 308 { 309 return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository, 310 remoteRepositories, source, filter, listeners, null ); 311 } 312 313 public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact, 314 Map managedVersions, ArtifactRepository localRepository, 315 List<ArtifactRepository> remoteRepositories, 316 ArtifactMetadataSource source, ArtifactFilter filter, 317 List<ResolutionListener> listeners, 318 List<ConflictResolver> conflictResolvers ) 319 throws ArtifactResolutionException, ArtifactNotFoundException 320 { 321 ArtifactResolutionRequest request = new ArtifactResolutionRequest() 322 .setArtifact( originatingArtifact ) 323 .setResolveRoot( false ) 324 // This is required by the surefire plugin 325 .setArtifactDependencies( artifacts ) 326 .setManagedVersionMap( managedVersions ) 327 .setLocalRepository( localRepository ) 328 .setRemoteRepositories( remoteRepositories ) 329 .setCollectionFilter( filter ) 330 .setListeners( listeners ); 331 332 injectSession2( request, legacySupport.getSession() ); 333 334 return resolveWithExceptions( request ); 335 } 336 337 public ArtifactResolutionResult resolveWithExceptions( ArtifactResolutionRequest request ) 338 throws ArtifactResolutionException, ArtifactNotFoundException 339 { 340 ArtifactResolutionResult result = resolve( request ); 341 342 // We have collected all the problems so let's mimic the way the old code worked and just blow up right here. 343 // That's right lets just let it rip right here and send a big incomprehensible blob of text at unsuspecting 344 // users. Bad dog! 345 346 resolutionErrorHandler.throwErrors( request, result ); 347 348 return result; 349 } 350 351 // ------------------------------------------------------------------------ 352 // 353 // ------------------------------------------------------------------------ 354 355 public ArtifactResolutionResult resolve( ArtifactResolutionRequest request ) 356 { 357 Artifact rootArtifact = request.getArtifact(); 358 Set<Artifact> artifacts = request.getArtifactDependencies(); 359 Map<String, Artifact> managedVersions = request.getManagedVersionMap(); 360 List<ResolutionListener> listeners = request.getListeners(); 361 ArtifactFilter collectionFilter = request.getCollectionFilter(); 362 ArtifactFilter resolutionFilter = request.getResolutionFilter(); 363 RepositorySystemSession session = getSession( request.getLocalRepository() ); 364 365 //TODO: hack because metadata isn't generated in m2e correctly and i want to run the maven i have in the workspace 366 if ( source == null ) 367 { 368 try 369 { 370 source = container.lookup( ArtifactMetadataSource.class ); 371 } 372 catch ( ComponentLookupException e ) 373 { 374 // won't happen 375 } 376 } 377 378 if ( listeners == null ) 379 { 380 listeners = new ArrayList<ResolutionListener>(); 381 382 if ( logger.isDebugEnabled() ) 383 { 384 listeners.add( new DebugResolutionListener( logger ) ); 385 } 386 387 listeners.add( new WarningResolutionListener( logger ) ); 388 } 389 390 ArtifactResolutionResult result = new ArtifactResolutionResult(); 391 392 // The root artifact may, or may not be resolved so we need to check before we attempt to resolve. 393 // This is often an artifact like a POM that is taken from disk and we already have hold of the 394 // file reference. But this may be a Maven Plugin that we need to resolve from a remote repository 395 // as well as its dependencies. 396 397 if ( request.isResolveRoot() /* && rootArtifact.getFile() == null */ ) 398 { 399 try 400 { 401 resolve( rootArtifact, request.getRemoteRepositories(), session ); 402 } 403 catch ( ArtifactResolutionException e ) 404 { 405 result.addErrorArtifactException( e ); 406 return result; 407 } 408 catch ( ArtifactNotFoundException e ) 409 { 410 result.addMissingArtifact( request.getArtifact() ); 411 return result; 412 } 413 } 414 415 ArtifactResolutionRequest collectionRequest = request; 416 417 if ( request.isResolveTransitively() ) 418 { 419 MetadataResolutionRequest metadataRequest = new DefaultMetadataResolutionRequest( request ); 420 421 metadataRequest.setArtifact( rootArtifact ); 422 metadataRequest.setResolveManagedVersions( managedVersions == null ); 423 424 try 425 { 426 ResolutionGroup resolutionGroup = source.retrieve( metadataRequest ); 427 428 if ( managedVersions == null ) 429 { 430 managedVersions = resolutionGroup.getManagedVersions(); 431 } 432 433 Set<Artifact> directArtifacts = resolutionGroup.getArtifacts(); 434 435 if ( artifacts == null || artifacts.isEmpty() ) 436 { 437 artifacts = directArtifacts; 438 } 439 else 440 { 441 List<Artifact> allArtifacts = new ArrayList<Artifact>(); 442 allArtifacts.addAll( artifacts ); 443 allArtifacts.addAll( directArtifacts ); 444 445 Map<String, Artifact> mergedArtifacts = new LinkedHashMap<String, Artifact>(); 446 for ( Artifact artifact : allArtifacts ) 447 { 448 String conflictId = artifact.getDependencyConflictId(); 449 if ( !mergedArtifacts.containsKey( conflictId ) ) 450 { 451 mergedArtifacts.put( conflictId, artifact ); 452 } 453 } 454 455 artifacts = new LinkedHashSet<Artifact>( mergedArtifacts.values() ); 456 } 457 458 collectionRequest = new ArtifactResolutionRequest( request ); 459 collectionRequest.setServers( request.getServers() ); 460 collectionRequest.setMirrors( request.getMirrors() ); 461 collectionRequest.setProxies( request.getProxies() ); 462 collectionRequest.setRemoteRepositories( resolutionGroup.getResolutionRepositories() ); 463 } 464 catch ( ArtifactMetadataRetrievalException e ) 465 { 466 ArtifactResolutionException are = 467 new ArtifactResolutionException( "Unable to get dependency information for " + rootArtifact.getId() 468 + ": " + e.getMessage(), rootArtifact, metadataRequest.getRemoteRepositories(), e ); 469 result.addMetadataResolutionException( are ); 470 return result; 471 } 472 } 473 474 if ( artifacts == null || artifacts.isEmpty() ) 475 { 476 if ( request.isResolveRoot() ) 477 { 478 result.addArtifact( rootArtifact ); 479 } 480 return result; 481 } 482 483 // After the collection we will have the artifact object in the result but they will not be resolved yet. 484 result = 485 artifactCollector.collect( artifacts, rootArtifact, managedVersions, collectionRequest, source, 486 collectionFilter, listeners, null ); 487 488 // We have metadata retrieval problems, or there are cycles that have been detected 489 // so we give this back to the calling code and let them deal with this information 490 // appropriately. 491 492 if ( result.hasMetadataResolutionExceptions() || result.hasVersionRangeViolations() || result.hasCircularDependencyExceptions() ) 493 { 494 return result; 495 } 496 497 if ( result.getArtifactResolutionNodes() != null ) 498 { 499 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 500 501 CountDownLatch latch = new CountDownLatch( result.getArtifactResolutionNodes().size() ); 502 503 for ( ResolutionNode node : result.getArtifactResolutionNodes() ) 504 { 505 Artifact artifact = node.getArtifact(); 506 507 if ( resolutionFilter == null || resolutionFilter.include( artifact ) ) 508 { 509 executor.execute( new ResolveTask( classLoader, latch, artifact, session, 510 node.getRemoteRepositories(), result ) ); 511 } 512 else 513 { 514 latch.countDown(); 515 } 516 } 517 try 518 { 519 latch.await(); 520 } 521 catch ( InterruptedException e ) 522 { 523 result.addErrorArtifactException( new ArtifactResolutionException( "Resolution interrupted", 524 rootArtifact, e ) ); 525 } 526 } 527 528 // We want to send the root artifact back in the result but we need to do this after the other dependencies 529 // have been resolved. 530 if ( request.isResolveRoot() ) 531 { 532 // Add the root artifact (as the first artifact to retain logical order of class path!) 533 Set<Artifact> allArtifacts = new LinkedHashSet<Artifact>(); 534 allArtifacts.add( rootArtifact ); 535 allArtifacts.addAll( result.getArtifacts() ); 536 result.setArtifacts( allArtifacts ); 537 } 538 539 return result; 540 } 541 542 public void resolve( Artifact artifact, List<ArtifactRepository> remoteRepositories, ArtifactRepository localRepository ) 543 throws ArtifactResolutionException, ArtifactNotFoundException 544 { 545 resolve( artifact, remoteRepositories, localRepository, null ); 546 } 547 548 /** 549 * ThreadCreator for creating daemon threads with fixed ThreadGroup-name. 550 */ 551 static final class DaemonThreadCreator 552 implements ThreadFactory 553 { 554 static final String THREADGROUP_NAME = "org.apache.maven.artifact.resolver.DefaultArtifactResolver"; 555 556 static final ThreadGroup GROUP = new ThreadGroup( THREADGROUP_NAME ); 557 558 static final AtomicInteger THREAD_NUMBER = new AtomicInteger( 1 ); 559 560 public Thread newThread( Runnable r ) 561 { 562 Thread newThread = new Thread( GROUP, r, "resolver-" + THREAD_NUMBER.getAndIncrement() ); 563 newThread.setDaemon( true ); 564 newThread.setContextClassLoader( null ); 565 return newThread; 566 } 567 } 568 569 private class ResolveTask 570 implements Runnable 571 { 572 573 private final ClassLoader classLoader; 574 575 private final CountDownLatch latch; 576 577 private final Artifact artifact; 578 579 private final RepositorySystemSession session; 580 581 private final List<ArtifactRepository> remoteRepositories; 582 583 private final ArtifactResolutionResult result; 584 585 public ResolveTask( ClassLoader classLoader, CountDownLatch latch, Artifact artifact, RepositorySystemSession session, 586 List<ArtifactRepository> remoteRepositories, ArtifactResolutionResult result ) 587 { 588 this.classLoader = classLoader; 589 this.latch = latch; 590 this.artifact = artifact; 591 this.session = session; 592 this.remoteRepositories = remoteRepositories; 593 this.result = result; 594 } 595 596 public void run() 597 { 598 ClassLoader old = Thread.currentThread().getContextClassLoader(); 599 try 600 { 601 Thread.currentThread().setContextClassLoader( classLoader ); 602 resolve( artifact, remoteRepositories, session ); 603 } 604 catch ( ArtifactNotFoundException anfe ) 605 { 606 // These are cases where the artifact just isn't present in any of the remote repositories 607 // because it wasn't deployed, or it was deployed in the wrong place. 608 609 synchronized ( result ) 610 { 611 result.addMissingArtifact( artifact ); 612 } 613 } 614 catch ( ArtifactResolutionException e ) 615 { 616 // This is really a wagon TransferFailedException so something went wrong after we successfully 617 // retrieved the metadata. 618 619 synchronized ( result ) 620 { 621 result.addErrorArtifactException( e ); 622 } 623 } 624 finally 625 { 626 latch.countDown(); 627 Thread.currentThread().setContextClassLoader( old ); 628 629 } 630 } 631 632 } 633 634 @Override 635 public void dispose() 636 { 637 if ( executor instanceof ExecutorService ) 638 { 639 ( (ExecutorService) executor ).shutdownNow(); 640 } 641 } 642 643}